previews / firm-migrations / unit load runtime walkthrough

Platform · Firm migrations · DM-14

Unit load runtime, step by step

A simulated run of the hierarchical unit load on a dummy firm: the sorted cursors, the loader's memory, every registry read and write, one pillar failure, and one out-of-scope matter, from flat JSONL to live v3 rows.

Status: Investigation, for reviewCompanion to the sorted-dump walk
TL;DR. The walk works. The loader holds one buffered row per open cursor, the single unit being assembled, and a small write-through cache of the registry entries the run itself wrote. Reads against the ImportedRecord tables drop to zero within a run, memory stays bounded by the largest pillar plus the firm-level cache, and the simulation below shows every read and write on 29 staged rows.

Companions: hierarchical unit load from sorted S3 dumps (Daniele's cursor mechanics, DM-14), units, buckets, and pillars (what a unit is and why), import boundary schemas (the canonical tables this run reads).

The setup

One firm, firm_9y2, created by hand before the run. The dump on S3 holds one sorted JSONL file per canonical schema from the boundary catalogue, singular names and all: matter.jsonl, matter_participant.jsonl, identity.jsonl, and so on. Roots sort by pk, children by parentFk, pk.

S3 dump · sorted JSONL, one forward cursor each

Import service · what is in memory

unit being assembled
rows held
0· peak 0 · dump total 29
run cache · externalRef → v3 id
registry lookups 0 · served by cache 0 · db reads 0
run log

Platform v3 · committed rows

What the loader holds, and why it stays flat

At any instant the import service holds exactly three things:

Nothing else. The dump can hold twenty thousand matters and the footprint does not move, because everything already inserted lives in the ImportedRecord registry inside each receiving service, written in the same transaction as the rows it describes.

The loader reads too: registry lookups and the run cache

Writes are only half the traffic. Almost every transaction also reads the registry, because every cross-unit reference resolves through it, and the simulation shows each lookup as it happens. The pattern:

Left naive, that is real load on the service databases: a 1,000-matter firm with five pillars each is on the order of ten thousand point reads, most of them fetching the same few hundred people over and over. The reads are indexed single-row lookups, so even the naive version survives, but there is no reason to pay for it. The run wrote every one of those registry entries itself, so it can simply remember them:

The bound is easy to state: cache size is the firm's referenced people, teams, and contacts, plus one matter. Even a large firm's address book is tens of thousands of entries at a few dozen bytes each, single-digit megabytes. In this simulated run the counter ends at 17 lookups, all 17 served by the cache, zero db reads.

The five amendments, read against DM-14

Daniele's walk is the right mechanism: sorted dumps, one forward cursor per file, drain on equality, resume by byte offset. The amendments are the places where the write-up and the unit model disagree, and each one is visible in the simulation.

Verdict

The mechanism holds. Cursor mechanics, the Zod annotation codegen, and byte-offset resume from DM-14 are keepable as-is. The five amendments turn the walk into the unit model's runtime rather than a table streamer, and the run cache removes the one cost the walk never priced: reading the registry it writes. Discussion on this page.