Life ConnectLife Connect
Wiki index
Architecture
Services
UI
Concepts
Runbooks
Infra
Swagger Docs
GitHub
Wiki index
Architecture
Services
UI
Concepts
Runbooks
Infra
Swagger Docs
GitHub
  • Runbooks

    • Run the local stack
    • Deploy a PR preview
    • Provision a new environment
Last updated 2026-10-08

Migrate Worker-stored allocation keys to the JVM entity shape

AI-generated content

This document was generated by an AI assistant. Verify accuracy before relying on the details.

Until lot 62 (gaps.md § AK-1008-1) services/utilities stored the « Ajouter une clé de répartition » request body as it came, so adb-utilities.allocationkeys holds two shapes: the JVM's AllocationKeyEntity (catalog ids, BigDecimal text, ISODates) and raw Worker bodies (catalog objects, numbers, date strings, the dialog's sub-tree and display members). The legacy rows make the JVM list of their part answer 400 (No enum constant CatalogValueCode) and the Worker's $year search answer 500. Since lot 62 the Worker reads both and writes the entity. tools/scripts/migrate-allocation-key-shape.mjs rewrites the legacy rows with lot 62's own write mapper (allocationKeyToEntity, imported, not copied) and never touches a JVM-shaped row (gaps.md § AK-1008-2). Dry-run by default, refuses a production-looking cluster, one guarded replaceOne per key, idempotent, write-ahead rollback log.

At a glance

FactValue
Scripttools/scripts/migrate-allocation-key-shape.mjs (core + tests in tools/scripts/lib / tools/scripts/test)
Runs asan engineer, from a workstation — never from a Worker or CI
Readsevery document of adb-utilities.allocationkeys, _id order, promoteValues: false (each number keeps its BSON type)
Selectsin process, by shape: a key with at least one legacy trait (below). A key with none is never written
Converts withallocationKeyToEntity of services/utilities/src/allocationKeyDto.mjs — what the Worker's POST / PUT store — under the key's own _id
Checks each rewriteno legacy trait left, _class = AllocationKeyEntity, and the API DTO (allocationKeyToDto) the same before and after
WritesreplaceOne, guarded by _id, every top-level member as read and _class absent. No insert, delete, index or transaction
Logwrite-ahead JSON Lines, --log <path> (default ./migrate-allocation-key-shape-<timestamp>.jsonl)
Rollback--rollback <log> (dry-run) then --rollback <log> --apply — restores the stored bytes, int32 included
Production guardrefused unless --i-know-this-is-prod; a host is non-production only if local or named like one
Side effectsnone known: no Atlas trigger and no services/event-dispatch route watch allocationkeys
Staging, 2026-10-08 (dry-run)44 documents, 21 JVM-shaped, 23 legacy (5 traits each), 23 to rewrite, 0 reported

Details

The legacy traits

TraitLegacy (Worker body)JVM entity
catalog-objecttotal.unit / shares[].quantity.unit = {id, code, value, label}"QUANTITY.KW_PER_HOUR"
number-amountan int32 / double amountthe BigDecimal's text, "0"
string-dateinterval.start / .end = "2026-10-05"an ISODate at UTC midnight
nested-shareseach share carries the dialog's shares sub-tree (empty on staging)absent — one rolled-up share per target
display-fieldstype discriminators, openDetails, and per share UnitName, UnitId, unitMortageNumber, ownerabsent

target.properties is a free map on both sides and is never a trait. The 23 staging rows come in five variants (the dialog's create and edit paths, with or without an owner on the units, and lot 62's own end-to-end key); tools/scripts/test/fixtures/allocation-keys-staging-shapes.mjs reproduces all 44 documents shape for shape with synthetic values.

Reported, never written: a key stamped _class that still has a trait, a key without _id, a value the log or the guard could not carry (undefined, an integer-like key, a $ or dotted key), a non-empty sub-tree (dropping it is a decision, not a migration), a unit object without a catalog id, a non-finite amount, a date that is not a day, and — defensive — a conversion whose DTO would differ.

