1 module tagion.testbench.dart.insert_remove_stress; 2 // Default import list for bdd 3 import std.algorithm; 4 import std.datetime.stopwatch; 5 import std.file : mkdirRecurse; 6 import std.format; 7 import std.random; 8 import std.range; 9 import std.stdio; 10 import std.typecons : Tuple; 11 import tagion.basic.Types : Buffer; 12 import tagion.behaviour; 13 import tagion.hibon.Document; 14 import tagion.hibon.HiBONJSON; 15 import tagion.testbench.tools.Environment; 16 17 // dart 18 import std.digest; 19 import tagion.basic.basic : forceRemove; 20 import tagion.crypto.SecureInterfaceNet : HashNet, SecureNet; 21 import tagion.dart.DART : DART; 22 import tagion.dart.DARTBasic; 23 import tagion.dart.DARTFakeNet; 24 import tagion.dart.DARTFile : DARTFile; 25 import tagion.dart.Recorder : Archive, RecordFactory; 26 import tagion.testbench.dart.dart_helper_functions; 27 import tagion.testbench.dart.dartinfo; 28 29 enum feature = Feature( 30 "insert random stress test", 31 [ 32 "This test uses dartfakenet to randomly add and remove archives in the same recorder." 33 ]); 34 35 alias FeatureContext = Tuple!( 36 AddRemoveAndReadTheResult, "AddRemoveAndReadTheResult", 37 FeatureGroup*, "result" 38 ); 39 40 @safe @Scenario("add remove and read the result", 41 []) 42 class AddRemoveAndReadTheResult { 43 44 DartInfo info; 45 DART db1; 46 RandomArchives[] random_archives; 47 Mt19937 gen; 48 auto insert_watch = StopWatch(AutoStart.no); 49 auto read_watch = StopWatch(AutoStart.no); 50 uint number_of_seeds; 51 uint number_of_rounds; 52 uint number_of_samples; 53 54 uint operations; 55 56 this(DartInfo info, const uint seed, const uint number_of_seeds, const uint number_of_rounds, const uint number_of_samples) { 57 check(number_of_samples < number_of_seeds, "number of samples must be smaller than number of seeds"); 58 this.info = info; 59 gen = Mt19937(seed); 60 this.number_of_rounds = number_of_rounds; 61 this.number_of_samples = number_of_samples; 62 this.number_of_seeds = number_of_seeds; 63 } 64 65 @Given("i have a dartfile") 66 Document dartfile() { 67 mkdirRecurse(info.module_path); 68 // create the dartfile 69 info.dartfilename.forceRemove; 70 DART.create(info.dartfilename, info.net); 71 72 Exception dart_exception; 73 db1 = new DART(info.net, info.dartfilename, dart_exception); 74 check(dart_exception is null, format("Failed to open DART %s", dart_exception.msg)); 75 return result_ok; 76 } 77 78 @Given("i have an array of randomarchives") 79 Document randomarchives() { 80 81 // first we generate the array of random archives 82 83 random_archives = gen.take(number_of_seeds).map!(s => RandomArchives(s)).array; 84 85 return result_ok; 86 } 87 88 @When("i select n amount of elements in the randomarchives and add them to the dart and flip their bool. And count the number of instructions.") 89 Document instructions() { 90 91 foreach (i; 0 .. number_of_rounds) { 92 writefln("running %s", i); 93 auto rnd = MinstdRand0(gen.front); 94 gen.popFront; 95 auto sample_numbers = iota(random_archives.length).randomSample(number_of_samples, rnd).array; 96 auto recorder = db1.recorder(); 97 98 foreach (sample_number; sample_numbers) { 99 auto docs = random_archives[sample_number].values.map!(a => DARTFakeNet.fake_doc(a)); 100 101 if (!random_archives[sample_number].in_dart) { 102 recorder.insert(docs, Archive.Type.ADD); 103 } 104 else { 105 106 recorder.insert(docs, Archive.Type.REMOVE); 107 } 108 operations += docs.walkLength; 109 110 random_archives[sample_number].in_dart = !random_archives[sample_number].in_dart; 111 } 112 113 insert_watch.start(); 114 db1.modify(recorder); 115 insert_watch.stop(); 116 } 117 118 return result_ok; 119 } 120 121 @Then("i read all the elements.") 122 Document elements() { 123 124 auto fingerprints = random_archives 125 .filter!(s => s.in_dart == true) 126 .map!(d => d.values) 127 .join 128 .map!(a => DARTFakeNet.fake_doc(a)) 129 .map!(a => info.net.dartIndex(a)) 130 .array; 131 132 writefln("###"); 133 read_watch.start(); 134 auto read_recorder = db1.loads(fingerprints, Archive.Type.NONE); 135 read_watch.stop(); 136 writeln(read_recorder[].walkLength); 137 138 check(read_recorder[].walkLength == fingerprints.length, "the length of the read archives is not the same as the expected"); 139 140 auto expected_read_docs = random_archives 141 .filter!(s => s.in_dart == true) 142 .map!(d => d.values) 143 .join 144 .map!(a => DARTFakeNet.fake_doc(a)) 145 .array; 146 147 auto expected_recorder = db1.recorder(); 148 expected_recorder.insert(expected_read_docs); 149 150 check(equal(expected_recorder[].map!(a => a.filed), read_recorder[].map!(a => a.filed)), "data not the same"); 151 152 const long insert_time = insert_watch.peek.total!"msecs"; 153 const long read_time = read_watch.peek.total!"msecs"; 154 155 writefln("Total number of operations: %d", operations + fingerprints.length); 156 writefln("ADD and REMOVE operations: %d. pr. sec: %s", operations, operations / double(insert_time) * 1000); 157 writefln("READ operations: %s. pr.sec %s", fingerprints.length, fingerprints.length / double(read_time) * 1000); 158 159 // the total time 160 writefln("Total operations pr. sec: %1.f", (operations + fingerprints.length) / double(insert_time + read_time) * 1000); 161 db1.close; 162 return result_ok; 163 } 164 165 }