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