From 9343ab54a28039d540e8d7d1b665cf930be2e09e Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 7 May 2026 21:56:04 +0200 Subject: [PATCH] Add command to migrate away from BasicCCDBManager in Analysis --- .claude/commands/migrate-ccdb.md | 148 +++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 .claude/commands/migrate-ccdb.md diff --git a/.claude/commands/migrate-ccdb.md b/.claude/commands/migrate-ccdb.md new file mode 100644 index 00000000000..48d0f49c971 --- /dev/null +++ b/.claude/commands/migrate-ccdb.md @@ -0,0 +1,148 @@ +Migrate the specified file (or all files mentioned in the conversation) from `Service` to the declarative CCDB table approach. + +## Background + +The old approach uses `Service` and calls `ccdb->getForTimeStamp(path, timestamp)` at runtime. The new approach declares CCDB columns and timestamped tables using macros, so the framework fetches objects automatically and exposes them as columns on BC rows. + +**New API summary:** + +```cpp +// In namespace o2::aod (or a sub-namespace): +DECLARE_SOA_CCDB_COLUMN(StructName, getterName, ConcreteType, "CCDB/Object/Path"); + +DECLARE_SOA_TIMESTAMPED_TABLE(TableName, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "TABLEDESC", + ns::StructName, ns::OtherColumn); + +// In the task — basic usage: +using MyBCs = soa::Join; +void process(MyBCs const& bcs) { + for (auto const& bc : bcs) { + auto const& obj = bc.getterName(); // reference to cached deserialized object; treat as immutable + } +} +``` + +**Configurable CCDB paths** (`ConfigurableCCDBPath`): + +If the original task used a `Configurable` to supply the CCDB path, the path can remain user-overridable after migration using `ConfigurableCCDBPath`. This is a typed `Configurable` whose option name is automatically set to `"ccdb:" + Column::mLabel` (where `mLabel = "f" + StructName`), defaulting to the compile-time path in the column declaration. The framework reads this option name when resolving CCDB URLs, so users can still redirect the path via JSON config. + +The Configurable is purely declarative for the path-override mechanism: declaring it is sufficient — you do **not** pass `.value` to a getter or fetcher. The accessor remains `bc.getterName()`. The `.value` member is still available if the task wants to log the resolved path. + +```cpp +struct MyTask { + // Replaces: Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "..."}; + ConfigurableCCDBPath grpMagFieldPath; // option name = "ccdb:fGRPMagField" + + void process(MyBCs const& bcs) { + auto const& grpmag = bcs.iteratorAt(0).grpMagField(); // path override is honoured automatically + LOGP(info, "Using GRPMagField path: {}", grpMagFieldPath.value); + } +}; +``` + +Required headers (add if missing): ``, ``, `` +Headers to remove (if no longer needed): `` + +## What to do + +Read the target file(s) and perform the following migration. Do NOT do a complete migration if the patterns are ambiguous or out of scope — instead note what was skipped and why. + +### Step 1 — Inventory + +Find every `ccdb->getForTimeStamp(path, ts)` call (and variants like `fCCDB->getForTimeStamp`, `mCcdb->getForTimeStamp`). For each call record: +- The concrete C++ type `T` +- The CCDB path string (may be a `Configurable` variable — record the default value and the Configurable's name) +- The timestamp source (BC timestamp, computed value, etc.) +- Where the result is used + +**Deduplicate**: for the same (type, path) pair, declare only one CCDB column. Multiple call sites collapse into multiple uses of the same getter. + +### Step 2 — Identify scope + +Determine whether each fetch is: +- **Per-BC/per-collision** (called inside `process()` with a timestamp from a BC) — these can be migrated +- **Per-run** (called once when `runNumber` changes, guarded by `mRunNumber == ...`) — these can be migrated; the framework caches per unique timestamp automatically +- **Global/init-time** (called in `init()` with a fixed timestamp, not keyed to a BC) — these **cannot** be migrated to CCDB tables; leave them as-is and note this + +Skip the migration for any global/init-time fetches. Skip the whole file if all fetches are global. + +### Step 3 — Declare CCDB columns and table + +In the `o2::aod` namespace (or a private sub-namespace inside the file, before the task struct), declare: + +```cpp +namespace o2::aod +{ +namespace myccdbtask // use a short, unique snake_case name derived from the task name +{ +DECLARE_SOA_CCDB_COLUMN(StructName, getterName, fully::qualified::ConcreteType, "CCDB/Path"); //! +// one per unique (type, path) pair +} // namespace myccdbtask + +DECLARE_SOA_TIMESTAMPED_TABLE(MyTaskCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "MYTASKCCDB", //! + myccdbtask::StructName /*, ... */); +} // namespace o2::aod +``` + +Rules for naming: +- `StructName` / `getterName`: derive from the type name, e.g. `GRPMagField` / `grpMagField`, `MeanVertex` / `meanVertex` +- Table name: `CCDBObjects`, e.g. `SkimmerDalitzEECCDBObjects` +- `_Desc_` string: short ALL-CAPS string unique within the binary (≤ 16 chars to fit the AOD descriptor), e.g. `"DALZECC"`, `"TOFCALIB"` +- Namespace: lowercase snake-case derived from the task name (avoid collisions with other CCDB column namespaces in the file) +- Use the **default value** of any `Configurable` path as the compile-time path in the `DECLARE_SOA_CCDB_COLUMN` macro; if the path has no obvious default, leave a `// TODO: verify path` comment + +### Step 4 — Update the task struct + +1. **Remove** `Service ccdb;` (and any variant field name) +2. **Remove** `int mRunNumber;` (or similar run-caching variables) **only if** their sole purpose was to guard CCDB re-fetches +3. **Remove** `ccdb->setURL(...)`, `ccdb->setCaching(...)`, `ccdb->setLocalObjectValidityChecking()`, `ccdb->setCreatedNotAfter(...)`, `ccdb->setFatalWhenNull(...)` from `init()` +4. **Remove** the entire `initCCDB()`/`initMagField()` helper method if it only did CCDB fetching; otherwise remove just the CCDB lines from it +5. **Handle path Configurables** — for each `Configurable` that held a CCDB path: + - If the path was used as the sole argument to `getForTimeStamp` and the user may want to override it at runtime: **replace** it with `ConfigurableCCDBPath` (e.g. `ConfigurableCCDBPath grpMagFieldPath;`). The member name should match the getter for clarity. Keep a comment explaining what path it controls. + - If the path was never intended to be user-facing (e.g. internal fixed paths): **remove** it outright; the compile-time path in `DECLARE_SOA_CCDB_COLUMN` is sufficient. + - Always remove Configurables that were only used for CCDB manager setup and not for paths: `ccdb-url`, `ccdb-no-later-than`, `skipGRPOquery`, `d_bz_input` (if only used to bypass CCDB), etc. +6. **Remove** cached pointer member variables (e.g. `GRPMagField* grpmag = nullptr`) if they were only populated by CCDB fetches that are now replaced + +### Step 5 — Update process() signatures + +Define one alias near the top of the task or just below the table declaration: +```cpp +using MyBCs = soa::Join; +``` + +Then for each `process()` that used to call `getForTimeStamp`: + +- If `process()` already takes `aod::BCsWithTimestamps const&` directly: change it to `MyBCs const&`. +- If `process()` accesses BCs via `collision.bc_as()`: add `MyBCs const&` to the process signature (so the framework knows to provide it) and replace the `bc_as<>` type with `MyBCs`. +- If `process()` does not currently mention BCs but called `ccdb->getForTimeStamp(path, collision.bc_as<...>().timestamp())`: add `MyBCs const&` to the signature and obtain the BC via `collision.bc_as()`. +- Replace every `ccdb->getForTimeStamp(path, ts)` call with `bc.getterName()`. The returned reference is to a cached deserialized object; treat it as immutable. +- Null-pointer checks (`if (!grpmag)`) on the result become unnecessary — the framework guarantees the object is present (or the task fails early). Remove them. +- If a helper template like `initCCDB(collision)` was called per-collision, inline its remaining (non-CCDB) work or drop it. + +### Step 6 — Fix includes + +- Remove `#include ` if no other code in the file still uses `BasicCCDBManager` +- Ensure `#include ` is present (may already be included transitively) +- Keep all type headers (e.g. ``) since they are still needed for the concrete type + +### Step 7 — Final review + +After making changes: +- Check that every remaining use of `ccdb` / `fCCDB` / `mCcdb` has been handled +- Check that `mRunNumber` (or similar) is fully removed if unused +- Check that any leftover `Configurable` for a path is either replaced by `ConfigurableCCDBPath<>` or removed +- Search for stale references to removed Configurables (e.g. `grpmagPath.value` lingering in log messages — switch to `grpMagFieldPath.value`) +- If `init()` is now empty, it can be removed +- Note any patterns that were intentionally skipped + +## Important limitations — tell the user if any apply + +- **Configurable paths**: CCDB column paths are compile-time constants in the macro. Add `ConfigurableCCDBPath` to allow runtime override; its default is `Column::query` so it always agrees with the macro by construction. +- **`getRunDuration()` calls**: these use `BasicCCDBManager` statically and are unrelated to per-BC fetching — do not touch them. +- **`ctpRateFetcher` / other helpers**: out of scope. +- **Multiple tasks in one file**: tasks can share a single CCDB table declaration if they need the same objects; otherwise each task gets its own with a unique `_Desc_`. +- **Non-BC timestamps**: if the timestamp comes from something other than a BC (e.g. computed manually), the migration is non-trivial — flag it instead of forcing it. +- **Global/init-time fetches** (e.g. `efficiencyGlobal.cxx` style): not migratable — the timestamped-table mechanism requires a row in a BC-keyed table. +- **Magnetic-field side effects**: tasks that compute `d_bz` from a fetched `GRPMagField` and seed a propagator can keep that logic, just sourcing the object from `bc.grpMagField()` instead of `ccdb->getForTimeStamp(...)`. + +$ARGUMENTS