Belle II Software  release-06-02-00
Database.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <boost/python/def.hpp>
10 #include <boost/python/overloads.hpp>
11 #include <boost/python/docstring_options.hpp>
12 #include <boost/python/list.hpp>
13 #include <boost/python/dict.hpp>
14 #include <boost/python/extract.hpp>
15 #include <boost/python/return_value_policy.hpp>
16 #include <boost/python/manage_new_object.hpp>
17 #include <boost/python/raw_function.hpp>
18 #include <boost/algorithm/string.hpp>
19 
20 #include <framework/database/Database.h>
21 
22 #include <framework/dataobjects/EventMetaData.h>
23 #include <framework/logging/Logger.h>
24 #include <framework/database/DBStore.h>
25 #include <framework/database/PayloadFile.h>
26 
27 #include <framework/database/PayloadProvider.h>
28 #include <framework/database/MetadataProvider.h>
29 #include <framework/database/LocalMetadataProvider.h>
30 #include <framework/database/CentralMetadataProvider.h>
31 #include <framework/database/Configuration.h>
32 
33 #include <cstdlib>
34 
35 namespace Belle2 {
42  {
43  static Database instance;
44  return instance;
45  }
46 
47  Database::~Database() = default;
48 
49  void Database::reset(bool keepConfig)
50  {
52  conf.setInitialized(false);
53  DBStore::Instance().reset(true);
55  Instance().m_metadataProvider.reset();
56  Instance().m_payloadCreation.reset();
57  if (not keepConfig)
58  conf.reset();
59  }
60 
62  {
63  // make sure we reread testing text files in case they got updated
64  for (auto& testing : m_testingPayloads) {
65  testing.reset();
66  }
67  // and return a downloader session guard for the downloader we use
69  }
70 
71  std::pair<TObject*, IntervalOfValidity> Database::getData(const EventMetaData& event, const std::string& name)
72  {
73  DBStoreEntry entry(DBStoreEntry::c_Object, name, TObject::Class(), false, true);
74  std::vector<DBQuery> query{DBQuery{name, true}};
75  getData(event, query);
76  entry.updatePayload(query[0].revision, query[0].iov, query[0].filename, query[0].checksum, query[0].globaltag, event);
77  return std::make_pair(entry.releaseObject(), query[0].iov);
78  }
79 
80  bool Database::getData(const EventMetaData& event, std::vector<DBQuery>& query)
81  {
82  // initialize lazily ...
84  // So first go over the requested payloads once, reset the info and check for any
85  // testing payloads we might want to use
86  const size_t testingPayloads = std::count_if(query.begin(), query.end(), [this, &event](auto & payload) {
87  // make sure the queries are "reset" to invalid revision and no filename before we start looking
88  payload.filename = "";
89  payload.revision = 0;
90  // and now look in all testing payload providers if any.
91  for (auto& tmp : m_testingPayloads) {
92  if (tmp.get(event, payload)) return true;
93  }
94  return false;
95  });
96  // if we already found all just return here
97  if (testingPayloads == query.size()) return true;
98  // nooow, lets look for proper payloads;
99  try {
100  m_metadataProvider->getPayloads(event.getExperiment(), event.getRun(), query);
101  } catch (std::exception&) {
102  // something went wrong with the metadata update ... so let's try next provider
103  B2WARNING("Conditions data: Problem with payload metadata provider, trying to fall back to next provider..."
104  << LogVar("provider", m_currentProvider));
105  nextMetadataProvider();
106  return getData(event, query);
107  }
108  // and if we could find the metadata lets also locate the payloads ...
109  const size_t payloadsLocated = std::count_if(query.begin(), query.end(), [this](auto & payload) {
110  // make sure we don't overwrite local payloads or otherwise already valid filenames;
111  if (!payload.filename.empty()) return true;
112  // but don't check for payloads we could not find. But this is only a
113  // problem if they are required so report success for not required
114  // payloads
115  if (payload.revision == 0) return not payload.required;
116  // and locate the payload.
117  if (not m_payloadProvider->find(payload)) {
118  // if that fails lets let the user know: Even for optional payloads, if
119  // we know the metadata but cannot find the file something is fishy and
120  // should be reported.
121  auto loglevel = payload.required ? LogConfig::c_Error : LogConfig::c_Warning;
122  B2LOG(loglevel, 0, "Conditions data: Could not find file for payload"
123  << LogVar("name", payload.name) << LogVar("revision", payload.revision)
124  << LogVar("checksum", payload.checksum) << LogVar("globaltag", payload.globaltag));
125  return not payload.required;
126  }
127  return true;
128  });
129  // did we find all payloads?
130  return payloadsLocated == query.size();
131  }
132 
133  bool Database::storeData(std::list<DBImportQuery>& query)
134  {
135  return std::all_of(query.begin(), query.end(), [this](const auto & import) {
136  return storeData(import.name, import.object, import.iov);
137  });
138  }
139 
140  bool Database::storeData(const std::string& name, TObject* obj, const IntervalOfValidity& iov)
141  {
142  if (!m_payloadCreation) initialize();
143  auto result = m_payloadCreation->storeData(name, obj, iov);
144  // we added payloads, make sure we reread testing files on next try
145  if (result) {
146  for (auto& testing : m_testingPayloads) {
147  testing.reset();
148  }
149  }
150  return result;
151  }
152 
153  std::string Database::getGlobalTags()
154  {
155  return boost::algorithm::join(m_globalTags, ",");
156  }
157 
158  void Database::nextMetadataProvider()
159  {
160  if (m_metadataConfigurations.empty()) {
161  B2FATAL("Conditions data: No more metadata providers available");
162  }
163  m_currentProvider = m_metadataConfigurations.back();
164  m_metadataConfigurations.pop_back();
165  bool remote{false};
166  if (auto pos = m_currentProvider.find("://"); pos != std::string::npos) {
167  // found a protocol: if file remove, otherwise keep as is and set as remote ...
168  auto protocol = m_currentProvider.substr(0, pos);
169  boost::algorithm::to_lower(protocol);
170  if (protocol == "file") {
171  m_currentProvider = m_currentProvider.substr(pos + 3);
172  } else if (protocol == "http" or protocol == "https") {
173  remote = true;
174  } else {
175  B2FATAL("Conditions data: Unknown metadata protocol, only supported protocols for payload metadata are file, http, https"
176  << LogVar("protocol", protocol));
177  }
178  }
179  try {
180  if (remote) {
181  m_metadataProvider = std::make_unique<Conditions::CentralMetadataProvider>(m_currentProvider, m_usableTagStates);
182  } else {
183  m_metadataProvider = std::make_unique<Conditions::LocalMetadataProvider>(m_currentProvider, m_usableTagStates);
184  }
185  } catch (std::exception& e) {
186  B2WARNING("Conditions data: Metadata provider not usable, trying next one ..."
187  << LogVar("provider", m_currentProvider) << LogVar("error", e.what()));
188  return nextMetadataProvider();
189  }
190  // and check the tags are useable
191  if (!m_metadataProvider->setTags(m_globalTags)) {
192  B2FATAL("Conditions data: Problems with globaltag configuration, cannot continue");
193  }
194  }
195 
196  void Database::initialize(const EDatabaseState target)
197  {
198  if (m_configState >= target) return;
199  auto conf = Conditions::Configuration::getInstance();
200 
201  if (m_configState == c_PreInit) {
202  // first step: freeze the configuration object and determine the list of globaltags
203  // this calculates if tag replay is possible and will create an error otherwise but
204  // it will not do anything else than setting the final list of globaltags
205  conf.setInitialized(true);
206  m_globalTags = conf.getFinalListOfTags();
207  // and remove duplicates, there's no need to look in the same gt multiple times
208  std::set<std::string> seen;
209  m_globalTags.erase(std::remove_if(m_globalTags.begin(), m_globalTags.end(),
210  [&seen](const auto & tag) {
211  return not seen.insert(tag).second;
212  }), m_globalTags.end());
213  // and also obtain usable tag states and metadata providers
214  m_usableTagStates = conf.getUsableTagStates();
215  m_metadataConfigurations = conf.getMetadataProviders();
216  // reverse because we want to pop out elements when used
217  std::reverse(m_metadataConfigurations.begin(), m_metadataConfigurations.end());
218  m_currentProvider = "";
219  m_configState = c_InitGlobaltagList;
220  }
221  // do we want to stop early?
222  if (m_configState >= target) return;
223  if (m_configState == c_InitGlobaltagList) {
224  // setup the first working provider;
225  if (m_metadataConfigurations.empty()) {
226  m_metadataProvider = std::make_unique<Conditions::NullMetadataProvider>();
227  } else {
228  nextMetadataProvider();
229  }
230  // we will actually use the globaltags so print them now
231  if (!m_globalTags.empty()) {
232  // Globaltags are useable so print out the final list we're gonna use
233  if (m_globalTags.size() == 1) {
234  B2INFO("Conditions data: configured globaltag is " << m_globalTags[0]);
235  } else {
236  B2INFO("Conditions data: configured globaltags (highest priority first) are " << boost::algorithm::join(m_globalTags, ", "));
237  }
238  }
239  // Configure payload location/download
240  m_payloadProvider = std::make_unique<Conditions::PayloadProvider>(
241  conf.getPayloadLocations(),
242  conf.getDownloadCacheDirectory(),
243  conf.getDownloadLockTimeout()
244  );
245  // Also we need to be able to create payloads ...
246  m_payloadCreation = std::make_unique<Conditions::TestingPayloadStorage>(conf.getNewPayloadLocation());
247  // And maaaybe we want to use testing payloads
248  m_testingPayloads.clear();
249  for (const auto& path : conf.getTestingPayloadLocations()) {
250  B2INFO("Conditions data: configured to use testing payloads" << LogVar("location", path));
251  m_testingPayloads.emplace_back(path);
252  }
253  // If so, warn again ... because
254  if (not m_testingPayloads.empty()) {
255  B2WARNING(R"(Conditions data: configured to look for temporary tesing payloads from one or more local folders.
256 
257  This will lead to non-reproducible results and is strictly only for testing purposes.
258  It is NOT ALLOWED for official productions or data analysis and any results obtained like this WILL NOT BE PUBLISHED.
259  )";);
260  }
261  m_configState = c_Ready;
262  }
263  }
264 
265  void Database::exposePythonAPI()
266  {
267  // To avoid confusion between std::arg and boost::python::arg we want a shorthand namespace as well
268  namespace py = boost::python;
269 
270  // Make sure the default instance is created
271  Database::Instance();
272 
273  // Don't show c++ signature in python doc to keep it simple
274  py::docstring_options options(true, true, false);
275 
276  // Expose our shiny configuration object
277  Conditions::Configuration::exposePythonAPI();
278  }
280 }
static Configuration & getInstance()
Get a reference to the instance which will be used when the Database is initialized.
ScopeGuard ensureSession()
Make sure there's an active session and return a ScopeGuard object that closes the session on destruc...
Definition: Downloader.h:43
static Downloader & getDefaultInstance()
Return the default instance.
Definition: Downloader.cc:133
Class to hold one entry from the ConditionsDB in the DBStore.
Definition: DBStoreEntry.h:47
@ c_Object
A ROOT file containing a object with the name of the DBStoreEntry.
Definition: DBStoreEntry.h:56
TObject * releaseObject()
Return the pointer to the current object and release ownership: The caller is responsible to clean up...
Definition: DBStoreEntry.h:159
Singleton base class for the low-level interface to the database.
Definition: Database.h:42
std::unique_ptr< Conditions::MetadataProvider > m_metadataProvider
Currently active metadata provider.
Definition: Database.h:232
EDatabaseState m_configState
Current configuration state of the database.
Definition: Database.h:240
EDatabaseState
State of the database.
Definition: Database.h:45
@ c_PreInit
Before any initialization.
Definition: Database.h:47
std::unique_ptr< Conditions::TestingPayloadStorage > m_payloadCreation
testing payload storage to create new payloads
Definition: Database.h:236
std::vector< Conditions::TestingPayloadStorage > m_testingPayloads
optional list of testing payload storages to look for existing payloads
Definition: Database.h:238
Store event, run, and experiment numbers.
Definition: EventMetaData.h:33
A class that describes the interval of experiments/runs for which an object in the database is valid.
Simple ScopeGuard to execute a function at the end of the object lifetime.
Definition: ScopeGuard.h:36
Class to store variables with their name which were sent to the logging service.
void reset(bool keepEntries=false)
Invalidate all payloads.
Definition: DBStore.cc:175
void initialize(const EDatabaseState target=c_Ready)
Initialize the database connection settings on first use.
Definition: Database.cc:196
~Database()
Hidden destructor, as it is a singleton.
std::pair< TObject *, IntervalOfValidity > getData(const EventMetaData &event, const std::string &name)
Request an object from the database.
Definition: Database.cc:71
static Database & Instance()
Instance of a singleton Database.
Definition: Database.cc:41
static DBStore & Instance()
Instance of a singleton DBStore.
Definition: DBStore.cc:26
static void reset(bool keepConfig=false)
Reset the database instance.
Definition: Database.cc:49
ScopeGuard createScopedUpdateSession()
Make sure we have efficient http pipelinging during initialize/beginRun but don't keep session alive ...
Definition: Database.cc:61
void updatePayload(unsigned int revision, const IntervalOfValidity &iov, const std::string &filename, const std::string &checksum, const std::string &globaltag, const EventMetaData &event)
Update the payload information for this entry and if appropriate open the new file and extract the ob...
Definition: DBStoreEntry.cc:83
Abstract base class for different kinds of events.
Simple struct to group all information necessary for a single payload.