fix(site-call-audit): composite keyset cursor eliminates the single-timestamp reconciliation pin; pinned state now a published event
Reconciliation cursor becomes composite (UpdatedAtUtc, TrackedOperationId); IPullSiteCallsClient/GrpcPullSiteCallsClient forward the Task-15 after_id keyset (additive param, null preserves the legacy inclusive >= contract). A burst sharing one exact UpdatedAtUtc now drains via the id tiebreak instead of pinning forever. A legacy site that ignores after_id is latched + published as SiteCallReconciliationPinnedChanged on the EventStream (transition-only), replacing the prior silent log line.
This commit is contained in:
@@ -278,6 +278,14 @@ non-draining (e.g., telemetry actor wedged), central issues a
|
||||
are flipped to `ForwardState = 'Reconciled'` site-side. Same self-healing
|
||||
pattern as Site Call Audit's reconciliation of `SiteCalls`.
|
||||
|
||||
> **Cursor keyset (tracked follow-up).** The `PullAuditEvents` cursor is still a
|
||||
> single `sinceUtc` timestamp. Site Call Audit's pull now uses a composite
|
||||
> `(UpdatedAtUtc, TrackedOperationId)` keyset to avoid a single-timestamp pin
|
||||
> (see Component-SiteCallAudit.md → Reconciliation). The same keyset should be
|
||||
> applied here, but it is lower urgency because the audit cursor already re-pulls
|
||||
> idempotently on `EventId` — a saturated single-timestamp window re-inserts
|
||||
> harmless no-ops rather than losing rows.
|
||||
|
||||
### Central direct-write (central-originated events)
|
||||
|
||||
Events originating at central never touch site SQLite. Inbound API writes one
|
||||
|
||||
@@ -98,6 +98,21 @@ reconnect — pulls "all tracking rows changed since cursor X" from each site.
|
||||
Gaps left by lost telemetry self-heal. Central converges to the site; the site
|
||||
never depends on central.
|
||||
|
||||
The per-site cursor is a **composite `(UpdatedAtUtc, TrackedOperationId)`
|
||||
keyset**, not a single timestamp. Each pull asks for rows strictly greater than
|
||||
the cursor pair and advances it to the maximum row seen; a burst of more rows
|
||||
than one batch all sharing one exact `UpdatedAtUtc` therefore drains via the
|
||||
`TrackedOperationId` tiebreak instead of pinning the timestamp forever. The
|
||||
`after_id` keyset field is additive on the pull contract — a first pull (or a
|
||||
**legacy** site that predates it) sends no `after_id` and keeps the inclusive
|
||||
`>=` timestamp behaviour. When such a legacy site keeps reporting
|
||||
`MoreAvailable` yet the composite cursor cannot advance, the actor latches the
|
||||
site as *pinned* and publishes `SiteCallReconciliationPinnedChanged(siteId,
|
||||
Pinned)` on the EventStream (transition-only, mirroring
|
||||
`SiteAuditTelemetryStalledChanged`) — the un-drainable tail is a
|
||||
health-observable condition rather than a silent log line, and the latch clears
|
||||
with `Pinned=false` once a later tick makes progress.
|
||||
|
||||
## Retry / Discard Relay
|
||||
|
||||
Parked cached calls live in the owning site's S&F buffer. Operator Retry/Discard
|
||||
|
||||
Reference in New Issue
Block a user