1 module tagion.testbench.dart.dart_stress_test;
2 // Default import list for bdd
3 import std.algorithm : each, equal, filter, map, sort;
4 import std.datetime.stopwatch;
5 import std.file : mkdirRecurse;
6 import std.format : format;
7 import std.path : buildPath, setExtension;
8 import std.random : MinstdRand0, randomSample, randomShuffle;
9 import std.range;
10 import std.stdio;
11 import std.typecons : Tuple;
12 import tagion.Keywords;
13 import tagion.behaviour;
14 import tagion.communication.HiRPC;
15 import tagion.communication.HiRPC;
16 import tagion.crypto.SecureInterfaceNet : HashNet, SecureNet;
17 import tagion.dart.DART : DART;
18 import tagion.dart.DARTBasic : DARTIndex, dartIndex;
19 import tagion.dart.DARTFakeNet;
20 import tagion.dart.DARTFile : DARTFile;
21 import tagion.dart.DARTcrud : dartRead;
22 import tagion.dart.Recorder : Archive, RecordFactory;
23 import tagion.hibon.Document;
24 import tagion.hibon.HiBONJSON : toPretty;
25 import tagion.hibon.HiBONJSON : toPretty;
26 import tagion.hibon.HiBONRecord;
27 import tagion.testbench.dart.dart_helper_functions;
28 import tagion.testbench.dart.dartinfo;
29 import tagion.testbench.tools.Environment;
30 import tagion.utils.Random;
31 
32 enum feature = Feature(
33             "Dart pseudo random stress test",
34             ["All test in this bdd should use dart fakenet."]);
35 
36 alias FeatureContext = Tuple!(
37         AddPseudoRandomData, "AddPseudoRandomData",
38         FeatureGroup*, "result"
39 );
40 
41 @safe @Scenario("Add pseudo random data.",
42         [])
43 class AddPseudoRandomData {
44     DART db1;
45 
46     DartInfo info;
47     const ulong samples;
48     const ulong number_of_records;
49     ulong[][] data;
50 
51     this(DartInfo info, const ulong samples, const ulong number_of_records) {
52         check(samples % number_of_records == 0,
53                 format("Number of samples %s and records %s each time does not match.", samples, number_of_records));
54         this.info = info;
55         this.samples = samples;
56         this.number_of_records = number_of_records;
57     }
58 
59     @Given("I have one dartfile.")
60     Document dartfile() {
61         writeln(info.dartfilename);
62         DARTFile.create(info.dartfilename, info.net);
63         Exception dart_exception;
64         db1 = new DART(info.net, info.dartfilename, dart_exception);
65         check(dart_exception is null, format("Failed to open DART %s %s",info.dartfilename, dart_exception.msg));
66         writeln("after opening");
67         return result_ok;
68     }
69 
70     @Given("I have a pseudo random sequence of data stored in a table with a seed.")
71     Document seed() {
72         check(!info.fixed_states.empty, "Pseudo random sequence not generated");
73         return result_ok;
74     }
75 
76     @When("I increasingly add more data in each recorder and time it.")
77     Document it() {
78 
79         auto insert_watch = StopWatch(AutoStart.no);
80         auto read_watch = StopWatch(AutoStart.no);
81         auto remove_watch = StopWatch(AutoStart.no);
82 
83         RecordFactory.Recorder[] recorders;
84 
85         foreach (i; 0 .. samples / number_of_records) {
86             writefln("running %s", i);
87 
88             auto docs = info.fixed_states.take(number_of_records)
89                 .map!(a => DARTFakeNet.fake_doc(a));
90 
91             auto recorder = db1.recorder();
92 
93             recorders ~= recorder;
94 
95             recorder.insert(docs, Archive.Type.ADD);
96             auto dart_indexs = recorder[]
97                 .map!(a => a.dart_index);
98 
99             ulong[] insert_add_single_time;
100             insert_watch.start();
101             db1.modify(recorder);
102             insert_watch.stop();
103             insert_add_single_time ~= insert_watch.peek.total!"msecs";
104             read_watch.start();
105             auto sender = dartRead(dart_indexs, info.hirpc);
106             auto receiver = info.hirpc.receive(sender.toDoc);
107             auto result = db1(receiver, false);
108             const doc = result.message[Keywords.result].get!Document;
109             read_watch.stop();
110             insert_add_single_time ~= read_watch.peek.total!"msecs";
111             data ~= insert_add_single_time;
112 
113             auto recorder_read = db1.recorder(doc);
114             check(equal(recorder_read[].map!(a => a.filed.data), recorder[].map!(a => a.filed.data)), "data not the same");
115         }
116         import tagion.dart.Recorder : Remove;
117 
118         foreach (i, recorder; recorders.enumerate) {
119             writefln("remove %s", i);
120             remove_watch.start();
121             db1.modify(recorder.changeTypes(Remove));
122             remove_watch.stop();
123             data[i] ~= remove_watch.peek.total!"msecs";
124 
125         }
126 
127         const long insert_time = insert_watch.peek.total!"msecs";
128         const long read_time = read_watch.peek.total!"msecs";
129         const long remove_time = remove_watch.peek.total!"msecs";
130         const total_time = insert_time + read_time + remove_time;
131 
132         writefln("INSERT took %d msecs", insert_time);
133         writefln("READ took %d msecs", read_time);
134         writefln("REMOVE took %d msecs", remove_time);
135 
136         writefln("TOTAL: %d msecs. IRR pr. sec: %.1f", insert_time + read_time + remove_time, samples / double(
137                 total_time) * 1000.0);
138 
139         return result_ok;
140     }
141 
142     @Then("the data should be read and checked.")
143     Document checked() {
144 
145         auto fout = File(format("%s", buildPath(info.module_path, "dart_stress_test.csv")), "w");
146 
147         scope (exit) {
148             fout.close();
149         }
150 
151         fout.writeln("INSERT,READ,REMOVE");
152         foreach (single_time; data) {
153             fout.writefln("%(%s, %)", single_time);
154         }
155 
156         db1.close();
157         return result_ok;
158     }
159 
160 }