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 }