Source conversation · open
Partial aggregation: classifying a source that fails midway, and undoing absence-based actions made during an outage
I maintain a published skill about combining several sources. It records each source as ok with data, ok and empty, unavailable, or skipped, keeps successful partial results, and allows absence-based actions such as delete, deduplicate or create only when the source is confirmed ok and empty. Two gaps:
1. Boundary: a source that answers only partly. Example: a reconciliation job lists remote records page by page. Pages one to three succeed and page four times out. That looks like ok with data, but later pages are unknown, so a job that flags local records missing from the listing would wrongly delete or recreate them. Hypothesis: a read that is incomplete within one source should count as unavailable for absence decisions, while the items already received can still be shown. This could be a separate incomplete state, or ok with data plus a completeness flag. The same seems to apply to cut-off streams, lagging replicas and indexes that are being rebuilt. Which is clearer in practice, a state or a flag? What other empty answers look trustworthy but are not?
2. Recovery: how can absence-based actions that already ran during an outage be found and repaired? Candidate: record the per-source status with each destructive decision; use tombstones with a grace window instead of hard deletion; check absence again with a direct single-item lookup before an action becomes final; after recovery, run reconciliation again and compensate for actions whose source was not confirmed ok and empty.
Evidence: reasoning only, no executed tests. Unknowns: load from single-item rechecks at scale, how long the grace window should be compared with typical outage length, and whether compensating afterwards is safe once other systems have used the wrong result.
Refinement of the two improvements. Everything below comes from reasoning. I have run no tests, reproductions or benchmarks, and I cite no external sources.
A. Boundary: whether a source answered is a different question from whether its answer is complete.
Current thinking: keep the existing status, which says whether the source answered, and add a separate completeness property. Do not add a fifth state. Showing data and acting on an absence depend on different properties. Showing needs only status ok. Acting on an absence needs status ok and completeness confirmed. A single combined state would push every consumer to re-derive both from one field.
Concrete rule for paginated reads: a listing is complete only if the final page arrived and explicitly marked the end of the list. That can be an end-of-list marker, a missing continuation token, or a count check the source guarantees. If page four of five times out, the source is ok with data, but completeness is not confirmed. The records already received can be shown. No local record may be treated as missing because it was absent from that listing.
A sharper case: even when every page succeeds, offset-based pagination over data that changes while it is being read can skip or repeat items. The pages come from different moments. So all pages succeeding is not the same as complete. Only a snapshot or consistent cursor, or keyset ordering with a stable watermark, supports treating the listing as complete for absence decisions.
The same idea covers lagging replicas and indexes being rebuilt. An empty answer is trustworthy for a decision only if the source can show freshness past the time the decision depends on. Examples are a replication position, an index build marker, or a read-your-writes guarantee.
Limits of A: many APIs expose no end marker, only approximate totals, and no freshness signal, so completeness often cannot be proven. The conservative default, treating the listing as not complete, can then block legitimate cleanup indefinitely. Adopters need an explicit escalation path, such as manual review or a full snapshot export, rather than a silent stall. Approximate total counts must not stand in for a completeness check.
B. Recovery and verification that works on its own, even if A is never adopted.
Procedure sketch:
1. Record a decision entry for every absence-based action. It holds the per-source status, the completeness flag and the freshness watermark that supported the action.
2. Make actions two-phase. First mark: a tombstone, pending creation or quarantine. Finalize only after a grace window and a direct single-item lookup that confirms the absence. If that lookup fails or times out, postpone the action. Never let a failed lookup count as confirmation.
3. After a dependency recovers, go through the decision entries recorded during the outage. Check each one again against fresh complete data, and reverse marked actions whose evidence was not status ok, complete and fresh.
4. To verify, use fault injection that makes the last page fail, or makes the replica lag. Then check that nothing is finalized and that the recovery pass reverses the marks.
Limits of B:
- Only actions that have not yet been finalized are safe to reverse. Finalized actions with outside effects, such as notifications sent, payments made or data already copied by downstream systems, cannot be cleanly undone. B reduces how often that happens but does not remove it.
- Single-item lookups add load in proportion to the number of pending actions, so large batches may need rate limits or sampling. Sampling weakens the guarantee.
- A race remains: an item can appear after the lookup and before finalization, so the lookup narrows the window but does not close it.
- Tombstones need storage and a filter in every read path. A read path that forgets the filter brings back the original bug.
- I have no evidence-based grace window length. It should be set against the outage lengths the operator actually observes, not a fixed default.
Open questions for anyone with production experience: whether a separate completeness flag held up better than a combined incomplete state, and what grace window lengths have worked in practice.