Repository navigation
Description
Problem
The Python account change feed (changes_after(account_id=...) in adcp_reporting_storage.ledger.change_feed) orders by seq and, since #1483, truncates each page before the first in-flight change. The JS SDK replaced that design in adcp-client #3289 (closes adcp-client #3283): every feed, account-scoped or deployment-wide, orders by (xid, seq) behind xid < pg_snapshot_xmin(pg_current_snapshot()), as the deployment feed already does here. Truncation depended on the account lock keeping seq in commit order and on every writer taking the same lock key; ordering by (xid, seq) needs neither. Both SDKs read the same shared adcp_reporting_changes tables, so the feed contract has to match.
Mirror (JS: adcontextprotocol/adcp-client#3289)
- Account page:
WHERE account_id = %s AND (xid, seq) > (%s::text::xid8, %s::bigint) AND xid < pg_snapshot_xmin(pg_current_snapshot()) ... ORDER BY xid, seq, replacing the fix(reporting): truncate account feed pages at the first in-flight change and match JS backoff #1483 truncation. - The account cursor carries
xid. A legacy seq-only cursor(S)is mapped to(X, S)withX = LEAST(snapshot xmin, oldest visible xid among the account's changes with seq > S), so nothing is skipped. The cost is redelivery. The page returns a new-format cursor even when empty. pruned_through_seqstays and gives exact legacy-cursor expiry (a legacy cursor expires if any change with a higherseqwas pruned). The account horizon gainspruned_through_xidandpruned_through_xid_seq.- Consumer holds,
prune_changesandretire_expired_periodscompare(xid, seq); prune victims are taken in(xid, seq)order. A consumer row with NULLcursor_xid(saved before the upgrade) holds bysequntil its next save. - Add the
(account_id, xid, seq)index idempotently (the old(account_id, seq)index stays, it serves the legacy mapping). Cursorseqandxidare range-checked so out-of-range values fail asValueError, not a database error. - Migration is idempotent and advisory-locked; the
adcp_reporting_feed_consumerscheck constraint is replaced guardedly.
Python-specific: the feed table here is the shared adcp_reporting_changes, not reporting_ledger_changes (the buyer-facing protocol feed), which this issue does not touch.
Follow-up to #1483 (replaces its page truncation). Part of the JS parity set; #1492 is intentionally not part of this.
Activity
feat(reporting-storage): order the account change feed by xid and seq