Repository navigation
Chunk Parent Field Backfill
This page rolls the chunk parent fields into an environment that predates them, and repairs chunks whose copy drifted. What the fields are: Chunk-Parent-Fields.
- Roll out: the
chunksindex lackstypeId,milestoneId,projectPhaseId,documentAuthorTypeIdorparentFieldsVersion. Follow steps 0 to 5. - Field added:
CHUNK_PARENT_FIELDS_VERSIONwas bumped. Follow steps 1 to 4. - Repair: the nightly reconcile reports
parentFieldsPending=N, or a message sits inchunk-restamp-poison. Run the repairs at the end.
Cosmos is private-endpoint-only and keyless, so a live run executes on the devbox (demi-devbox-<env>) through demi-run; see the root README.md "Running anything against the database". A live run also deletes index rows, so it needs the search grant as well as the Cosmos one.
Steps 1 and 2 are one command, run before the app that writes the fields is deployed:
scripts/demi-devbox.sh apply --env <env> --only chunks --datasources demi-chunks-ds --no-wait
apply pulls and writes whatever the devbox checkout holds. It PUTs demi-chunks-ds first behind its own prompt, then dry-runs and asks again. Answering N at the second prompt leaves the data source rewritten with no index PUT and no reset, so run apply again to finish.
-
Finish the List-id backfill. Run
src/scripts/backfill-document-list-ids.js --liveto completion and check that its summary readsplanned: 0.- The chunk backfill copies what the document row holds. A row seeded before that script holds only the List label, with its four ids null.
- A chunk backfill started first stamps nulls, reports success and checkpoints those partitions.
- The List-id script re-stamps the chunks of every document whose ids it moves, so step 3 then mostly confirms.
-
Index PUT.
apply-search-definitions.js --live --only chunksadds the four copied fields andparentFieldsVersionfromazure/search/indexes/chunks.json. It widens the live index in place; existing chunks readnullon the five fields until backfilled. -
Data source PUT and indexer reset.
put-search-datasources.jswritesdemi-chunks-dsso itsSELECTprojects the four columns andparentFieldsVersion. Thenreset-and-run-indexers.js chunks-indexerresets the indexer; the data-source script resets nothing.--datasourcesforces the reset. Without it,applyresets only a data source its dry run reports as DIFFERS. -
Backfill. Run
npm run db:backfill-chunk-parent-fields, a dry run by default; add--liveto write. It stamps the four fields andparentFieldsVersiononto every chunk whose version is belowCHUNK_PARENT_FIELDS_VERSION. -
Indexer catch-up. No manual run is needed. The backfill's Cosmos PATCH moves each chunk's
_ts, so thePT5Mschedule ofchunks-indexerre-pulls every patched row within one cycle. -
Queue the ongoing re-stamp. Set
chunkRestampQueuein the environment's.bicepparam(azure/main.test.bicepparam,azure/main.prod.bicepparam) and deploy the infrastructure.- The deploy creates the queue, its
-poisonpair and theCHUNK_RESTAMP_QUEUEapp setting. - Deploy infrastructure before the code. With the setting present and no queue behind it, the send fails and the re-stamp is lost with only a log line.
- The deploy creates the queue, its
- The data-source PUT must land before the backfill. Otherwise the indexer re-pulls the patched rows under the old
SELECT, which has no such column, and its_tshigh-water mark advances past them. The values then sit in Cosmos with no way into the index except a reset over the full chunk corpus, and every step in between answered 200. - Both PUTs land before the app deploy. On test, the merge to
mainis the staging deploy, and the PR gate "Search Schema Change" wantsSearch-Schema: applied testin the PR body. - So on test, put the devbox checkout on the PR branch before the merge, run
apply, and put the checkout back onmainafter it. The commands are inazure/search/README.md. - On prod, the deploy's schema probe compares the live index with the tag's definitions. Run
apply --env prodafter the release merge and before theazure-deploy-prod.yamldispatch, while the devboxmainhas nothing past the tag underazure/search/. - After the app deploy,
scripts/demi-devbox.sh watch --env <env> --datasources demi-chunks-dswaits for the indexer run. It gives up with a warning afterINDEXER_TIMEOUT_LONG(80 minutes), so rerun it until the run ends. - Code deployed before the
chunkRestampQueuesetting does not patch inline. The write logs one warning, flags the document rowparentFieldsPending: trueand skips the patch, so a misconfigured environment cannot hold up an eagle-api push. The--pendingrepair below clears that window. - Do not set
CHUNK_RESTAMP_INLINE=1on a deployed app. It restores the inline walk forfunc starton a box with no storage account.
- Chunks written between the PUT and the app deploy come from the old app and miss the fields in Cosmos. Step 3 stamps them, because their version is below the current one.
- If the app went out first, the chunks it wrote before the PUT are right in Cosmos and null in the index. The reset re-pulls every row and repairs them.
- A data-source PUT without the reset leaves those chunks null. The ordinary backfill skips a chunk whose Cosmos copy already matches its document, so no
_tsmoves. Run step 3 once with--force, which re-patches every chunk.
node src/scripts/backfill-chunk-parent-fields.js [--live | --dry-run]
[--project <id> | --pending] [--force] [--concurrency N] [--max-attempts N]
[--state ./backfill-chunk-parent-fields.state.json]
| Flag | Effect |
|---|---|
--live |
Writes. Without it, or with --dry-run, the run is a dry run that counts and writes nothing. --live with --dry-run is an error. |
--project <id> |
Walks one project: the manual repair the poison-queue alert names. It ignores the state file. |
--pending |
Walks only documents flagged parentFieldsPending. It ignores the state file and cannot combine with --project. |
--force |
Re-patches every chunk, even one that already agrees with its document. |
--concurrency N |
Parallel partitions, 1 to 8, default 2. |
--max-attempts N |
Chunk-patch retry budget, 1 to 20, default 12. A corpus walk on a serverless account stays throttled for minutes, and a batch that runs out of attempts leaves the document part-stamped. |
--state <path> |
Checkpoint file, default ./backfill-chunk-parent-fields.state.json. A corpus run resumes from it. |
- The walk covers every partition the document rows name, plus one bucket for documents whose
projectIdis JSON null. That bucket is read cross-partition, counted inexpectedand checkpointed like any other. Left out, its chunks stay unstamped, the unstamped count stays above zero and the facets stay withheld on every unscoped search. - A live run deletes index rows whose Cosmos chunk is gone, because the indexer detects no deletions and no stamp can reach such a row. Each candidate is confirmed by a Cosmos point read before deletion. A dry run counts orphans and deletes none.
- The orphan scan is capped per partition. Right after a patch the whole partition still reads as unstamped in the index, so the next pass finishes any partition that reported more unstamped rows than the run examined.
- Each finished partition is checkpointed, and the first run records the corpus total. A resume is measured against that total, not a fresh count, so documents ingested during the run cannot make a complete run report INCOMPLETE.
- A state file with no recorded total is compared against a fresh count.
- Every stamp is guarded on an instant: the document's
parentFieldsPendingAtwhere it has one, the walk's start instant otherwise. - Every live run clears the pending flag on the documents it verified, patched or already agreeing, under any of the three scopes. The clear is conditional on
parentFieldsPendingAt, so an unrelated write to the row cannot defeat it. - A document whose chunks the run could not write keeps its flag. The decision is per document.
The summary line carries these counters.
| Counter | Meaning |
|---|---|
nullProject |
Documents walked in the null-project bucket. |
skippedNewer |
Chunk operations a newer walk had already stamped; the run left them alone. |
pendingCleared |
Pending flags cleared. |
pendingConflicts |
Flags left raised because the document was flagged again during the walk. The newer write owns the flag and clears it. Not a failure. |
pendingMissed |
Flags whose row the clear could not find. |
pendingLeftRaised |
Flagged documents a --pending run never reached, because the row moved partition between the flag listing and the walk. The run names the ids. |
orphansFound, orphansPurged
|
Index rows without a Cosmos chunk, and how many were deleted. |
orphansFailed |
Orphans the service refused to delete. They stay searchable and unstamped. |
orphansSkipped |
Partitions whose orphan scan could not run, because search is unconfigured or the index has no parentFieldsVersion column. The line carries the reason. Those rows are unknown, not absent. |
A live run exits 1 on any of these:
- a rejected chunk write;
- a partition the walk never reached (
INCOMPLETE); -
pendingMissedorpendingLeftRaisedabove zero; -
orphansSkippedororphansFailedabove zero.
A dry run is not held to the orphan purge.
The --pending repair covers a lost or skipped re-stamp. A seed-nosql --live or backfill-document-list-ids --live run can raise the same flag when its own re-stamp fails, and prints the affected document ids.
npm run db:backfill-chunk-parent-fields -- --live --pending
The project repair covers a message in chunk-restamp-poison. It re-stamps one project, clears the flag on the documents it verified and needs no queue. Re-sending the message works too, because the handler re-reads the document row.
npm run db:backfill-chunk-parent-fields -- --live --project <projectId>
- Before step 1, the API reads the live index, finds no
typeIdto filter on and names the key inmeta[0].dropped.filter. It never emits a clause the service would answer with a 400. - From step 1 to the end of step 3, the filter is emitted and matches only the chunks stamped so far. The response carries
meta[0].degraded.reasons: ['chunk-parent-fields-unstamped'], so a UI can say "filter partially applied". - That count uses the caller's own ACL clause and the request's project scope. Backfilling project by project marks only the projects still behind.
- The completeness count reads
parentFieldsVersion(below the constant or absent), not null List refs, because a fully stamped document can hold four null refs. - Both signals clear on their own. The live-schema answer is cached for ten minutes, so nothing needs a restart after the index PUT.
- Environment Reality & Gotchas
- Architecture
- Seeding — filling Cosmos from Track and Eagle
- Frontend — React shell: screens, routes, sign-in gate
- Sources and Status — every source, one state
- Search & Retrieval — overview
- Search Index Reference
- Search Query Construction
- Deep Search Queries — passages, chunk filters, degraded modes
- Native List Reads — native list endpoints and mirror list reads
- Chunk Parent Fields — parent fields copied onto chunks
- Chunk Parent-Field Backfill — roll the fields out, repair
- Search Measurements
- Extraction Pipeline
- Extraction Quality — coverage, word-joining, tiling
- Identity Model — which id means what
- Attribute-Level Access — field-visibility levels, catalog
- Sync Architecture — how records get in
- Eagle Mirror Reference — mirrored kinds, refusals, drift classes
- ENGAGE Rows Reference — ENGAGE-owned comment periods, reconcile
- Public-Read Backfill — filling and repairing the mirror containers
-
Short Links —
/s/{code}redirect and/api/links -
Recent Uploads —
/api/documents/recent-uploads, eagle-public homepage panel - Reports — comment export and BCGW CSV reads
-
User Data — saved map areas, preferences,
/meroutes -
Bulk Download —
/api/bulk-downloads, zip jobs - Bulk Download: Operations — enable, poison-queue runbook
- Document Intake — upload link, malware scan, release
- Connecting an Application to DEMI
- Azure Environments
- API Gateway — APIM, Front Door edge secret, caller IP
- Document Backup — archive copy of originals, run and check
- Document Backup: Revert and Drill — put documents back from the archive
- PDF Titles — public name written into stored PDFs, run, restore
- Secret Sync — Key Vault, secret-sync Function app, required names
-
Operating DEMI in Azure — deploy, verify
- Operating the DEMI Data Plane — devbox access, probes, audit and cost reads
- Edge ban detector — hourly timer, modes, kill switch, soak and promote
-
CI Workflows — every GitHub Actions workflow, trigger, identity, roles
- CI Deploy Workflows — eagle-demi staging and production deploys
- CI Admin Frontend Workflows — eagle-demi-admin checks and deploys
- Release Process — cutting, tagging, shipping and rolling back a version
- Runbook: Search Outage — index/app schema drift, diagnose, fix, roll back
- BC Gov Landing Zone
- Business Rules
- EPIC.search Business Rules — where DEMI differs from EPIC.search
- 001: Typesense for DFL Search — superseded
- 002: Async Processing (MongoDB Queue) — superseded
- 003: Rule-Based Classification
- 004: Read ACL Authorization Model
- 005: Cosmos Full-Text Search Ruled Out — rejected
- 006: AI Summarization over BM25
- 007: Service-to-Service Credentials
- 008: Audit and Usage Analytics Store
- 009: Track Feed
- 010: Attribute-Level Access Control
- 011: Public Read Surface
- 012: Public Config Surface
- 013: Stored Project Summaries — proposed
- 014: Sync-out to Eagle
- 015: Malware scanning on intake
- Developer Brief: Digital File Library
- Developer Brief: AI Classifier Tool
- Developer Brief: EPIC System Integration
- Managing Government Information Policy 2022
- Guidelines on Documenting Government Decisions 2019
- AICT Guidelines
- Per-Document Data Format
- Per-Document Generator
- Document Types Draft
- DFL Reference Metadata
- Act and Legislation Full Text
- ORCS Filing Reference Sheet