fix(reporting): reuse staged payloads across source observations - #1224
Conversation
4f70b78 to
84bd392
Compare
50c9a21 to
7a68b19
Compare
7a68b19 to
a1c0a2a
Compare
|
Rebased the four staging commits onto current main after #1218 merged; the source delta is unchanged. Current-head lint/typecheck and 48 focused staging/evidence tests pass (28 skipped). Fresh CI is running. @aao-secretariat please review the current head when checks finish. |
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — account-scoped content-addressed staging refactor with correct fail-closed filesystem publish semantics.
What I checked:
- Filesystem publish path: fsync temp → hard-link without replacing a winner → verify-before-reuse, fail-closed on corruption. Correct and exhaustively tested.
- Account isolation, legacy-ref read compatibility, and the
fix:semver signal all check out — no public-surface break, no discriminated-union change, no generated-code edits, no import-layering violations. - Diff is limited to
src/adcp/reporting/inline_source.pyplus two test files (conformance + staging-reuse), consistent with a non-breaking bug fix.
Medium findings:
src/adcp/reporting/inline_source.py—_sync_directoryfsyncs every ancestor up to filesystem root on each stage, including the hot reuse path, buying no durability while contending on shared parent inodes. Worth tightening but not blocking.
Decision path: no critical/high findings; gated_paths false; high_risk false; only one medium finding (not data-loss/schema/infra category); no author-team gate; fewer than three mediums. Falls through to row 9 → approve.
Note: review_decision is REVIEW_REQUIRED but gated_paths is false, so row 2 does not fire.
|
@aao-secretariat The Medium fsync finding is fixed on 0596246 and its thread is resolved. Please review this current head when available; fresh CI is running. |
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — durability-barrier optimization in FileSystemStagingStore, clean pass.
The delta narrows per-write directory fsync to the store's own root (walking target.parent up through self._root) with a one-time root/ancestor commit via _ensure_root_durable, guarded by a threading.Lock. The fsync chain preserves crash consistency, every failure path stays fail-closed (retained bytes, no escaped seal), and no public API / wire-shape / semver / import-layering surface is touched. New and updated tests cover the behavior.
No critical/high/medium findings. Consistent with the prior approve on the earlier head; no prior blocker to re-surface.
Decision path: no critical/high (row 1 n/a); gated_paths false (row 2 n/a); high_risk false (rows 3/5 n/a); no medium findings (rows 4/8 n/a); prior decision was approve (row 6 n/a); no no-auto-approve team match (row 7 n/a) → row 9 approve.
|
The installed PostgreSQL sdist lane hit its 1,800-second aggregate deadline after 1,443 tests, during typing, without an assertion failure. I cherry-picked the already-reviewed narrow timeout correction from #1217 to head 6f4cc7b. Local lint, typecheck, and 20 focused tests pass; fresh CI is running. @aao-secretariat, please review and post an APPROVE on this exact head once satisfied. All existing threads are resolved. |
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean pass, no findings.
Delta since the prior approve (head 0596246) is three coordinated timeout bumps: CI job timeout-minutes 40→45, step timeout-minutes 35→40, and the _production_packaging.py subprocess timeout 1800→2100s. The timeout hierarchy holds (subprocess 35m < step 40m < job 45m, ~5-min headroom per layer), so the inner run_step SIGTERM→SIGKILL evidence-capture path still fires first — fail-closed ordering preserved.
No public surface, wire shape, credential handling, or spec touched; no semver signal required. No CI gate disabled. high_risk=false, gated_paths=false, no author team gate. No blocking or medium findings.
Exact-head technical verdict —
|
| before | after | margin | |
|---|---|---|---|
aggregate test timeout (_production_packaging.py) |
1800 s (30 m) | 2100 s (35 m) | — |
step timeout-minutes |
35 m | 40 m | +5 m over aggregate |
job timeout-minutes |
40 m | 45 m | +5 m over step |
The ladder is the substantive thing to check here, and it holds: aggregate < step < job, with the 5 m / 5 m headroom preserved exactly as before. The bump is uniform, so the change can't invert the ordering or let the job deadline fire before the aggregate one — which is what would turn a slow run into an uninformative kill instead of a readable test timeout. Matches the stated cause (1,443 tests completed, typing cut off by the old deadline).
One thing worth flagging, since my earlier approval was dismissed
The staging durability code on this head is not the same implementation I approved previously — the branch was rebased and carries 05962460c ("bound staging fsync to store root") rather than the variant I'd reviewed. I re-reviewed it rather than assuming continuity. It is equivalent on the property that matters and better on two counts:
_ensure_root_durable()does the full, unconditional ancestor walk once per instance under a lock, including when the root already exists — the property that matters, since existence is not durability evidence (a peer may have created an ancestor without syncing its parent).- It's called at the top of
_write, and_writeis now an instance method — so the barrier is structurally enforced rather than being a caller contract. That closes a residual I'd raised against the earlier variant. _sync_directoryderives its stop point fromself._rootinstead of a hardcoded depth, so it stays correct if_pathever gains a level. That closes a second residual.
Nits (optional, non-blocking, not regressions — main doesn't have these either)
_writeusestarget.parent.mkdir(parents=True, exist_ok=True)while_root_syncedlatchesTrue. If the store root is removed externally after the first stage, a later stage silently recreates the chain and_sync_directorystops atself._root, so the root's own entry in its parent is never re-synced. Narrow (requires external removal mid-process); a non-recursivemkdirwould fail closed instead.__init__keepsPath(root)unresolved, so a relative root re-binds if the processchdirs; and payload opens don't useO_NOFOLLOW. Both pre-existing on main.
What I ran at this exact head
make lint— cleanpytest -k staging— 50 passed, 32 skipped (skips are PG-gated; no local PG)pytest tests/test_reporting_inline_source.py tests/test_reporting_staging_reuse.py— 50 passed
CI
35 SUCCESS / 14 running / 0 failures. base-vcs and base-sdist are already green. pg-sdist is the decisive one — it's the job the headroom change targets and it's still in flight, so treat this verdict as conditional on it landing green.
6f4cc7b to
ad5c6dc
Compare
|
@aao-secretariat #1224 is rebased on merged #1221 at ad5c6dc. The redundant timeout cherry-pick was dropped because main already has the larger PG-specific bound; staging source behavior is unchanged. Current-head make lint, make typecheck-all, and 44 focused staging/lifecycle tests pass. Fresh CI is running; please renew formal approval on this exact head when reviewed. |
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean re-review, one non-blocking medium noted inline.
This PR touches only src/adcp/reporting/inline_source.py and three test files. The core change swaps os.replace for os.link + FileExistsError/verify, correctly preventing a concurrent winner from being clobbered, with fail-closed corruption handling and thorough fault-injection tests.
Medium findings
src/adcp/reporting/inline_source.py—_ensure_root_durablefsyncs every ancestor up to/rather than just store-owned directories, raisingPermissionErroron the first stage under a traversable-but-unreadable ancestor (mode 711); contradicts the head commit's "bound fsync to store root" intent. Non-blocking.
Decision trace: no critical/high findings (row 1 n/a). gated_paths is false (row 2 n/a). Not high-risk / no deletions (rows 3, 5 n/a). The single medium is not in data-loss/schema/infra category (row 4 n/a). Prior decision was approve, not escalate (row 6 n/a). No no-auto-approve team match (row 7 n/a). Only 1 medium finding, fewer than 3 (row 8 n/a). Falls through to row 9 → approve.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — no blocking or medium findings.
This is a subsequent pass (prior outcome: approve). The delta narrows _ensure_root_durable in inline_source.py from fsyncing the root plus every ancestor up to / down to only the newly-created directories plus the first existing ancestor — a correct durability superset that avoids opening traversable-but-unreadable ancestors. Loop termination, concurrency (mkdir exist_ok), and fail-closed semantics all hold.
The only noted item is Low: the new test exercises the boundary path but, because the process owns the 0o711 ancestor, it isn't a true fail-without-fix guard. Low findings are omitted and do not block.
Decision-table walk: no critical/high (row 1 no); gated_paths false (row 2 no); high_risk false, no deletions/modifications triggers (rows 3–5 no); prior decision was approve, not escalate (row 6 no); no no-auto-approve team match (row 7 no); zero medium findings (row 8 no). Falls through to row 9 → approve.
Note: review_decision is REVIEW_REQUIRED, but gated_paths is false, so no hard gate applies.
Technical verdict —
|
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean subsequent pass, no blocking or medium findings.
This is a follow-up to a prior approve. The delta touches src/adcp/reporting/inline_source.py and its tests. The reviewer verified:
- The
inline_source.pychange adds a best-effort parentfsyncwhen the staging root pre-existed, guarded against the filesystem root and fail-open onPermissionError— a correct durability trade-off, not a trust-boundary regression. - The rewritten
test_reporting_staging_reuse.pymonkeypatchesos.opento deterministically enforce the restriction, with assertions matching the new control flow.
No credential-in-metadata concerns, no generated-code hand edits, no type-layering or discriminated-union changes, no public-surface semver signal implicated. No high-risk paths, no gated paths, no author-team gate.
Decision table: rows 1–8 do not fire (no critical/high/medium findings, gated_paths false, high_risk false, no team gate, fewer than three mediums). Falls through to row 9 → approve.
Technical verdict —
|
Repeated acquisitions with identical row bytes previously created separate staged objects because the object reference included the acquisition key. The memory and filesystem staging stores now use an account-scoped content reference, allowing observations to reuse verified payload bytes while retaining distinct manifests and seals.
Filesystem publication writes and fsyncs an owned temporary file, atomically links it without replacing a concurrent winner, and fsyncs the final directory chain. Reuse verifies retained bytes. The initial root durability check syncs only directories created for that root and the nearest existing parent, so staging also works beneath a traversable but unreadable ancestor. A fresh instance also best-effort syncs an existing root's parent to repair a root name left unsynced by another writer; permission denial on that parent does not prevent reuse. The restricted-ancestor regression enforces its own access denial even on privileged runners. Existing NDJSON and legacy opaque-reference reads remain compatible.
The installed PostgreSQL timeout correction is now provided by merged #1221 (2,400-second PG aggregate bound with 50/45-minute CI job/step limits); the redundant smaller correction was dropped during rebase. No staging behavior or test selection changed in that integration.
Validation on the current head:
make lint,make typecheck-all, commit hooks, and 76 focused staging/inline-source/lifecycle tests pass. The POSIX restricted-ancestor regression passes.make testpassed on this head (11,884 passed, 2,255 skipped, 9 deselected, 1 xfailed; 81.68% coverage). Fresh CI is running.