otsdaq  3.03.00
Database_configInterface.cc
1 #include "otsdaq/ConfigurationInterface/Database_configInterface.h"
2 #include "otsdaq/Macros/ConfigurationInterfacePluginMacros.h"
3 #include "otsdaq/Macros/CoutMacros.h"
4 #include "otsdaq/MessageFacility/MessageFacility.h"
5 
6 #include <algorithm>
7 #include <iostream>
8 #include <iterator>
9 #include <string>
10 
11 #include "artdaq-database/BasicTypes/basictypes.h"
12 #include "artdaq-database/ConfigurationDB/configurationdbifc.h"
13 #include "otsdaq/TableCore/TableBase.h"
14 
15 #include "artdaq-database/ConfigurationDB/configuration_common.h"
16 #include "artdaq-database/ConfigurationDB/dispatch_common.h"
17 #include "artdaq-database/StorageProviders/FileSystemDB/provider_filedb.h"
18 #include "artdaq-database/StorageProviders/FileSystemDB/provider_filedb_index.h"
19 
20 //artdaq database may set TRACE_NAME
21 #ifdef TRACE_NAME
22 #undef TRACE_NAME
23 #endif
24 #define TRACE_NAME __MF_DECOR__
25 
26 using namespace ots;
27 
28 using artdaq::database::basictypes::FhiclData;
29 using artdaq::database::basictypes::JsonData;
30 
32 using table_version_map_t = ots::DatabaseConfigurationInterface::table_version_map_t;
33 
34 namespace db = artdaq::database::configuration;
35 using VersionInfoList_t = db::ConfigurationInterface::VersionInfoList_t;
36 
37 constexpr auto default_dbprovider = "filesystem";
38 constexpr auto default_entity = "OTSROOT";
39 
40 //==============================================================================
41 DatabaseConfigurationInterface::DatabaseConfigurationInterface()
42 {
43 #ifdef ARTDAQ_DATABASE_DEBUG_ENABLE
44  // to enable debugging
45  {
46  artdaq::database::configuration::debug::ExportImport();
47  artdaq::database::configuration::debug::ManageAliases();
48  artdaq::database::configuration::debug::ManageConfigs();
49  artdaq::database::configuration::debug::ManageDocuments();
50  artdaq::database::configuration::debug::Metadata();
51 
52  artdaq::database::configuration::debug::detail::ExportImport();
53  artdaq::database::configuration::debug::detail::ManageAliases();
54  artdaq::database::configuration::debug::detail::ManageConfigs();
55  artdaq::database::configuration::debug::detail::ManageDocuments();
56  artdaq::database::configuration::debug::detail::Metadata();
57 
58  artdaq::database::configuration::debug::options::OperationBase();
59  artdaq::database::configuration::debug::options::BulkOperations();
60  artdaq::database::configuration::debug::options::ManageDocuments();
61  artdaq::database::configuration::debug::options::ManageConfigs();
62  artdaq::database::configuration::debug::options::ManageAliases();
63 
64  artdaq::database::configuration::debug::MongoDB();
65  artdaq::database::configuration::debug::UconDB();
66  artdaq::database::configuration::debug::FileSystemDB();
67 
68  artdaq::database::filesystem::index::debug::enable();
69 
70  // THIS TURNS OFF TRACE SLOW PATH!!! (bug? Gennadiy says was trying to avoid slowing down TRACE with too many messages on slow path)
71  artdaq::database::filesystem::debug::enable();
72 
73  // artdaq::database::mongo::debug::enable();
74 
75  // artdaq::database::docrecord::debug::JSONDocumentBuilder();
76  // artdaq::database::docrecord::debug::JSONDocument();
77 
78  // debug::registerUngracefullExitHandlers();
79  // artdaq::database::useFakeTime(true);
80  artdaq::database::configuration::Multitasker();
81  TRACE_CNTL("modeS", true); //TURN BACK ON TRACE SLOW PATH
82  }
83 #endif
84 
85  std::string envVar = __ENV__("ARTDAQ_DATABASE_URI");
86  if(envVar.length() &&
87  envVar[0] != 'f') //e.g., filesystemdb:///path/filesystemdb/test_db
88  IS_FILESYSTEM_DB = false;
89  else
90  IS_FILESYSTEM_DB = true;
91  __COUTV__(IS_FILESYSTEM_DB);
92 } //end constructor()
93 
94 //==============================================================================
98 {
99  auto start = std::chrono::high_resolution_clock::now();
100 
101  auto ifc = db::ConfigurationInterface{default_dbprovider};
102 
103  auto versionstring = version.toString();
104 
105  auto result = ifc.template loadVersion<decltype(table), JsonData>(
106  table, versionstring, default_entity);
107 
108  auto end = std::chrono::high_resolution_clock::now();
109  auto duration =
110  std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
111  __COUTT__ << "Time taken to call DatabaseConfigurationInterface::fill(tableName="
112  << table->getTableName() << ", version=" << versionstring << ") "
113  << duration << " milliseconds." << std::endl;
114 
115  if(result.first)
116  {
117  // make sure version is set.. not clear it was happening in loadVersion
118  table->getViewP()->setVersion(version);
119  return;
120  }
121  if(result.second.find("failed to create a client session") != std::string::npos ||
122  result.second.find("closed connection. calling hello") != std::string::npos)
123  {
124  __SS__ << "Error at time: " << time(0)
125  << "\n\n======> Database Interface Error while filling '"
126  << table->getTableName() << "' version '" << versionstring
127  << "' - it appears that the connection to the database been lost. Please "
128  "check the database server and route to server.\n\n"
129  << "Here is the error detail:\n\n"
130  << result.second << "\n\n"
131  << StringMacros::stackTrace() << __E__;
132  __SS_ONLY_THROW__;
133  }
134 
135  __SS__ << "\n\n======> Database Interface Error while filling '"
136  << table->getTableName() << "' version '" << versionstring
137  << "' - are you sure this version exists? Or has the connection to the "
138  "database been lost?\n\n"
139  << "Here is the error detail:\n\n"
140  << result.second << __E__;
141  __SS_ONLY_THROW__;
142 } // end fill()
143 
144 //==============================================================================
147  bool overwrite) const
148 {
149  auto start = std::chrono::high_resolution_clock::now();
150 
151  auto ifc = db::ConfigurationInterface{default_dbprovider};
152 
153  auto versionstring = table->getView().getVersion().toString();
154  std::stringstream preSaveJSONss;
155  table->getView().printJSON(preSaveJSONss);
156  //__COUT__ << "versionstring: " << versionstring << "\n";
157 
158  // auto result =
159  // ifc.template storeVersion<decltype(configuration), JsonData>(configuration,
160  // versionstring, default_entity);
161  auto result = overwrite ? ifc.template overwriteVersion<decltype(table), JsonData>(
162  table, versionstring, default_entity)
163  : ifc.template storeVersion<decltype(table), JsonData>(
164  table, versionstring, default_entity);
165 
166  auto end = std::chrono::high_resolution_clock::now();
167  auto duration =
168  std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
169  __COUTT__ << "Time taken to call "
170  "DatabaseConfigurationInterface::saveActiveVersion(tableName="
171  << table->getTableName() << ", versionstring=" << versionstring << ") "
172  << duration << " milliseconds" << std::endl;
173 
174  __COUTTV__(result.first);
175  __COUTVS__(10, result.second);
176 
177  if(result.first)
178  {
179  { //check that table save worked (FIXME -- this is temporary while waiting for permanent solution from artdaq-database developments)
180 
181  TableBase localDocLoader(
182  table->getTableName()); //can not use special table when filling
183  localDocLoader.changeVersionAndActivateView(
184  localDocLoader.createTemporaryView(), table->getView().getVersion());
185  fill(&localDocLoader, table->getView().getVersion());
186 
187  std::stringstream postSaveJSONss;
188  localDocLoader.getView().printJSON(postSaveJSONss);
189 
190  __COUTVS__(2, preSaveJSONss.str());
191  __COUTVS__(2, postSaveJSONss.str());
192  bool same = true;
193  //compare and ignore white space (since json might shift)
194  {
195  auto preSaveJSON = preSaveJSONss.str();
196  auto postSaveJSON = postSaveJSONss.str();
197  size_t prec = 0, postc = 0;
198  for(; prec < preSaveJSON.size() && postc < postSaveJSON.size();
199  ++prec, ++postc)
200  {
201  if(preSaveJSON[prec] == '\n' || preSaveJSON[prec] == '\t' ||
202  preSaveJSON[prec] == ' ')
203  {
204  //advance only prec to skip whitespace
205  --postc;
206  continue;
207  }
208  else if(postSaveJSON[postc] == '\n' || postSaveJSON[postc] == '\t' ||
209  postSaveJSON[postc] == ' ')
210  {
211  //advance only postc to skip whitespace
212  --prec;
213  continue;
214  }
215  if(preSaveJSON[prec] != postSaveJSON[postc])
216  {
217  __COUTT__ << "Mismatch at preSaveJSON[" << prec
218  << "] != postSaveJSON[" << postc << "] ... "
219  << preSaveJSON.substr(prec, 30)
220  << " != " << postSaveJSON.substr(postc, 30) << __E__;
221  same = false;
222  break;
223  }
224  }
225  //Note: is ok to not consider the case when prec != postc
226  // because json must have a closing bracket and must have been matched to be same.
227  }
228 
229  if(same)
230  __COUTT__ << "Same";
231  else
232  {
233  __COUT__ << "NOT Same";
234  auto end = std::chrono::high_resolution_clock::now();
235  auto duration =
236  std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
237  .count();
238  __COUTT__
239  << "Time taken to call "
240  "DatabaseConfigurationInterface::saveActiveVersion(tableName="
241  << table->getTableName() << ", versionstring=" << versionstring
242  << ") " << duration << " milliseconds" << std::endl;
243 
244  __SS__ << "Error saving table '" << table->getTableName() << "'-v"
245  << versionstring
246  << " (perhaps there was a collision with another user saving the "
247  "same table name/version?! Please try again with an "
248  "incremented table version). "
249  << "Expected data size is " << preSaveJSONss.str().size()
250  << " and readback found size of " << postSaveJSONss.str().size()
251  << " with character mismatches." << __E__;
252  __SS_THROW__;
253  }
254 
255  auto end = std::chrono::high_resolution_clock::now();
256  auto duration =
257  std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
258  .count();
259  __COUTT__ << "Time taken to call "
260  "DatabaseConfigurationInterface::saveActiveVersion(tableName="
261  << table->getTableName() << ", versionstring=" << versionstring
262  << ") " << duration << " milliseconds" << std::endl;
263 
264  } //end check that table save worked
265  return;
266  }
267 
268  __SS__ << "Database Interface saveActiveVersion Error:" << result.second << __E__;
269  __SS_THROW__;
270 } //end saveActiveVersion()
271 
272 //==============================================================================
275  const TableBase* table) const noexcept
276 {
277  auto versions = getVersions(table);
278 
279  if(TTEST(1))
280  {
281  __COUTT__ << "Table Name: " << table->getTableName() << __E__;
282  __SS__ << "All Versions: ";
283  for(auto& v : versions)
284  ss << v << " ";
285  ss << __E__;
286  __COUTT__ << "\n" << ss.str();
287  }
288 
289  if(!versions.size())
290  return TableVersion(); // return INVALID
291 
292  return *(versions.rbegin());
293 } //end findLatestVersion()
294 
295 //==============================================================================
298  const TableBase* table) const noexcept
299 try
300 {
301  auto start = std::chrono::high_resolution_clock::now();
302 
303  auto ifc = db::ConfigurationInterface{default_dbprovider};
304  auto result = ifc.template getVersions<decltype(table)>(table, default_entity);
305 
306  auto end = std::chrono::high_resolution_clock::now();
307  auto duration =
308  std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
309  __COUTT__
310  << "Time taken to call DatabaseConfigurationInterface::getVersions(tableName="
311  << table->getTableName() << ") " << duration << " milliseconds." << std::endl;
312 
313  auto resultSet = std::set<TableVersion>{};
314  for(std::string const& version : result)
315  resultSet.insert(TableVersion(std::stol(version, 0, 10)));
316 
317  __COUTVS__(10, StringMacros::setToString(resultSet));
318 
319  return resultSet;
320 } //end getVersions()
321 catch(std::exception const& e)
322 {
323  __COUT_WARN__ << "Database Interface Exception:" << e.what() << "\n";
324  return {};
325 } //end getVersions() catch
326 
327 //==============================================================================
329 std::set<std::string /*name*/> DatabaseConfigurationInterface::getAllTableNames() const
330 try
331 {
332  auto start = std::chrono::high_resolution_clock::now();
333 
334  auto ifc = db::ConfigurationInterface{default_dbprovider};
335  auto collection_name_prefix = std::string{};
336 
337  auto result = ifc.listCollections(collection_name_prefix);
338 
339  auto end = std::chrono::high_resolution_clock::now();
340  auto duration =
341  std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
342  __COUTT__
343  << "Time taken to call "
344  "DatabaseConfigurationInterface::getAllTableNames(collection_name_prefix="
345  << collection_name_prefix << ") " << duration << " milliseconds." << std::endl;
346 
347  return result;
348 } //end getAllTableNames()
349 catch(std::exception const& e)
350 {
351  __SS__ << "Database Interface Exception:" << e.what() << "\n";
352  __SS_THROW__;
353 }
354 catch(...)
355 {
356  __SS__ << "Database Interface Unknown exception.\n";
357  __SS_THROW__;
358 } //end getAllTableNames() catch
359 
360 //==============================================================================
363  std::string const& filterString) const
364 try
365 {
366  auto start = std::chrono::high_resolution_clock::now();
367 
368  auto ifc = db::ConfigurationInterface{default_dbprovider};
369 
370  auto result = std::set<std::string>();
371 
372  if(filterString == "")
373  result = ifc.findGlobalConfigurations("*"); // GConfig will return all GConfig*
374  // with filesystem db.. for mongodb
375  // would require reg expr
376  else
377  result = ifc.findGlobalConfigurations(filterString + "*"); // GConfig will return
378  // all GConfig* with
379  // filesystem db.. for
380  // mongodb would require
381  // reg expr
382  auto end = std::chrono::high_resolution_clock::now();
383  auto duration =
384  std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
385  __COUTT__ << "Time taken to call "
386  "DatabaseConfigurationInterface::getAllTableGroupNames(filterString="
387  << filterString << ") " << duration << " milliseconds." << std::endl;
388 
389  return result;
390 } //end getAllTableGroupNames()
391 catch(std::exception const& e)
392 {
393  __SS__ << "Filter string '" << filterString
394  << "' yielded Database Interface Exception:" << e.what() << "\n";
395  __SS_THROW__;
396 }
397 catch(...)
398 {
399  __SS__ << "Filter string '" << filterString
400  << "' yielded Database Interface Unknown exception.\n";
401  __SS_THROW__;
402 } //end getAllTableGroupNames() catch
403 
404 //==============================================================================
408  const std::string& groupName) const noexcept
409 {
410  //attempt to use cache first! (potentially way faster .04 s vs 4 s)
411  try
412  {
413  TableBase localGroupMemberCacheLoader(
414  true /*special table*/
415  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),,
416  TableBase::GROUP_CACHE_PREPEND + groupName);
417  TableVersion lastestGroupCacheKey =
418  findLatestVersion(&localGroupMemberCacheLoader);
419  __COUTTV__(lastestGroupCacheKey);
420  if(!lastestGroupCacheKey.isInvalid())
421  return TableGroupKey(lastestGroupCacheKey.version());
422  }
423  catch(...)
424  {
425  __COUT__ << "Ignoring cache loading error." << __E__;
426  }
427 
428  std::set<TableGroupKey> keys = DatabaseConfigurationInterface::getKeys(groupName);
429  if(keys.size()) // if keys exist, return the last
430  return *(keys.crbegin());
431 
432  // else, return invalid
433  return TableGroupKey();
434 } //end findLatestGroupKey()
435 
436 //==============================================================================
439  const std::string& groupName) const
440 {
441  std::set<TableGroupKey> retSet;
442  std::set<std::string /*name*/> names = getAllTableGroupNames();
443  for(auto& n : names)
444  if(n.find(groupName) == 0)
445  retSet.insert(TableGroupKey(n));
446  return retSet;
447 } // end getKeys()
448 
449 //==============================================================================
452  std::string const& tableGroup, bool includeMetaDataTable /* = false */) const
453 try
454 {
455  auto start = std::chrono::high_resolution_clock::now();
456 
457  //Flow (motivation -- getTableGroupMembers() is super slow; can be 3 to 15 seconds):
458  // when saveTableGroup() is called
459  // saveDocument (collection: "GroupCache" + tableGroup, version: groupKey)
460  // containing --> table group members
461  // when getTableGroupMembers() is called
462  // loadDocument (collection: "GroupCache" + tableGroup, version: groupKey)
463  // if succeeds, use that
464  // else continue with db extended lookup
465  // AND create cache file (in this way slowly populating the cache even without saves)
466 
467  // format: groupName + "_v" + groupKey
468  try
469  {
470  table_version_map_t retMap = getCachedTableGroupMembers(tableGroup);
471  __COUTV__(tableGroup);
472  __COUTS__(20) << (StringMacros::mapToString(retMap));
473 
474  if(!includeMetaDataTable)
475  {
476  // remove special meta data table from member map
477  auto metaTable = retMap.find(TableBase::GROUP_METADATA_TABLE_NAME);
478  if(metaTable != retMap.end())
479  retMap.erase(metaTable);
480  }
481 
482  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
483  std::chrono::high_resolution_clock::now() - start)
484  .count();
485  __COUTT__ << "Time taken to call "
486  "DatabaseConfigurationInterface::getTableGroupMembers(tableGroup="
487  << tableGroup << ") " << duration << " milliseconds." << std::endl;
488  return retMap;
489  }
490  catch(...) //ignore error and proceed with standard db access
491  {
492  __COUTT__ << "Ignoring error "
493  "DatabaseConfigurationInterface::getTableGroupMembers(tableGroup="
494  << tableGroup << ") " << __E__;
495  }
496 
497  auto ifc = db::ConfigurationInterface{default_dbprovider};
498  auto result = ifc.loadGlobalConfiguration(tableGroup);
499 
500  if(TTEST(1))
501  {
502  for(auto& item : result)
503  __COUTT__ << "====================> " << item.configuration << ": "
504  << item.version << __E__;
505  }
506 
507  auto to_map = [](auto const& inputList, bool includeMetaDataTable) {
508  auto resultMap = table_version_map_t{};
509 
510  std::for_each(inputList.begin(), inputList.end(), [&resultMap](auto const& info) {
511  resultMap[info.configuration] = std::stol(info.version, 0, 10);
512  });
513 
514  if(!includeMetaDataTable)
515  {
516  // remove special meta data table from member map
517  auto metaTable = resultMap.find(TableBase::GROUP_METADATA_TABLE_NAME);
518  if(metaTable != resultMap.end())
519  resultMap.erase(metaTable);
520  }
521  return resultMap;
522  };
523 
524  table_version_map_t retMap = to_map(result, includeMetaDataTable);
525 
526  //now create cache for next time!
527  saveTableGroupMemberCache(retMap, tableGroup);
528 
529  __COUTT__ << "Loaded db member map string " << StringMacros::mapToString(retMap)
530  << __E__;
531 
532  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
533  std::chrono::high_resolution_clock::now() - start)
534  .count();
535  __COUTT__ << "Time taken to call "
536  "DatabaseConfigurationInterface::getTableGroupMembers(tableGroup="
537  << tableGroup << ") " << duration << " milliseconds." << std::endl;
538 
539  return retMap;
540 } // end getTableGroupMembers()
541 catch(std::exception const& e)
542 {
543  __SS__ << "Database Interface Exception getting Group's member tables for '"
544  << tableGroup << "':\n\n"
545  << e.what() << "\n";
546  if(std::string(e.what()).find("connection refused") != std::string::npos)
547  {
548  ss << "\n\nConnection to database refused. Perhaps your ssh tunnel has "
549  "closed?\n\n";
550  }
551  __SS_THROW__;
552 }
553 catch(...)
554 {
555  __SS__ << "Database Interface Unknown exception getting Group's member tables for '"
556  << tableGroup << ".'\n";
557  __COUT_ERR__ << ss.str();
558  __SS_THROW__;
559 } // end getTableGroupMembers() catch
560 
561 //==============================================================================
564 table_version_map_t DatabaseConfigurationInterface::getCachedTableGroupMembers(
565  std::string const& tableGroup) const
566 try
567 {
568  table_version_map_t retMap;
569 
570  //Flow:
571  // when saveTableGroup() is called
572  // saveDocument (collection: "GroupCache_" + tableGroup, version: groupKey)
573  // containing --> table group members
574  // when getTableGroupMembers() is called
575  // loadDocument (collection: "GroupCache_" + tableGroup, version: groupKey)
576  // if succeeds, use that
577  // else continue with db extended lookup
578  // AND create cache file (in this way slowly populating the cache even without saves)
579 
580  // tableGroup format: groupName + "_v" + groupKey
581 
582  std::size_t vi = tableGroup.rfind("_v");
583  std::string groupName = tableGroup.substr(0, vi);
584  std::string groupKey = tableGroup.substr(vi + 2);
585  __COUTT__ << "Getting cache for " << groupName << "(" << groupKey << ")" << __E__;
586 
587  TableBase localGroupMemberCacheSaver(
588  true /*special table*/
589  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),
590  TableBase::GROUP_CACHE_PREPEND + groupName);
591  TableVersion localVersion(atoi(groupKey.c_str()));
592 
593  //if filesystem db, as of April 2024, artdaq_database returned latest version when version is missing...
594  if(IS_FILESYSTEM_DB)
595  {
596  __COUTT__ << "IS_FILESYSTEM_DB=true, so checking cached keys for " << groupName
597  << "(" << groupKey << ")" << __E__;
598  std::set<TableVersion> versions = getVersions(&localGroupMemberCacheSaver);
599  if(versions.find(localVersion) == versions.end())
600  {
601  __SS__ << "Cached member table versions not found for " << groupName << "("
602  << groupKey << ")" << __E__;
603  __SS_THROW__;
604  }
605  }
606 
607  localGroupMemberCacheSaver.changeVersionAndActivateView(
608  localGroupMemberCacheSaver.createTemporaryView(), localVersion);
609 
610  fill(&localGroupMemberCacheSaver, localVersion);
611 
612  __COUTS__(20) << "Loaded cache member map string "
613  << localGroupMemberCacheSaver.getViewP()->getCustomStorageData()
614  << __E__;
615 
616  { //get table member map from cleaned json string
617  //remove json { } and all " characters
618  std::string jsonClean = "";
619  const std::string& json =
620  localGroupMemberCacheSaver.getViewP()->getCustomStorageData();
621  for(auto& c : json)
622  if(c == '{' || c == '}' || c == '"' || c == ' ')
623  continue;
624  else
625  jsonClean += c;
626  __COUTVS__(21, jsonClean);
627  StringMacros::getMapFromString(jsonClean, retMap);
628  }
629 
630  __COUTS__(20) << "Loaded cache member map string "
631  << StringMacros::mapToString(retMap) << __E__;
632 
633  return retMap;
634 } //end getCachedTableGroupMembers()
635 catch(std::exception const& e)
636 {
637  __SS__ << "Database Interface Exception getCachedTableGroupMembers for '"
638  << tableGroup << "':\n\n"
639  << e.what() << "\n";
640  __SS_THROW__;
641 }
642 catch(...)
643 {
644  __SS__ << "Database Interface Unknown exception getCachedTableGroupMembers for '"
645  << tableGroup << ".'\n";
646  __SS_THROW__;
647 } //end getCachedTableGroupMembers() catch
648 
649 //==============================================================================
651 void DatabaseConfigurationInterface::saveTableGroupMemberCache(
652  table_version_map_t const& memberMap, std::string const& tableGroup) const
653 try
654 {
655  //Flow:
656  // when saveTableGroup() is called
657  // saveDocument (collection: "GroupCache_" + tableGroup, version: groupKey)
658  // containing --> table group members
659  // when getTableGroupMembers() is called
660  // loadDocument (collection: "GroupCache_" + tableGroup, version: groupKey)
661  // if succeeds, use that
662  // else continue with db extended lookup
663  // AND create cache file (in this way slowly populating the cache even without saves)
664 
665  // tableGroup format: groupName + "_v" + groupKey
666 
667  std::size_t vi = tableGroup.rfind("_v");
668  std::string groupName = tableGroup.substr(0, vi);
669  std::string groupKey = tableGroup.substr(vi + 2);
670  __COUTT__ << "Saving cache for " << groupName << "(" << groupKey << ")" << __E__;
671 
672  TableBase localGroupMemberCacheSaver(
673  true /*special table*/
674  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),
675  TableBase::GROUP_CACHE_PREPEND + groupName);
676  localGroupMemberCacheSaver.changeVersionAndActivateView(
677  localGroupMemberCacheSaver.createTemporaryView(),
678  TableVersion(atoi(groupKey.c_str())));
679 
680  { //set custom storage data
681  std::stringstream groupCacheData;
682  groupCacheData << "{ ";
683  for(const auto& member : memberMap)
684  groupCacheData << (member.first == memberMap.begin()->first ? "" : ", ")
685  << //skip comma on first
686  "\"" << member.first << "\" : \"" << member.second << "\"";
687  groupCacheData << "}";
688 
689  localGroupMemberCacheSaver.getViewP()->setCustomStorageData(groupCacheData.str());
690  } //end set custom storage data
691 
692  __COUTT__ << "Saving member map string "
693  << localGroupMemberCacheSaver.getViewP()->getCustomStorageData() << __E__;
694 
695  __COUTT__ << "Saving cache table "
696  << localGroupMemberCacheSaver.getView().getTableName() << "("
697  << localGroupMemberCacheSaver.getView().getVersion().toString() << ")"
698  << __E__;
699 
700  // save to db, and do not allow overwrite
701  saveActiveVersion(&localGroupMemberCacheSaver, false /* overwrite */);
702 
703 } //end saveTableGroupMemberCache()
704 catch(std::exception const& e)
705 {
706  __SS__ << "Database Interface Exception saveTableGroupMemberCache for '" << tableGroup
707  << "':\n\n"
708  << e.what() << "\n";
709  __COUT_ERR__ << ss.str();
710  __SS_THROW__;
711 }
712 catch(...)
713 {
714  __SS__ << "Database Interface Unknown exception saveTableGroupMemberCache for '"
715  << tableGroup << ".'\n";
716  __COUT_ERR__ << ss.str();
717  __SS_THROW__;
718 } //end saveTableGroupMemberCache() catch
719 
720 //==============================================================================
722 void DatabaseConfigurationInterface::saveTableGroup(table_version_map_t const& memberMap,
723  std::string const& tableGroup) const
724 try
725 {
726  auto start = std::chrono::high_resolution_clock::now();
727 
728  auto ifc = db::ConfigurationInterface{default_dbprovider};
729 
730  //======
732  auto to_list = [](auto const& inputMap) {
733  auto resultList = VersionInfoList_t{};
734  std::transform(
735  inputMap.begin(),
736  inputMap.end(),
737  std::back_inserter(resultList),
738  [](auto const& mapEntry) {
739  return VersionInfoList_t::value_type{
740  mapEntry.first, mapEntry.second.toString(), default_entity};
741  });
742 
743  return resultList;
744  };
745  __COUTTV__(StringMacros::mapToString(memberMap));
746 
747  auto result = IS_FILESYSTEM_DB
748  ? ifc.storeGlobalConfiguration(to_list(memberMap), tableGroup)
749  : ifc.storeGlobalConfiguration_mt(to_list(memberMap), tableGroup);
750 
751  __COUTTV__(result.first);
752  __COUTVS__(10, result.second);
753 
754  auto end = std::chrono::high_resolution_clock::now();
755  auto duration =
756  std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
757  __COUTT__
758  << "Time taken to call DatabaseConfigurationInterface::saveTableGroup(tableGroup="
759  << tableGroup << ") " << duration << " milliseconds." << std::endl;
760 
761  if(result.first)
762  {
763  { //check that group save worked (FIXME -- this is temporary while waiting for permanent solution from artdaq-database developments)
764  auto readbackResult = ifc.loadGlobalConfiguration(tableGroup);
765 
766  if(TTEST(1))
767  for(auto& item : readbackResult)
768  __COUTT__ << "--==> " << item.configuration << ": " << item.version
769  << __E__;
770 
771  size_t countOfMatches = 0;
772  for(auto& item : readbackResult)
773  {
774  __COUTT__ << "====================> " << item.configuration << ": "
775  << item.version << __E__;
776  const auto& it = memberMap.find(item.configuration);
777  if(it == memberMap.end() ||
778  it->second != TableVersion(std::stol(item.version, 0, 10)))
779  {
780  auto end = std::chrono::high_resolution_clock::now();
781  auto duration =
782  std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
783  .count();
784  __COUTT__
785  << "Time taken to call "
786  "DatabaseConfigurationInterface::saveTableGroup(tableGroup="
787  << tableGroup << ") " << duration << " milliseconds."
788  << std::endl;
789  __SS__ << "Error saving group '" << tableGroup
790  << "' (perhaps there was a collision with another user saving "
791  "the same group name?! Please try again with an "
792  "incremented group key)). Table '"
793  << item.configuration << "'-v" << item.version
794  << " was unexpectedly read back as a member table after the "
795  "attempted group save. Expected member tables of group '"
796  << tableGroup << "' are as follows:" << __E__;
797  for(const auto& memberPair : memberMap)
798  ss << "\t" << memberPair.first << "-v" << memberPair.second
799  << __E__;
800  __SS_THROW__;
801  }
802  else
803  ++countOfMatches;
804  } //end individual member check
805 
806  //make sure all members were found
807  if(countOfMatches != memberMap.size())
808  {
809  auto end = std::chrono::high_resolution_clock::now();
810  auto duration =
811  std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
812  .count();
813  __COUTT__ << "Time taken to call "
814  "DatabaseConfigurationInterface::saveTableGroup(tableGroup="
815  << tableGroup << ") " << duration << " milliseconds."
816  << std::endl;
817  __SS__ << "Error saving group '" << tableGroup
818  << "' (perhaps there was a collision with another user saving the "
819  "same group name?! Please try again with an incremented group "
820  "key). "
821  << "Expected group count is " << memberMap.size() << ", and found "
822  << countOfMatches << " matching tables during readback check."
823  << __E__;
824  __SS_THROW__;
825  }
826  __COUTT__ << "Readback check passed." << __E__;
827  } //end check that group save worked
828 
829  {
830  auto end = std::chrono::high_resolution_clock::now();
831  auto duration =
832  std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
833  .count();
834  __COUTT__ << "Time taken to call "
835  "DatabaseConfigurationInterface::saveTableGroup(tableGroup="
836  << tableGroup << ") " << duration << " milliseconds." << std::endl;
837  }
838 
839  //now save to db cache for reverse index lookup of group members
840  try
841  {
842  saveTableGroupMemberCache(memberMap, tableGroup);
843  }
844  catch(...)
845  {
846  __COUT_WARN__ << "Ignoring errors during saveTableGroupMemberCache()"
847  << __E__;
848  }
849 
850  {
851  auto end = std::chrono::high_resolution_clock::now();
852  auto duration =
853  std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
854  .count();
855  __COUTT__ << "Time taken to call "
856  "DatabaseConfigurationInterface::saveTableGroup(tableGroup="
857  << tableGroup << ") " << duration << " milliseconds." << std::endl;
858  }
859 
860  return;
861  }
862 
863  __THROW__(result.second);
864 } // end saveTableGroup()
865 catch(std::exception const& e)
866 {
867  __SS__ << "Database Interface Exception saveTableGroup for '" << tableGroup
868  << "':\n\n"
869  << e.what() << "\n";
870  __SS_THROW__;
871 }
872 catch(...)
873 {
874  __SS__ << "Database Interface Unknown exception saveTableGroup for '" << tableGroup
875  << ".'\n";
876  __SS_THROW__;
877 } //end saveTableGroup() catch
878 
879 //==============================================================================
881 std::pair<std::string, TableVersion> DatabaseConfigurationInterface::saveCustomJSON(
882  const std::string& json, const std::string& documentNameToSave) const
883 try
884 {
885  __COUTT__ << "Saving doc '" << documentNameToSave << "'" << __E__;
886 
887  TableBase localDocSaver(
888  true /*special table*/
889  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table)
890  TableBase::JSON_DOC_PREPEND + documentNameToSave);
891 
892  std::set<TableVersion> versions = getVersions(&localDocSaver);
893  TableVersion version;
894  if(versions.size())
895  version = TableVersion::getNextVersion(*versions.rbegin());
896  else
897  version = TableVersion::DEFAULT;
898  __COUTV__(version);
899 
900  localDocSaver.changeVersionAndActivateView(localDocSaver.createTemporaryView(),
901  version);
902 
903  localDocSaver.getViewP()->setCustomStorageData(json);
904 
905  __COUTS__(10) << "Saving JSON string: "
906  << localDocSaver.getViewP()->getCustomStorageData() << __E__;
907 
908  __COUTT__ << "Saving JSON doc as " << localDocSaver.getView().getTableName() << "("
909  << localDocSaver.getView().getVersion().toString() << ")" << __E__;
910 
911  // save to db, and do not allow overwrite
912  saveActiveVersion(&localDocSaver, false /* overwrite */);
913 
914  return std::make_pair(localDocSaver.getTableName(),
915  localDocSaver.getView().getVersion());
916 } //end saveCustomJSON()
917 catch(std::exception const& e)
918 {
919  __SS__ << "Database Interface Exception saveCustomJSON for '" << documentNameToSave
920  << "':\n\n"
921  << e.what() << "\n";
922  __COUT_ERR__ << ss.str();
923  __SS_THROW__;
924 }
925 catch(...)
926 {
927  __SS__ << "Database Interface Unknown exception saveCustomJSON for '"
928  << documentNameToSave << ".'\n";
929  __COUT_ERR__ << ss.str();
930  __SS_THROW__;
931 } //end saveCustomJSON() catch
932 
933 //==============================================================================
935 std::string DatabaseConfigurationInterface::loadCustomJSON(
936  const std::string& documentNameToLoad, TableVersion documentVersionToLoad) const
937 try
938 {
939  __COUTT__ << "Loading doc '" << documentNameToLoad << "-v" << documentVersionToLoad
940  << "'" << __E__;
941 
942  TableBase localDocLoader(
943  true /*special table*/
944  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),
945  TableBase::JSON_DOC_PREPEND + documentNameToLoad);
946 
947  localDocLoader.changeVersionAndActivateView(localDocLoader.createTemporaryView(),
948  documentVersionToLoad);
949 
950  fill(&localDocLoader, documentVersionToLoad);
951 
952  __COUTS__(10) << "Loaded JSON doc string "
953  << localDocLoader.getViewP()->getCustomStorageData() << __E__;
954 
955  return localDocLoader.getViewP()->getCustomStorageData();
956 } //end loadCustomJSON()
957 catch(std::exception const& e)
958 {
959  __SS__ << "Database Interface Exception saveCustomJSON for '" << documentNameToLoad
960  << "-v" << documentVersionToLoad << "':\n\n"
961  << e.what() << "\n";
962  __COUTS__(3) << ss.str();
963  __SS_ONLY_THROW__;
964 }
965 catch(...)
966 {
967  __SS__ << "Database Interface Unknown exception saveCustomJSON for '"
968  << documentNameToLoad << "-v" << documentVersionToLoad << ".'\n";
969  __COUTS__(3) << ss.str();
970  __SS_ONLY_THROW__;
971 } //end loadCustomJSON() catch
972 
973 DEFINE_OTS_CONFIGURATION_INTERFACE(DatabaseConfigurationInterface)
void saveTableGroup(table_version_map_t const &memberMap, std::string const &tableGroup) const override
create a new table group from the contents map
TableVersion findLatestVersion(const TableBase *table) const noexcept override
find the latest table version by table type
table_version_map_t getTableGroupMembers(std::string const &tableGroup, bool includeMetaDataTable=false) const override
return the contents of a table group
std::set< std::string > getAllTableGroupNames(std::string const &filterString="") const override
find all table groups in database
std::set< TableGroupKey > getKeys(const std::string &groupName) const override
find all configuration groups in database
std::set< TableVersion > getVersions(const TableBase *table) const noexcept override
find all table versions by table type
std::set< std::string > getAllTableNames(void) const override
returns a list of all table names
void fill(TableBase *table, TableVersion version) const override
read table from database
TableGroupKey findLatestGroupKey(const std::string &groupName) const noexcept override
void saveActiveVersion(const TableBase *table, bool overwrite=false) const override
write table to database
const std::string & getTableName(void) const
Getters.
Definition: TableBase.cc:814
TableVersion createTemporaryView(TableVersion sourceViewVersion=TableVersion(), TableVersion destTemporaryViewVersion=TableVersion::getNextTemporaryVersion())
source of -1, from MockUp, else from valid view version
Definition: TableBase.cc:1734
std::string toString(void) const
toString
Definition: TableVersion.cc:33
bool isInvalid(void) const
isInvalid
unsigned int version(void) const
Definition: TableVersion.cc:29
void setVersion(const T &version)
< in included .icc source
const std::string & getCustomStorageData(void) const
Getters.
Definition: TableView.h:71
void setCustomStorageData(const std::string &storageData)
Definition: TableView.h:168
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
getMapFromString ~
static std::string stackTrace(void)