The staging dry-run (2026-10-08, read-only)

== allocation keys — adb-utilities.allocationkeys
   documents: 44   JVM-shaped (never touched): 21   legacy: 23
   legacy traits: catalog-object: 23, number-amount: 23, string-date: 23, nested-shares: 23, display-fields: 23
   to rewrite: 23
   reported (left as is): 0
   left alone in all: 21

gaps.md § AK-1008 counted 21 legacy rows earlier the same day; two more (6ac7582b939ad742f0d76a44, 6ac75848939ad742f0d76a49) were written before lot 62 was deployed. Re-run the dry-run right before --apply: it is the count that matters.

Prerequisites

  • Lot 62 is deployed on the target (services/utilities writes the entity). Otherwise new legacy rows keep appearing; a later re-run picks them up.
  • A dry-run of the target cluster has been read.
  • A mongodump of adb-utilities.allocationkeys, taken right before --apply.
  • pnpm install at the repository root (the script needs the mongodb driver of @adb/scripts).
  • The connection string in MONGO_URI, exported in a shell that is not logged (--uri refuses credentials).

Steps — staging

export MONGO_URI='mongodb+srv://…@adb-stage.cm0co.mongodb.net/'
node tools/scripts/migrate-allocation-key-shape.mjs | tee stage-dry-run.txt          # read the header: the cluster, non-production
node tools/scripts/migrate-allocation-key-shape.mjs --verbose --limit 1              # one key before / after
mongodump --uri "$MONGO_URI" --db adb-utilities --collection allocationkeys --out "backup-$(date +%Y%m%d)"
node tools/scripts/migrate-allocation-key-shape.mjs --apply --limit 1 --log stage-allocation-keys.jsonl
node tools/scripts/migrate-allocation-key-shape.mjs --apply --log stage-allocation-keys.jsonl
node tools/scripts/migrate-allocation-key-shape.mjs                                   # verify: legacy 0, to rewrite 0

Per key: PLAN (or REPORT with its category and reason) names the key, its building and its state. Under --apply the intent (before + after) is appended to the log, then the guarded replaceOne, then its result; a key changed since it was read is SKIPPED and logged as such. Re-run the same command after an interruption: done keys are JVM-shaped and no longer selected. Then open the « Clés de répartition » of building 64c11316be873b04f9adf176 in Angular (stage): the list answers instead of 400.

Steps — production

The Worker has never written production's allocationkeys, so the dry-run there is the whole job: it must show legacy: 0.

node tools/scripts/migrate-allocation-key-shape.mjs --i-know-this-is-prod | tee prod-dry-run.txt

If it does not, stop and escalate before applying.

Rollback

node tools/scripts/migrate-allocation-key-shape.mjs --rollback stage-allocation-keys.jsonl          # what it would restore
node tools/scripts/migrate-allocation-key-shape.mjs --rollback stage-allocation-keys.jsonl --apply

Newest entry first, each restore is a replaceOne of the logged document guarded by the migrated one as a whole: a key changed since the migration is left alone (not-in-migrated-state), an entry whose own write did not land is skipped, an entry without a result line is attempted and counted uncertain. A second rollback restores nothing. Every log entry is checked before anything is restored — legacy before, JVM entity after, the same _id, the same API DTO — so a hand-edited log cannot restore another key's content; the check is by meaning, not by replaying the mapper, so a later change of lot 62's mapper does not strand a rollback. The rollback refuses a log recorded against another cluster, and the same production guard applies.

Open questions

  • Non-empty sub-trees. None exists on staging. If one appears elsewhere it is reported: the JVM keeps one rolled-up share per target, and whether the parent share's amount equals its children's sum must be looked at before the sub-tree is dropped.
  • AllocationObjectEntity.shares exists in the JVM entity but no mapper ever fills it; lot 62 and this migration follow the mapper, not the entity class.
Edit this page
Last Updated:
Contributors: Grégory Horion