diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index 935262e7c0508..b43706cc56d3e 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -84,8 +84,13 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& /*ctx*/) // device's options. Here we just read the final value — honouring any further // runtime override supplied via CLI or JSON config. std::unordered_map ccdbUrls; + std::unordered_map runDependent; for (auto& input : dec.analysisCCDBInputs) { for (auto& m : input.metadata) { + if (m.name.starts_with("ccdb-run-dependent:")) { + runDependent.emplace(m.name, m.defaultValue.asString()); + continue; + } if (!m.name.starts_with("ccdb:") || ccdbUrls.count(m.name)) { continue; } @@ -120,6 +125,8 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& /*ctx*/) auto fieldMetadata = std::make_shared(); auto it = ccdbUrls.find(m.name); fieldMetadata->Append("url", it != ccdbUrls.end() ? it->second : m.defaultValue.asString()); + auto runDep = runDependent.find("ccdb-run-dependent:" + m.name.substr(strlen("ccdb:"))); + fieldMetadata->Append("runDependent", runDep != runDependent.end() ? runDep->second : "0"); auto columnName = m.name.substr(strlen("ccdb:")); fields.emplace_back(std::make_shared(columnName, soa::asArrowDataType(), false, fieldMetadata)); } @@ -280,12 +287,22 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& /*ctx*/) for (auto& field : schema->fields()) { auto const& url = pathTables[i][fi++].resolve(uniformityKey, field->name()); // Time to actually populate the blob + // A run-dependent object is queried with the run number rather than by + // timestamp alone. The run comes from the uniformity value, so the column's + // table has to be uniform in the run number for this to mean anything. + int const fieldRunDependent = field->metadata()->Contains("runDependent") + ? std::stoi(*field->metadata()->Get("runDependent")) + : 0; + if (fieldRunDependent != 0 && uniformityColumnName != "fRunNumber") { + LOGP(fatal, R"(Column "{}" of {} is declared run-dependent, but its table is uniform in "{}" rather than fRunNumber, so no run number is available to query with. Declare the table with DECLARE_SOA_UNIFORM_TABLE(..., aod::BCs, o2::aod::bc::RunNumber, ...).)", + field->name(), outBinding, uniformityColumnName); + } ops.push_back({ .spec = spec, .url = url, .timestamp = timestamp, - .runNumber = 1, - .runDependent = 0, + .runNumber = fieldRunDependent != 0 ? static_cast(uniformityKey) : 1, + .runDependent = fieldRunDependent, .queryRate = 0, }); } diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 2d880648c42f4..4f1ef6bdeda57 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -2370,10 +2370,15 @@ consteval static std::string_view namespace_prefix() }; \ [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, _Name_::hash, o2::framework::expressions::selectArrowType<_Type_>() } -#define DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, _Label_, _Getter_, _ConcreteType_, _CCDBQuery_, ...) \ +#define DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, _Label_, _Getter_, _ConcreteType_, _CCDBQuery_, _RunDependent_, ...) \ struct _Name_ : o2::soa::Column { \ static constexpr const char* mLabel = _Label_; \ static constexpr const char* query = _CCDBQuery_; \ + /* How the object is keyed in CCDB: 0 queries by timestamp alone, 1 additionally sends */ \ + /* the run number as "runNumber" metadata (o2::ccdb run-dependent objects), 2 uses the */ \ + /* run number in place of the timestamp. A non-zero value needs the column's table to */ \ + /* be uniform in the run number, since that is where the run comes from. */ \ + static constexpr int run_dependent = _RunDependent_; \ static constexpr const uint32_t hash = crc32(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ static constexpr bool needs_ptr_rec = true; \ /* Post-deserialisation fixup for objects which are not usable straight out of the ROOT */ \ @@ -2438,8 +2443,8 @@ consteval static std::string_view namespace_prefix() for DECLARE_SOA_CCDB_COLUMN_FULL when it needs finalising first — a FlatObject whose pointers must be rectified, say. Its finaliser is the trailing argument, so commas in a lambda body are absorbed by __VA_ARGS__. */ -#define DECLARE_SOA_CCDB_COLUMN(_Name_, _Getter_, _ConcreteType_, _CCDBQuery_) \ - DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, "f" #_Name_, _Getter_, _ConcreteType_, _CCDBQuery_, \ +#define DECLARE_SOA_CCDB_COLUMN(_Name_, _Getter_, _ConcreteType_, _CCDBQuery_) \ + DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, "f" #_Name_, _Getter_, _ConcreteType_, _CCDBQuery_, 0, \ [](_ConcreteType_* ccdbObject) { return ccdbObject; }) #define DECLARE_SOA_COLUMN(_Name_, _Getter_, _Type_) \ @@ -3336,53 +3341,56 @@ consteval auto getIndexTargets() // The columns of this table have to be CCDB_COLUMNS so that for each timestamp, we get a row // which points to the specified CCDB objectes described by those columns. #define DECLARE_SOA_TIMESTAMPED_TABLE_FULL(_Name_, _Label_, _TimestampSource_, _TimestampColumn_, _UniformitySource_, _UniformityColumn_, _Version_, _Desc_, ...) \ - O2HASH(_Desc_ "/" #_Version_); \ - template \ - using _Name_##TimestampFrom = soa::Table, o2::aod::Hash<_Desc_ "/" #_Version_ ""_h>, O>; \ - using _Name_##Timestamp = _Name_##TimestampFrom>; \ - struct _Name_##TimestampMetadata : TableMetadata, __VA_ARGS__> { \ - template > \ - using base_table_t = _TimestampSource_##From; \ - template > \ - using extension_table_t = _Name_##TimestampFrom; \ - static constexpr const auto ccdb_urls = [](framework::pack) { \ - return std::array{Cs::query...}; \ - }(framework::pack<__VA_ARGS__>{}); \ - static constexpr const auto ccdb_bindings = [](framework::pack) { \ - return std::array{Cs::mLabel...}; \ - }(framework::pack<__VA_ARGS__>{}); \ - /* The uniformity column may live in a table other than the timestamp source (the run */ \ - /* number is on aod::BCs, the timestamp on aod::Timestamps). Both are handed to the */ \ - /* fetcher, which reads them positionally — sound because the two are row-aligned. */ \ - /* Row alignment cannot be checked here: ASoA encodes no type-level relation between */ \ - /* two tables that happen to have equal row counts (aod::BCs and aod::Timestamps have */ \ - /* disjoint originals). The CCDB fetcher verifies the lengths match before reading. */ \ - static constexpr auto N = o2::soa::mergeOriginals<_TimestampSource_, _UniformitySource_>().size(); \ - template > \ - static consteval auto generateSources() \ - { \ - return o2::soa::mergeOriginals<_TimestampSource_##From, _UniformitySource_##From>(); \ - } \ - static constexpr auto timestamp_column_label = _TimestampColumn_::mLabel; \ - /* Rows sharing a uniformity value resolve to the same CCDB object, so the fetcher */ \ - /* need only query once per distinct value. Defaults to the timestamp column, i.e. */ \ - /* every distinct timestamp may yield a different object — the pre-existing behaviour.*/ \ - static constexpr auto uniformity_column_label = _UniformityColumn_::mLabel; \ - /*static constexpr auto timestampColumn = _TimestampColumn_;*/ \ - }; \ - template <> \ - struct MetadataTrait> { \ - static constexpr void isMetadataTrait() {}; \ - using metadata = _Name_##TimestampMetadata; \ - }; \ - template \ - using _Name_##From = o2::soa::Join<_TimestampSource_, _Name_##TimestampFrom>; \ - using _Name_ = _Name_##From \ + using _Name_##TimestampFrom = soa::Table, o2::aod::Hash<_Desc_ "/" #_Version_ ""_h>, O>; \ + using _Name_##Timestamp = _Name_##TimestampFrom>; \ + struct _Name_##TimestampMetadata : TableMetadata, __VA_ARGS__> { \ + template > \ + using base_table_t = _TimestampSource_##From; \ + template > \ + using extension_table_t = _Name_##TimestampFrom; \ + static constexpr const auto ccdb_urls = [](framework::pack) { \ + return std::array{Cs::query...}; \ + }(framework::pack<__VA_ARGS__>{}); \ + static constexpr const auto ccdb_bindings = [](framework::pack) { \ + return std::array{Cs::mLabel...}; \ + }(framework::pack<__VA_ARGS__>{}); \ + static constexpr const auto ccdb_run_dependent = [](framework::pack) { \ + return std::array{Cs::run_dependent...}; \ + }(framework::pack<__VA_ARGS__>{}); \ + /* The uniformity column may live in a table other than the timestamp source (the run */ \ + /* number is on aod::BCs, the timestamp on aod::Timestamps). Both are handed to the */ \ + /* fetcher, which reads them positionally — sound because the two are row-aligned. */ \ + /* Row alignment cannot be checked here: ASoA encodes no type-level relation between */ \ + /* two tables that happen to have equal row counts (aod::BCs and aod::Timestamps have */ \ + /* disjoint originals). The CCDB fetcher verifies the lengths match before reading. */ \ + static constexpr auto N = o2::soa::mergeOriginals<_TimestampSource_, _UniformitySource_>().size(); \ + template > \ + static consteval auto generateSources() \ + { \ + return o2::soa::mergeOriginals<_TimestampSource_##From, _UniformitySource_##From>(); \ + } \ + static constexpr auto timestamp_column_label = _TimestampColumn_::mLabel; \ + /* Rows sharing a uniformity value resolve to the same CCDB object, so the fetcher */ \ + /* need only query once per distinct value. Defaults to the timestamp column, i.e. */ \ + /* every distinct timestamp may yield a different object — the pre-existing behaviour.*/ \ + static constexpr auto uniformity_column_label = _UniformityColumn_::mLabel; \ + /*static constexpr auto timestampColumn = _TimestampColumn_;*/ \ + }; \ + template <> \ + struct MetadataTrait> { \ + static constexpr void isMetadataTrait() {}; \ + using metadata = _Name_##TimestampMetadata; \ + }; \ + template \ + using _Name_##From = o2::soa::Join<_TimestampSource_, _Name_##TimestampFrom>; \ + using _Name_ = _Name_##From>; /* Uniformity defaults to the timestamp column of the timestamp source: each distinct diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index 6e046e4de0311..6071e8291e387 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -266,6 +266,12 @@ inline constexpr auto getCCDBUrls() framework::VariantType::String, T::ccdb_urls[i], {"\"\""}}); + // How this object is keyed in CCDB; the fetcher turns a non-zero value into a + // run-number-qualified query rather than a plain timestamp one. + result.push_back({std::string{"ccdb-run-dependent:"} + std::string{T::ccdb_bindings[i]}, + framework::VariantType::Int, + T::ccdb_run_dependent[i], + {"\"\""}}); } return result; }