LCOV - code coverage report
Current view: top level - /opt/artdaq/srcs/artdaq-database/test/CaseStudy/OtsDaqCPPInterface/test018 - MultithreadedStore_t.cc (source / functions) Coverage Total Hit
Test: artdaq.info.cleaned Lines: 100.0 % 359 359
Test Date: 2026-10-29 00:35:58 Functions: 100.0 % 27 27

            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()
        

Generated by: LCOV version 2.0-1