Line data Source code
1 : #define BOOST_TEST_DYN_LINK
2 : #define BOOST_TEST_MODULE (MultithreadedStore test)
3 :
4 : #include <boost/test/unit_test.hpp>
5 :
6 : #include <algorithm>
7 : #include <atomic>
8 : #include <chrono>
9 : #include <cstdlib>
10 : #include <iostream>
11 : #include <memory>
12 : #include <mutex>
13 : #include <set>
14 : #include <string>
15 : #include <thread>
16 : #include <vector>
17 :
18 : #include "../DatabaseConfigurationInterface.h"
19 :
20 : using namespace ots;
21 :
22 16 : bool isFileSystemDB() {
23 16 : const char* uri = std::getenv("ARTDAQ_DATABASE_URI");
24 48 : return uri != nullptr && std::string(uri).find("filesystem") != std::string::npos;
25 : }
26 :
27 : struct MultithreadedStoreTestData {
28 2 : MultithreadedStoreTestData() {
29 2 : srand(time(nullptr));
30 2 : baseVersion = rand() % 99999 + 100000;
31 2 : std::cout << "=== MULTITHREADED STORE TEST SUITE ===\n";
32 2 : std::cout << "Base version: " << baseVersion << "\n";
33 2 : std::cout << "Tests for: versionInfo.validate(), parallel unassignments, _mt correctness\n";
34 2 : }
35 :
36 2 : ~MultithreadedStoreTestData() { std::cout << "\n=== TEST SUITE COMPLETE ===\n"; }
37 :
38 19 : int createConfig001(DatabaseConfigurationInterface& ifc, int version) {
39 19 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
40 19 : cfg->getView().version = version;
41 38 : return ifc.saveActiveVersion(cfg.get());
42 19 : }
43 :
44 10 : int createConfig002(DatabaseConfigurationInterface& ifc, int version) {
45 10 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration002>();
46 10 : cfg->getView().version = version;
47 20 : return ifc.saveActiveVersion(cfg.get());
48 10 : }
49 :
50 39 : std::string config001Name() {
51 39 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration001>();
52 78 : return cfg->getConfigurationName();
53 39 : }
54 :
55 21 : std::string config002Name() {
56 21 : std::shared_ptr<ConfigurationBase> cfg = std::make_shared<TestConfiguration002>();
57 42 : return cfg->getConfigurationName();
58 21 : }
59 :
60 : int baseVersion;
61 : };
62 :
63 : MultithreadedStoreTestData fixture;
64 :
65 : BOOST_AUTO_TEST_SUITE(multithreaded_store_test)
66 :
67 4 : BOOST_AUTO_TEST_CASE(mt_rejects_empty_version_list) {
68 2 : std::cout << "\n=== TEST 1: MT rejects empty version list ===\n";
69 :
70 2 : auto ifc = DatabaseConfigurationInterface();
71 :
72 2 : auto emptyMap = DatabaseConfigurationInterface::config_version_map_t{};
73 2 : std::string compName = "MTValidation_Empty_" + std::to_string(fixture.baseVersion);
74 :
75 2 : std::cout << "Attempting to create composition with empty version list...\n";
76 :
77 4 : BOOST_CHECK_THROW(ifc.storeGlobalConfiguration_mt(emptyMap, compName), std::exception);
78 :
79 2 : std::cout << " Result: Exception thrown (as expected)\n";
80 2 : std::cout << " Verified: Empty version list rejected by _mt version\n";
81 2 : }
82 :
83 4 : BOOST_AUTO_TEST_CASE(mt_rejects_empty_configuration_name) {
84 2 : std::cout << "\n=== TEST 2: MT rejects empty configuration name ===\n";
85 :
86 2 : auto ifc = DatabaseConfigurationInterface();
87 :
88 2 : int version1 = fixture.baseVersion;
89 2 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, version1), 0);
90 :
91 2 : auto map = DatabaseConfigurationInterface::config_version_map_t{};
92 2 : map[fixture.config001Name()] = version1;
93 :
94 2 : std::cout << "Attempting to create composition with empty name...\n";
95 :
96 8 : BOOST_CHECK_THROW(ifc.storeGlobalConfiguration_mt(map, ""), std::exception);
97 :
98 2 : std::cout << " Result: Exception thrown (as expected)\n";
99 2 : std::cout << " Verified: Empty configuration name rejected by _mt version\n";
100 2 : }
101 :
102 4 : BOOST_AUTO_TEST_CASE(mt_overwrite_removes_members_parallel) {
103 2 : std::cout << "\n=== TEST 3: MT overwrite removes members (parallel unassignments) ===\n";
104 :
105 2 : if (isFileSystemDB()) {
106 1 : std::cout << " SKIPPED: FileSystemDB has race conditions in configurationComposition\n";
107 1 : BOOST_CHECK(true);
108 1 : return;
109 : }
110 :
111 1 : auto ifc = DatabaseConfigurationInterface();
112 :
113 1 : int v1 = fixture.baseVersion + 10;
114 1 : int v2 = fixture.baseVersion + 11;
115 :
116 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, v1), 0);
117 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig002(ifc, v2), 0);
118 1 : std::cout << "Step 1: Created versions " << v1 << ", " << v2 << "\n";
119 :
120 1 : auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
121 1 : map1[fixture.config001Name()] = v1;
122 1 : map1[fixture.config002Name()] = v2;
123 :
124 1 : std::string compName = "MTUnassignParallel_" + std::to_string(fixture.baseVersion);
125 1 : ifc.storeGlobalConfiguration_mt(map1, compName);
126 1 : std::cout << "Step 2: Created composition with 2 members using _mt\n";
127 :
128 1 : auto initial = ifc.loadGlobalConfiguration(compName);
129 1 : BOOST_CHECK_EQUAL(initial.size(), 2);
130 :
131 1 : auto map2 = DatabaseConfigurationInterface::config_version_map_t{};
132 1 : map2[fixture.config001Name()] = v1;
133 :
134 1 : std::cout << "Step 3: Overwriting to remove Config002 using _mt...\n";
135 1 : auto start = std::chrono::high_resolution_clock::now();
136 :
137 1 : BOOST_CHECK_NO_THROW(ifc.storeGlobalConfiguration_mt(map2, compName, true));
138 :
139 1 : auto end = std::chrono::high_resolution_clock::now();
140 1 : auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
141 1 : std::cout << " Completed in " << duration.count() << "ms\n";
142 :
143 1 : auto loaded = ifc.loadGlobalConfiguration(compName);
144 1 : BOOST_CHECK_EQUAL(loaded.size(), 1);
145 1 : BOOST_CHECK_EQUAL(loaded[fixture.config001Name()], v1);
146 :
147 1 : bool hasConfig002 = (loaded.find(fixture.config002Name()) != loaded.end());
148 1 : BOOST_CHECK_EQUAL(hasConfig002, false);
149 :
150 1 : std::cout << " Verified: Composition now has 1 member (Config002 removed)\n";
151 :
152 1 : auto foundComps = ifc.findCompositionsContaining(fixture.config002Name(), std::to_string(v2));
153 1 : bool stillFound = (foundComps.find(compName) != foundComps.end());
154 1 : BOOST_CHECK_EQUAL(stillFound, false);
155 :
156 1 : std::cout << " Verified: Reverse lookup no longer finds removed member\n";
157 1 : }
158 :
159 4 : BOOST_AUTO_TEST_CASE(mt_overwrite_updates_version_parallel) {
160 2 : std::cout << "\n=== TEST 4: MT overwrite updates version (parallel unassign + assign) ===\n";
161 :
162 2 : if (isFileSystemDB()) {
163 1 : std::cout << " SKIPPED: FileSystemDB has race conditions in configurationComposition\n";
164 1 : BOOST_CHECK(true);
165 1 : return;
166 : }
167 :
168 1 : auto ifc = DatabaseConfigurationInterface();
169 :
170 1 : int versionOld = fixture.baseVersion + 20;
171 1 : int versionNew = fixture.baseVersion + 21;
172 :
173 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, versionOld), 0);
174 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, versionNew), 0);
175 1 : std::cout << "Step 1: Created versions " << versionOld << " and " << versionNew << "\n";
176 :
177 1 : auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
178 1 : map1[fixture.config001Name()] = versionOld;
179 :
180 1 : std::string compName = "MTUpdateVersion_" + std::to_string(fixture.baseVersion);
181 1 : ifc.storeGlobalConfiguration_mt(map1, compName);
182 1 : std::cout << "Step 2: Created composition with version " << versionOld << " using _mt\n";
183 :
184 1 : auto map2 = DatabaseConfigurationInterface::config_version_map_t{};
185 1 : map2[fixture.config001Name()] = versionNew;
186 :
187 1 : std::cout << "Step 3: Overwriting to update version to " << versionNew << " using _mt...\n";
188 1 : BOOST_CHECK_NO_THROW(ifc.storeGlobalConfiguration_mt(map2, compName, true));
189 :
190 1 : auto loaded = ifc.loadGlobalConfiguration(compName);
191 1 : BOOST_CHECK_EQUAL(loaded.size(), 1);
192 1 : BOOST_CHECK_EQUAL(loaded[fixture.config001Name()], versionNew);
193 :
194 1 : std::cout << " Verified: Composition now has version " << versionNew << "\n";
195 :
196 1 : auto foundOld = ifc.findCompositionsContaining(fixture.config001Name(), std::to_string(versionOld));
197 1 : bool oldFound = (foundOld.find(compName) != foundOld.end());
198 1 : BOOST_CHECK_EQUAL(oldFound, false);
199 :
200 1 : std::cout << " Verified: Reverse lookup for old version returns empty\n";
201 :
202 1 : auto foundNew = ifc.findCompositionsContaining(fixture.config001Name(), std::to_string(versionNew));
203 1 : bool newFound = (foundNew.find(compName) != foundNew.end());
204 1 : BOOST_CHECK_EQUAL(newFound, true);
205 :
206 1 : std::cout << " Verified: Reverse lookup for new version finds composition\n";
207 1 : }
208 :
209 4 : BOOST_AUTO_TEST_CASE(mt_and_st_produce_identical_results) {
210 2 : std::cout << "\n=== TEST 5: MT and single-threaded produce identical results ===\n";
211 :
212 2 : if (isFileSystemDB()) {
213 1 : std::cout << " SKIPPED: FileSystemDB has race conditions in configurationComposition\n";
214 1 : BOOST_CHECK(true);
215 1 : return;
216 : }
217 :
218 1 : auto ifc = DatabaseConfigurationInterface();
219 :
220 1 : int v1 = fixture.baseVersion + 30;
221 1 : int v2 = fixture.baseVersion + 31;
222 :
223 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, v1), 0);
224 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig002(ifc, v2), 0);
225 :
226 1 : auto map = DatabaseConfigurationInterface::config_version_map_t{};
227 1 : map[fixture.config001Name()] = v1;
228 1 : map[fixture.config002Name()] = v2;
229 :
230 1 : std::string compNameST = "IdenticalST_" + std::to_string(fixture.baseVersion);
231 1 : std::string compNameMT = "IdenticalMT_" + std::to_string(fixture.baseVersion);
232 :
233 1 : std::cout << "Creating composition using single-threaded version...\n";
234 1 : ifc.storeGlobalConfiguration(map, compNameST);
235 :
236 1 : std::cout << "Creating composition using multi-threaded version...\n";
237 1 : ifc.storeGlobalConfiguration_mt(map, compNameMT);
238 :
239 1 : auto loadedST = ifc.loadGlobalConfiguration(compNameST);
240 1 : auto loadedMT = ifc.loadGlobalConfiguration(compNameMT);
241 :
242 1 : BOOST_CHECK_EQUAL(loadedST.size(), loadedMT.size());
243 :
244 3 : for (auto const& entry : loadedST) {
245 2 : auto it = loadedMT.find(entry.first);
246 2 : BOOST_CHECK_MESSAGE(it != loadedMT.end(), "MT missing member: " << entry.first);
247 2 : if (it != loadedMT.end()) {
248 2 : BOOST_CHECK_EQUAL(it->second, entry.second);
249 : }
250 : }
251 :
252 1 : std::cout << " Verified: Both compositions have identical content\n";
253 :
254 3 : for (auto const& entry : map) {
255 2 : auto foundST = ifc.findCompositionsContaining(entry.first, std::to_string(entry.second));
256 2 : auto foundMT = ifc.findCompositionsContaining(entry.first, std::to_string(entry.second));
257 :
258 2 : bool stFound = (foundST.find(compNameST) != foundST.end());
259 2 : bool mtFound = (foundMT.find(compNameMT) != foundMT.end());
260 :
261 2 : BOOST_CHECK_EQUAL(stFound, true);
262 2 : BOOST_CHECK_EQUAL(mtFound, true);
263 2 : }
264 :
265 1 : std::cout << " Verified: Reverse lookups work for both\n";
266 1 : }
267 :
268 4 : BOOST_AUTO_TEST_CASE(mt_overwrite_many_unassignments) {
269 2 : std::cout << "\n=== TEST 6: MT overwrite with many unassignments ===\n";
270 :
271 2 : if (isFileSystemDB()) {
272 1 : std::cout << " SKIPPED: FileSystemDB has race conditions in configurationComposition\n";
273 1 : BOOST_CHECK(true);
274 1 : return;
275 : }
276 :
277 1 : auto ifc = DatabaseConfigurationInterface();
278 :
279 1 : const int NUM_VERSIONS = 5;
280 1 : std::vector<int> config001Versions;
281 1 : std::vector<int> config002Versions;
282 :
283 6 : for (int i = 0; i < NUM_VERSIONS; ++i) {
284 5 : int v1 = fixture.baseVersion + 40 + i;
285 5 : int v2 = fixture.baseVersion + 40 + NUM_VERSIONS + i;
286 :
287 5 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, v1), 0);
288 5 : BOOST_REQUIRE_EQUAL(fixture.createConfig002(ifc, v2), 0);
289 :
290 5 : config001Versions.push_back(v1);
291 5 : config002Versions.push_back(v2);
292 : }
293 :
294 1 : std::cout << "Step 1: Created " << (NUM_VERSIONS * 2) << " configuration versions\n";
295 :
296 1 : auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
297 1 : map1[fixture.config001Name()] = config001Versions[0];
298 1 : map1[fixture.config002Name()] = config002Versions[0];
299 :
300 1 : std::string compName = "MTManyUnassign_" + std::to_string(fixture.baseVersion);
301 1 : ifc.storeGlobalConfiguration_mt(map1, compName);
302 1 : std::cout << "Step 2: Created initial composition with 2 members\n";
303 :
304 5 : for (int i = 1; i < NUM_VERSIONS; ++i) {
305 4 : auto mapN = DatabaseConfigurationInterface::config_version_map_t{};
306 4 : mapN[fixture.config001Name()] = config001Versions[i];
307 4 : mapN[fixture.config002Name()] = config002Versions[i];
308 :
309 4 : std::cout << "Step " << (2 + i) << ": Overwriting with versions " << config001Versions[i] << ", " << config002Versions[i] << "...\n";
310 :
311 4 : auto start = std::chrono::high_resolution_clock::now();
312 4 : ifc.storeGlobalConfiguration_mt(mapN, compName, true);
313 4 : auto end = std::chrono::high_resolution_clock::now();
314 4 : auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
315 :
316 4 : std::cout << " Completed in " << duration.count() << "ms\n";
317 4 : }
318 :
319 1 : auto loaded = ifc.loadGlobalConfiguration(compName);
320 1 : BOOST_CHECK_EQUAL(loaded.size(), 2);
321 1 : BOOST_CHECK_EQUAL(loaded[fixture.config001Name()], config001Versions[NUM_VERSIONS - 1]);
322 1 : BOOST_CHECK_EQUAL(loaded[fixture.config002Name()], config002Versions[NUM_VERSIONS - 1]);
323 :
324 1 : std::cout << " Verified: Final composition has latest versions\n";
325 :
326 5 : for (int i = 0; i < NUM_VERSIONS - 1; ++i) {
327 4 : auto found1 = ifc.findCompositionsContaining(fixture.config001Name(), std::to_string(config001Versions[i]));
328 4 : auto found2 = ifc.findCompositionsContaining(fixture.config002Name(), std::to_string(config002Versions[i]));
329 :
330 4 : BOOST_CHECK_MESSAGE(found1.find(compName) == found1.end(), "Old version " << config001Versions[i] << " should not be found");
331 4 : BOOST_CHECK_MESSAGE(found2.find(compName) == found2.end(), "Old version " << config002Versions[i] << " should not be found");
332 4 : }
333 :
334 1 : auto foundLast1 = ifc.findCompositionsContaining(fixture.config001Name(), std::to_string(config001Versions[NUM_VERSIONS - 1]));
335 1 : auto foundLast2 = ifc.findCompositionsContaining(fixture.config002Name(), std::to_string(config002Versions[NUM_VERSIONS - 1]));
336 :
337 1 : BOOST_CHECK(foundLast1.find(compName) != foundLast1.end());
338 1 : BOOST_CHECK(foundLast2.find(compName) != foundLast2.end());
339 :
340 1 : std::cout << " Verified: Only latest versions found in reverse lookup\n";
341 1 : }
342 :
343 4 : BOOST_AUTO_TEST_CASE(mt_three_versions_bug_regression) {
344 2 : std::cout << "\n=== TEST 7: MT three versions bug regression ===\n";
345 :
346 2 : if (isFileSystemDB()) {
347 1 : std::cout << " SKIPPED: FileSystemDB has race conditions in configurationComposition\n";
348 1 : BOOST_CHECK(true);
349 1 : return;
350 : }
351 :
352 1 : auto ifc = DatabaseConfigurationInterface();
353 :
354 1 : int version1 = fixture.baseVersion + 60;
355 1 : int version2 = fixture.baseVersion + 61;
356 1 : int version3 = fixture.baseVersion + 62;
357 :
358 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, version1), 0);
359 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, version2), 0);
360 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, version3), 0);
361 1 : std::cout << "Step 1: Created versions " << version1 << ", " << version2 << ", " << version3 << "\n";
362 :
363 1 : std::string compName = "MTThreeVersionsBug_" + std::to_string(fixture.baseVersion);
364 :
365 1 : auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
366 1 : map1[fixture.config001Name()] = version1;
367 1 : ifc.storeGlobalConfiguration_mt(map1, compName);
368 1 : std::cout << "Step 2: Created composition with version " << version1 << " using _mt\n";
369 :
370 1 : auto map2 = DatabaseConfigurationInterface::config_version_map_t{};
371 1 : map2[fixture.config001Name()] = version2;
372 1 : ifc.storeGlobalConfiguration_mt(map2, compName, true);
373 1 : std::cout << "Step 3: Overwrote with version " << version2 << " using _mt\n";
374 :
375 1 : auto map3 = DatabaseConfigurationInterface::config_version_map_t{};
376 1 : map3[fixture.config001Name()] = version3;
377 1 : ifc.storeGlobalConfiguration_mt(map3, compName, true);
378 1 : std::cout << "Step 4: Overwrote with version " << version3 << " using _mt\n";
379 :
380 1 : auto loaded = ifc.loadGlobalConfiguration(compName);
381 :
382 1 : BOOST_CHECK_EQUAL(loaded.size(), 1);
383 1 : BOOST_CHECK_EQUAL(loaded[fixture.config001Name()], version3);
384 :
385 1 : std::cout << "Step 5: Verified composition has only version " << version3 << "\n";
386 :
387 1 : auto found1 = ifc.findCompositionsContaining(fixture.config001Name(), std::to_string(version1));
388 1 : BOOST_CHECK_MESSAGE(found1.find(compName) == found1.end(), "BUG: Version " << version1 << " should NOT be found!");
389 :
390 1 : auto found2 = ifc.findCompositionsContaining(fixture.config001Name(), std::to_string(version2));
391 1 : BOOST_CHECK_MESSAGE(found2.find(compName) == found2.end(), "BUG: Version " << version2 << " should NOT be found!");
392 :
393 1 : auto found3 = ifc.findCompositionsContaining(fixture.config001Name(), std::to_string(version3));
394 1 : BOOST_CHECK_MESSAGE(found3.find(compName) != found3.end(), "Version " << version3 << " should be found");
395 :
396 1 : std::cout << " v" << version1 << " found: " << (found1.find(compName) != found1.end() ? "YES (BUG!)" : "NO (correct)") << "\n";
397 1 : std::cout << " v" << version2 << " found: " << (found2.find(compName) != found2.end() ? "YES (BUG!)" : "NO (correct)") << "\n";
398 1 : std::cout << " v" << version3 << " found: " << (found3.find(compName) != found3.end() ? "YES (correct)" : "NO (BUG!)") << "\n";
399 :
400 1 : std::cout << "\n SUCCESS: Three Versions Bug verified fixed for _mt version!\n";
401 1 : }
402 :
403 4 : BOOST_AUTO_TEST_CASE(mt_complete_replacement) {
404 2 : std::cout << "\n=== TEST 8: MT complete replacement ===\n";
405 :
406 2 : if (isFileSystemDB()) {
407 1 : std::cout << " SKIPPED: FileSystemDB has race conditions in configurationComposition\n";
408 1 : BOOST_CHECK(true);
409 1 : return;
410 : }
411 :
412 1 : auto ifc = DatabaseConfigurationInterface();
413 :
414 1 : int vA = fixture.baseVersion + 70;
415 1 : int vB = fixture.baseVersion + 71;
416 1 : int vC = fixture.baseVersion + 72;
417 :
418 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, vA), 0);
419 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, vC), 0);
420 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig002(ifc, vB), 0);
421 :
422 1 : auto map1 = DatabaseConfigurationInterface::config_version_map_t{};
423 1 : map1[fixture.config001Name()] = vA;
424 :
425 1 : std::string compName = "MTCompleteReplace_" + std::to_string(fixture.baseVersion);
426 1 : ifc.storeGlobalConfiguration_mt(map1, compName);
427 1 : std::cout << "Step 1: Created composition with Config001/v" << vA << " using _mt\n";
428 :
429 1 : auto map2 = DatabaseConfigurationInterface::config_version_map_t{};
430 1 : map2[fixture.config001Name()] = vC;
431 1 : map2[fixture.config002Name()] = vB;
432 :
433 1 : std::cout << "Step 2: Complete replacement using _mt...\n";
434 1 : auto start = std::chrono::high_resolution_clock::now();
435 :
436 1 : BOOST_CHECK_NO_THROW(ifc.storeGlobalConfiguration_mt(map2, compName, true));
437 :
438 1 : auto end = std::chrono::high_resolution_clock::now();
439 1 : auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
440 1 : std::cout << " Completed in " << duration.count() << "ms\n";
441 :
442 1 : auto loaded = ifc.loadGlobalConfiguration(compName);
443 1 : BOOST_CHECK_EQUAL(loaded.size(), 2);
444 1 : BOOST_CHECK_EQUAL(loaded[fixture.config001Name()], vC);
445 1 : BOOST_CHECK_EQUAL(loaded[fixture.config002Name()], vB);
446 :
447 1 : std::cout << " Verified: Composition replaced completely\n";
448 :
449 1 : auto foundA = ifc.findCompositionsContaining(fixture.config001Name(), std::to_string(vA));
450 1 : BOOST_CHECK(foundA.find(compName) == foundA.end());
451 :
452 1 : std::cout << " Verified: Old version A not in reverse lookup\n";
453 1 : }
454 :
455 4 : BOOST_AUTO_TEST_CASE(mt_performance_overwrite_comparison) {
456 2 : std::cout << "\n=== TEST 9: MT performance comparison for overwrite ===\n";
457 :
458 2 : if (isFileSystemDB()) {
459 1 : std::cout << " SKIPPED: FileSystemDB has race conditions in configurationComposition\n";
460 1 : BOOST_CHECK(true);
461 1 : return;
462 : }
463 :
464 1 : auto ifc = DatabaseConfigurationInterface();
465 :
466 1 : int v1_old = fixture.baseVersion + 80;
467 1 : int v2_old = fixture.baseVersion + 81;
468 1 : int v1_new = fixture.baseVersion + 82;
469 1 : int v2_new = fixture.baseVersion + 83;
470 :
471 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, v1_old), 0);
472 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig002(ifc, v2_old), 0);
473 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, v1_new), 0);
474 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig002(ifc, v2_new), 0);
475 :
476 1 : auto mapOld = DatabaseConfigurationInterface::config_version_map_t{};
477 1 : mapOld[fixture.config001Name()] = v1_old;
478 1 : mapOld[fixture.config002Name()] = v2_old;
479 :
480 1 : auto mapNew = DatabaseConfigurationInterface::config_version_map_t{};
481 1 : mapNew[fixture.config001Name()] = v1_new;
482 1 : mapNew[fixture.config002Name()] = v2_new;
483 :
484 1 : std::string compNameST = "PerfOverwriteST_" + std::to_string(fixture.baseVersion);
485 1 : std::string compNameMT = "PerfOverwriteMT_" + std::to_string(fixture.baseVersion);
486 :
487 1 : ifc.storeGlobalConfiguration(mapOld, compNameST);
488 1 : ifc.storeGlobalConfiguration_mt(mapOld, compNameMT);
489 :
490 1 : std::cout << "Initial compositions created. Now testing overwrite performance...\n";
491 :
492 1 : auto startST = std::chrono::high_resolution_clock::now();
493 1 : ifc.storeGlobalConfiguration(mapNew, compNameST, true);
494 1 : auto endST = std::chrono::high_resolution_clock::now();
495 1 : auto durationST = std::chrono::duration_cast<std::chrono::milliseconds>(endST - startST);
496 :
497 1 : auto startMT = std::chrono::high_resolution_clock::now();
498 1 : ifc.storeGlobalConfiguration_mt(mapNew, compNameMT, true);
499 1 : auto endMT = std::chrono::high_resolution_clock::now();
500 1 : auto durationMT = std::chrono::duration_cast<std::chrono::milliseconds>(endMT - startMT);
501 :
502 1 : std::cout << " Single-threaded overwrite: " << durationST.count() << "ms\n";
503 1 : std::cout << " Multi-threaded overwrite: " << durationMT.count() << "ms\n";
504 :
505 1 : auto loadedST = ifc.loadGlobalConfiguration(compNameST);
506 1 : auto loadedMT = ifc.loadGlobalConfiguration(compNameMT);
507 :
508 1 : BOOST_CHECK_EQUAL(loadedST[fixture.config001Name()], v1_new);
509 1 : BOOST_CHECK_EQUAL(loadedST[fixture.config002Name()], v2_new);
510 1 : BOOST_CHECK_EQUAL(loadedMT[fixture.config001Name()], v1_new);
511 1 : BOOST_CHECK_EQUAL(loadedMT[fixture.config002Name()], v2_new);
512 :
513 1 : std::cout << " Verified: Both overwrites produced correct results\n";
514 1 : }
515 :
516 4 : BOOST_AUTO_TEST_CASE(mt_duplicate_rejection) {
517 2 : std::cout << "\n=== TEST 10: MT duplicate rejection ===\n";
518 :
519 2 : if (isFileSystemDB()) {
520 1 : std::cout << " SKIPPED: FileSystemDB has race conditions in configurationComposition\n";
521 1 : BOOST_CHECK(true);
522 1 : return;
523 : }
524 :
525 1 : auto ifc = DatabaseConfigurationInterface();
526 :
527 1 : int version1 = fixture.baseVersion + 90;
528 1 : BOOST_REQUIRE_EQUAL(fixture.createConfig001(ifc, version1), 0);
529 :
530 1 : auto map = DatabaseConfigurationInterface::config_version_map_t{};
531 1 : map[fixture.config001Name()] = version1;
532 :
533 1 : std::string compName = "MTDuplicateTest_" + std::to_string(fixture.baseVersion);
534 :
535 1 : ifc.storeGlobalConfiguration_mt(map, compName);
536 1 : std::cout << "Step 1: Created composition using _mt\n";
537 :
538 1 : std::cout << "Step 2: Attempting duplicate creation without overwrite flag...\n";
539 2 : BOOST_CHECK_THROW(ifc.storeGlobalConfiguration_mt(map, compName), std::runtime_error);
540 1 : std::cout << " Result: Exception thrown (as expected)\n";
541 :
542 1 : auto loaded = ifc.loadGlobalConfiguration(compName);
543 1 : BOOST_CHECK_EQUAL(loaded.size(), 1);
544 1 : BOOST_CHECK_EQUAL(loaded[fixture.config001Name()], version1);
545 :
546 1 : std::cout << " Verified: Original composition preserved\n";
547 1 : }
548 :
549 : BOOST_AUTO_TEST_SUITE_END()
|