fix(graph): exempt deleted rows from the orphan-registration sweep - #1367
Conversation
`cleanup_stale_graphs` removes a deleted graph-registry row only once it is older than `STALE_GRAPH_DAYS`. A deleted row inside that window fell through to the orphan check, which had no `status` predicate — so it was stamped `instance_missing_since` and counted in `OrphanedGraphRegistrations` as soon as the instance it named left the instance registry. That instance is always about to leave. A deleted graph's writer is released with the graph and recycled by the ASG well inside seven days, so every rolling fleet replacement that followed a deletion raised the alarm for a week and then healed itself when the rows aged out. It happened three times in the last month; the most recent, on 2026-09-02, reported eight orphans and cleared on 09-09 at the exact moment the sweep deleted the last seven rows. The reported count equalled `total rows - live rows` on every sweep of that window, and no live graph was ever involved: each user-graph row was re-pointed at its replacement writer within seconds of the swap, and the instance registry carried the whole fleet throughout. The alarm's stated meaning is "a live graph whose routing is stale and needs reconciling from on-disk truth". A row for a graph that no longer exists is not routing, the same way a shared repository's row is not routing, so it gets the same exemption — and a marker left on a row that was stamped while still live is cleared when it is deleted, rather than held until the row ages out. Three tests cover the window: a deleted row with a missing instance is not counted, its stale marker is cleared, and a live graph swept alongside it is still marked. The first reproduces the production warning line exactly and fails without the predicate. Claude-Session: https://claude.ai/code/session_01PBkpeEv3gBUzL812xqfXnS
|
Change classification: normal — platform or application code changed. A normal change carries a documented automated review on this pull request before merge, in place of a second human approver. The |
|
@claude please review this PR |
|
Claude finished @jfrench9's task in 1m 25s —— View job Review: PR #1367 — exempt deleted rows from the orphan-registration sweep
SummaryStraightforward, well-scoped fix. Read the full diff against Correctness
TestsThe three new cases in
All three reuse the existing Nits (non-blocking)
Note on verificationI could not execute Verdict: LGTM. No correctness, security, or style issues found. |
Summary
The
OrphanedGraphRegistrationsalarm has been firing on graph-registry rows for graphs that no longer exist.cleanup_stale_graphsremoves a deleted row only once it is older thanSTALE_GRAPH_DAYS; a deleted row still inside that window fell through to the orphan check, which had nostatuspredicate, and was stamped and counted as soon as the instance it named left the instance registry.That instance is always about to leave — a deleted graph's writer is released with the graph and recycled by the ASG well inside seven days — so every rolling fleet replacement that followed a deletion raised the alarm for a week and then healed itself. This scopes the orphan check to rows that are actually routing.
Changes
robosystems/operations/graph/infrastructure.pycleanup_stale_graphs: addstatus != "deleted"to theinstance_missingpredicate, alongside the existing shared-repository exemption. Both exemptions say the same thing — the row is not a routing pointer — so they sit together and are documented together.STALE_GRAPH_DAYS).instance_missing_sincemarker on the next sweep, rather than carrying it until the row ages out.tests/operations/graph/test_infrastructure.py— three cases inTestCleanupStaleGraphs, mirroring the shared-repository exemption trio that already exists:test_recently_deleted_graph_is_not_orphaned— a deleted row whose instance is absent is neither counted nor stamped, and the published metric is 0.test_deleted_graph_marker_from_before_the_exemption_is_cleared— a marker left from a sweep that predates this rule is removed.test_live_graph_is_still_orphaned_alongside_a_deleted_row— the exemption is scoped; an active graph swept at the same time is still marked and counted.Worth a close look: the first test is a negative control. Verified that it fails without the predicate, and that when it fails it emits the same warning line the production sweep did — so it reproduces the defect rather than asserting around it.
No behaviour change to routing, allocation, or any request path.
cleanup_stale_graphsruns only from the dailyinstance_registry_cleanup_joband the weeklyfull_instance_maintenance_job; what changes is which rows those sweeps count and stamp.Breaking Changes
None. No API surface is touched — no GraphQL schema, operations envelope, or REST shape changes — so there is no client-SDK impact in either tier and no regen is required.
Testing
just test-code— clean (ruff, format, basedpyright, cf-lint).uv run pytest tests/operations/graph/test_infrastructure.py— 33 passed.uv run pytest tests/operations/graph tests/dagster— 992 passed.test_recently_deleted_graph_is_not_orphanedfails and emits the production warning line, restored.Full
just test-allwas not run; the unit suite above covers the changed module and its callers, and CI runs the full gate.Certification
🤖 Generated with Claude Code
https://claude.ai/code/session_01PBkpeEv3gBUzL812xqfXnS