Belle II Software  release-05-02-19
Database.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2015-2018 Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Thomas Kuhr, Martin Ritter *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <boost/python/def.hpp>
12 #include <boost/python/overloads.hpp>
13 #include <boost/python/docstring_options.hpp>
14 #include <boost/python/list.hpp>
15 #include <boost/python/dict.hpp>
16 #include <boost/python/extract.hpp>
17 #include <boost/python/return_value_policy.hpp>
18 #include <boost/python/manage_new_object.hpp>
19 #include <boost/python/raw_function.hpp>
20 #include <boost/algorithm/string.hpp>
21 
22 #include <framework/database/Database.h>
23 
24 #include <framework/dataobjects/EventMetaData.h>
25 #include <framework/logging/Logger.h>
26 #include <framework/database/LocalDatabase.h>
27 #include <framework/database/ConditionsDatabase.h>
28 #include <framework/database/DatabaseChain.h>
29 #include <framework/database/DBStore.h>
30 #include <framework/database/PayloadFile.h>
31 
32 #include <framework/database/PayloadProvider.h>
33 #include <framework/database/MetadataProvider.h>
34 #include <framework/database/LocalMetadataProvider.h>
35 #include <framework/database/CentralMetadataProvider.h>
36 #include <framework/database/Configuration.h>
37 
38 #include <cstdlib>
39 
40 namespace Belle2 {
46  Database& Database::Instance()
47  {
48  static Database instance;
49  return instance;
50  }
51 
52  Database::~Database() = default;
53 
54  void Database::reset(bool keepConfig)
55  {
57  conf.setInitialized(false);
58  DBStore::Instance().reset(true);
60  Instance().m_metadataProvider.reset();
61  Instance().m_payloadCreation.reset();
62  if (not keepConfig)
63  conf.reset();
64  }
65 
67  {
68  // make sure we reread testing text files in case they got updated
69  for (auto& testing : m_testingPayloads) {
70  testing.reset();
71  }
72  // and return a downloader session guard for the downloader we use
74  }
75 
76  std::pair<TObject*, IntervalOfValidity> Database::getData(const EventMetaData& event, const std::string& name)
77  {
78  DBStoreEntry entry(DBStoreEntry::c_Object, name, TObject::Class(), false, true);
79  std::vector<DBQuery> query{DBQuery{name, true}};
80  getData(event, query);
81  entry.updatePayload(query[0].revision, query[0].iov, query[0].filename, query[0].checksum, event);
82  return std::make_pair(entry.releaseObject(), query[0].iov);
83  }
84 
85  bool Database::getData(const EventMetaData& event, std::vector<DBQuery>& query)
86  {
87  // initialize lazily ...
89  // So first go over the requested payloads once, reset the info and check for any
90  // testing payloads we might want to use
91  const size_t testingPayloads = std::count_if(query.begin(), query.end(), [this, &event](auto & payload) {
92  // make sure the queries are "reset" to invalid revision and no filename before we start looking
93  payload.filename = "";
94  payload.revision = 0;
95  // and now look in all testing payload providers if any.
96  for (auto& tmp : m_testingPayloads) {
97  if (tmp.get(event, payload)) return true;
98  }
99  return false;
100  });
101  // if we already found all just return here
102  if (testingPayloads == query.size()) return true;
103  // nooow, lets look for proper payloads;
104  try {
105  m_metadataProvider->getPayloads(event.getExperiment(), event.getRun(), query);
106  } catch (std::exception&) {
107  // something went wrong with the metadata update ... so let's try next provider
108  B2ERROR("Conditions data: Problem with payload metadata, trying to fall back to next provider ...");
109  nextMetadataProvider();
110  return getData(event, query);
111  }
112  // and if we could find the metadata lets also locate the payloads ...
113  const size_t payloadsLocated = std::count_if(query.begin(), query.end(), [this](auto & payload) {
114  // make sure we don't overwrite local payloads or otherwise already valid filenames;
115  if (!payload.filename.empty()) return true;
116  // but don't check for payloads we could not find. But this is only a
117  // problem if they are required so report success for not required
118  // payloads
119  if (payload.revision == 0) return not payload.required;
120  // and locate the payload.
121  if (not m_payloadProvider->find(payload)) {
122  // if that fails lets let the user know: Even for optional payloads, if
123  // we know the metadata but cannot find the file something is fishy and
124  // should be reported.
125  auto loglevel = payload.required ? LogConfig::c_Error : LogConfig::c_Warning;
126  B2LOG(loglevel, 0, "Conditions data: Could not find file for payload"
127  << LogVar("name", payload.name) << LogVar("revision", payload.revision)
128  << LogVar("checksum", payload.checksum) << LogVar("globaltag", payload.globaltag));
129  return not payload.required;
130  }
131  return true;
132  });
133  // did we find all payloads?
134  return payloadsLocated == query.size();
135  }
136 
137  bool Database::storeData(std::list<DBImportQuery>& query)
138  {
139  return std::all_of(query.begin(), query.end(), [this](const auto & import) {
140  return storeData(import.name, import.object, import.iov);
141  });
142  }
143 
144  bool Database::storeData(const std::string& name, TObject* obj, const IntervalOfValidity& iov)
145  {
146  if (!m_payloadCreation) initialize();
147  auto result = m_payloadCreation->storeData(name, obj, iov);
148  // we added payloads, make sure we reread testing files on next try
149  if (result) {
150  for (auto& testing : m_testingPayloads) {
151  testing.reset();
152  }
153  }
154  return result;
155  }
156 
157  std::string Database::getGlobalTags()
158  {
159  return boost::algorithm::join(m_globalTags, ",");
160  }
161 
162  void Database::nextMetadataProvider()
163  {
164  if (m_metadataConfigurations.empty()) {
165  B2FATAL("Conditions data: No more metadata providers available");
166  }
167  auto provider = m_metadataConfigurations.back();
168  m_metadataConfigurations.pop_back();
169  bool remote{false};
170  if (auto pos = provider.find("://"); pos != std::string::npos) {
171  // found a protocol: if file remove, otherwise keep as is and set as remote ...
172  auto protocol = provider.substr(0, pos);
173  boost::algorithm::to_lower(protocol);
174  if (protocol == "file") {
175  provider = provider.substr(pos + 3);
176  } else if (protocol == "http" or protocol == "https") {
177  remote = true;
178  } else {
179  B2FATAL("Conditions data: Unknown metadata protocol, only supported protocols for payload metadata are file, http, https"
180  << LogVar("protocol", protocol));
181  }
182  }
183  try {
184  if (remote) {
185  m_metadataProvider = std::make_unique<Conditions::CentralMetadataProvider>(provider, m_usableTagStates);
186  } else {
187  m_metadataProvider = std::make_unique<Conditions::LocalMetadataProvider>(provider, m_usableTagStates);
188  }
189  } catch (std::exception& e) {
190  B2WARNING("Conditions data: Metadata provider not usable, trying next one ..."
191  << LogVar("provider", provider) << LogVar("error", e.what()));
192  return nextMetadataProvider();
193  }
194  // and check the tags are useable
195  if (!m_metadataProvider->setTags(m_globalTags)) {
196  B2FATAL("Conditions data: Problems with globaltag configuration, cannot continue");
197  }
198  }
199 
200  void Database::initialize(const EDatabaseState target)
201  {
202  if (m_configState >= target) return;
203  auto conf = Conditions::Configuration::getInstance();
204 
205  if (m_configState == c_PreInit) {
206  // first step: freeze the configuration object and determine the list of globaltags
207  // this calculates if tag replay is possible and will create an error otherwise but
208  // it will not do anything else than setting the final list of globaltags
209  conf.setInitialized(true);
210  m_globalTags = conf.getFinalListOfTags();
211  // and remove duplicates, there's no need to look in the same gt multiple times
212  std::set<std::string> seen;
213  m_globalTags.erase(std::remove_if(m_globalTags.begin(), m_globalTags.end(),
214  [&seen](const auto & tag) {
215  return not seen.insert(tag).second;
216  }), m_globalTags.end());
217  // and also obtain usable tag states and metadata providers
218  m_usableTagStates = conf.getUsableTagStates();
219  m_metadataConfigurations = conf.getMetadataProviders();
220  // reverse because we want to pop out elements when used
221  std::reverse(m_metadataConfigurations.begin(), m_metadataConfigurations.end());
222  m_configState = c_InitGlobaltagList;
223  }
224  // do we want to stop early?
225  if (m_configState >= target) return;
226  if (m_configState == c_InitGlobaltagList) {
227  // setup the first working provider;
228  if (m_metadataConfigurations.empty()) {
229  m_metadataProvider = std::make_unique<Conditions::NullMetadataProvider>();
230  } else {
231  nextMetadataProvider();
232  }
233  // we will actually use the globaltags so print them now
234  if (!m_globalTags.empty()) {
235  // Globaltags are useable so print out the final list we're gonna use
236  if (m_globalTags.size() == 1) {
237  B2INFO("Conditions data: configured globaltag is " << m_globalTags[0]);
238  } else {
239  B2INFO("Conditions data: configured globaltags (highest priority first) are " << boost::algorithm::join(m_globalTags, ", "));
240  }
241  }
242  // Configure payload location/download
243  m_payloadProvider = std::make_unique<Conditions::PayloadProvider>(
244  conf.getPayloadLocations(),
245  conf.getDownloadCacheDirectory(),
246  conf.getDownloadLockTimeout()
247  );
248  // Also we need to be able to create payloads ...
249  m_payloadCreation = std::make_unique<Conditions::TestingPayloadStorage>(conf.getNewPayloadLocation());
250  // And maaaybe we want to use testing payloads
251  m_testingPayloads.clear();
252  for (const auto& path : conf.getTestingPayloadLocations()) {
253  B2INFO("Conditions data: configured to use testing payloads" << LogVar("location", path));
254  m_testingPayloads.emplace_back(path);
255  }
256  // If so, warn again ... because
257  if (not m_testingPayloads.empty()) {
258  B2WARNING(R"(Conditions data: configured to look for temporary tesing payloads from one or more local folders.
259 
260  This will lead to non-reproducible results and is strictly only for testing purposes.
261  It is NOT ALLOWED for official productions or data analysis and any results obtained like this WILL NOT BE PUBLISHED.
262  )";);
263  }
264  m_configState = c_Ready;
265  }
266  }
267 
268 // we know LocalDatabase/ConditionsDatabase/DatabaseChain are deprecated, we don't want the warnings when compiling the service itself ...
269 #ifdef __INTEL_COMPILER
270 #pragma warning (disable:1478) //[[deprecated]]
271 #pragma warning (disable:1786) //[[deprecated("message")]]
272 #else
273 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
274 #endif
275 
276  void Database::exposePythonAPI()
277  {
278  // to avoid confusion between std::arg and boost::python::arg we want a shorthand namespace as well
279  namespace py = boost::python;
280  // make sure the default instance is created
281  Database::Instance();
282 
283  //don't show c++ signature in python doc to keep it simple
284  py::docstring_options options(true, true, false);
285 
286  py::def("reset_database", &Database::reset, (py::arg("keep_config") = false),
287  R"DOC(Reset the database setup to have no database sources
288 
289 .. deprecated:: release-04-00-00
290  Please use `basf2.conditions` for all configuration of the conditions database)DOC");
291  py::def("use_database_chain", &DatabaseChain::createInstance,
292  (py::arg("resetIoVs") = true, py::arg("loglevel") = LogConfig::c_Warning, py::arg("invertLogging") = false),
293  R"DOCSTRING(
294 Use a database chain. This function used to be necessary to enable usage of
295 multiple globaltags but has been deprecated.
296 
297 .. deprecated:: release-04-00-00
298  This function is no longer needed and is just kept for compatibility.
299  In its current state it does nothing and ignores all arguments.
300  Please use `basf2.conditions` for all configuration of the conditions database
301 )DOCSTRING");
302  py::def("use_local_database", &LocalDatabase::createInstance,
303  (py::arg("filename"), py::arg("directory")="", py::arg("readonly")=false,
304  py::arg("loglevel")=LogConfig::c_Warning, py::arg("invertLogging")=false),
305  R"DOCSTRING(
306 Use a local database backend: a single file containing the payload information in plain text.
307 
308 Parameters:
309  filename (str): filename containing the payload information, defaults to
310  "database.txt". This file needs to exist.
311  directory (str): directory containing the payloads, defaults to the directory
312  of the database filename. This parameter doesn't have any effect anymore:
313  payloads **must** be in the same directory as the text file. If this parameter
314  is not empty and doesn't point to the same directory a error is raised.
315  readonly (bool): if True the database will refuse to create new payloads.
316  This parameter doesn't have any effect anymore. Local databases are always readonly
317  loglevel (LogLevel): The severity of messages from this backend when
318  payloads cannot be found. This parameter doesn't have any effect anymore.
319  invertLogging (bool): A flag to indicate whether logging of obtained
320  payloads should be inverted. This parameter doesn't have any effect anymore.
321 
322 .. deprecated:: release-04-00-00
323  Most of the parameters don't have any effect anymore and are kept for compatibility
324  Please use `basf2.conditions` for all configuration of the conditions database.
325 )DOCSTRING");
326  {
327  //use_central_database has different signatures so the docstring confuses sphinx. Handcraft one complete docstring.
328  py::docstring_options subOptions(true, false, false);
329 
330  py::def("use_central_database", &ConditionsDatabase::createDefaultInstance,
331  (py::arg("globalTag"), py::arg("loglevel")=LogConfig::c_Warning, py::arg("payloaddir")="centraldb"));
332  py::def("use_central_database", &ConditionsDatabase::createInstance,
333  (py::arg("globalTag"), py::arg("restBaseName"), py::arg("payloaddir"), py::arg("fileBaseLocal"),
334  py::arg("loglevel")=LogConfig::c_Warning, py::arg("invertLogging")=false),
335  R"DOCSTRING(use_central_database(globalTag, restBaseName=None, payloadDir="centraldb", fileBaseLocal=None, loglevel=LogLevel.WARNING, invertLogging=False)
336 
337 Use the central database to obtain conditions data. Usually users should only
338 need to call this with one parameter which is the global tag to identify the
339 payloads.
340 
341 Parameters:
342  globalTag (str): name of the global tag to use for payload lookup
343  restBaseName (str): base URL for the REST api. This parameter is no longer supported,
344  any value other than an empty string will lead to an error.
345  fileBaseName (str): base directory to look for payloads instead of
346  downloading them. This parameter is no longer supported,
347  any value other than an empty string will lead to an error.
348  payloaddir (str): directory where to save downloaded payloads.
349  loglevel (LogLevel): The LogLevel of messages from this backend when
350  payloads cannot be found. This parameter is no longer supported and is ignored.
351  invertLogging (bool): A flag to indicate whether logging of obtained
352  payloads should be inverted. This parameter is no longer supported and is ignored.
353 
354 .. deprecated:: release-04-00-00
355  Most of the parameters don't have any effect anymore and are kept for compatibility
356  Please use `basf2.conditions` for all configuration of the conditions database.
357 )DOCSTRING");
358  }
359  // and expose our shiny configuration object
360  Conditions::Configuration::exposePythonAPI();
361  }
363 }
Belle2::Database::getData
std::pair< TObject *, IntervalOfValidity > getData(const EventMetaData &event, const std::string &name)
Request an object from the database.
Definition: Database.cc:84
Belle2::Database::c_PreInit
@ c_PreInit
Before any initialization.
Definition: Database.h:49
Belle2::DBStoreEntry::c_Object
@ c_Object
A ROOT file containing a object with the name of the DBStoreEntry.
Definition: DBStoreEntry.h:58
prepareAsicCrosstalkSimDB.e
e
aux.
Definition: prepareAsicCrosstalkSimDB.py:53
Belle2::ScopeGuard
Simple ScopeGuard to execute a function at the end of the object lifetime.
Definition: ScopeGuard.h:46
Belle2::DBStoreEntry
Class to hold one entry from the ConditionsDB in the DBStore.
Definition: DBStoreEntry.h:49
Belle2::Database::createScopedUpdateSession
ScopeGuard createScopedUpdateSession()
Make sure we have efficient http pipelinging during initialize/beginRun but don't keep session alive ...
Definition: Database.cc:74
Belle2::Database::m_payloadCreation
std::unique_ptr< Conditions::TestingPayloadStorage > m_payloadCreation
testing payload storage to create new payloads
Definition: Database.h:222
Belle2::Conditions::Downloader::ensureSession
ScopeGuard ensureSession()
Make sure there's an active session and return a ScopeGuard object that closes the session on destruc...
Definition: Downloader.h:53
Belle2::DBStore::reset
void reset(bool keepEntries=false)
Invalidate all payloads.
Definition: DBStore.cc:185
Belle2::Database::reset
static void reset(bool keepConfig=false)
Reset the database instance.
Definition: Database.cc:62
Belle2::DBStoreEntry::updatePayload
void updatePayload(unsigned int revision, const IntervalOfValidity &iov, const std::string &filename, const std::string &checksum, const EventMetaData &event)
Update the payload information for this entry and if appropriate open the new file and extract the ob...
Definition: DBStoreEntry.cc:84
Belle2::Conditions::Configuration::getInstance
static Configuration & getInstance()
Get a reference to the instance which will be used when the Database is initialized.
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::Conditions::Downloader::getDefaultInstance
static Downloader & getDefaultInstance()
Return the default instance.
Definition: Downloader.cc:143
LogVar
Class to store variables with their name which were sent to the logging service.
Definition: LogVariableStream.h:24
Belle2::DBStore::Instance
static DBStore & Instance()
Instance of a singleton DBStore.
Definition: DBStore.cc:36
Belle2::Database::m_configState
EDatabaseState m_configState
Current configuration state of the database.
Definition: Database.h:226
Belle2::DBStoreEntry::releaseObject
TObject * releaseObject()
Return the pointer to the current object and release ownership: The caller is responsible to clean up...
Definition: DBStoreEntry.h:153
Belle2::Database::m_metadataProvider
std::unique_ptr< Conditions::MetadataProvider > m_metadataProvider
Currently active metadata provider.
Definition: Database.h:218
Belle2::Database::initialize
void initialize(const EDatabaseState target=c_Ready)
Initialize the database connection settings on first use.
Definition: Database.cc:208
Belle2::Database::Instance
static Database & Instance()
Instance of a singleton Database.
Definition: Database.cc:54
Belle2::EventMetaData
Store event, run, and experiment numbers.
Definition: EventMetaData.h:43
Belle2::Database::~Database
~Database()
Hidden destructor, as it is a singleton.
Belle2::Database::DBQuery
Conditions::PayloadMetadata DBQuery
Struct for bulk read queries.
Definition: Database.h:74
Belle2::Database::Database
Database()=default
Hidden constructor, as it is a singleton.
Belle2::Database::m_testingPayloads
std::vector< Conditions::TestingPayloadStorage > m_testingPayloads
optional list of testing payload storages to look for existing payloads
Definition: Database.h:224