Repository navigation
feat(reporting-storage): deliver reporting subscriptions through the PostgreSQL production store - #1734
Conversation
…PostgreSQL production store PgReportingProductionStore now serves managed subscription delivery, as the memory store does since the previous change. Both stores keep one admitted owed set per generation, and each owed obligation's frozen constituents. - PostgreSQL keeps the enrollment (producer and frozen destination binding) and the constituents in two private tables, adcp_reporting_production_owed_sets and adcp_reporting_production_owed_constituents. The constituents are written in the obligation's transaction and never rewritten. - Planning, dispatch and the materializer context read the owed set; an owed set has no generation-wide source binding. Activation re-enrolls owed sets from their bindings. - Reconciliation records, materializer discovery and status checkpoints record the generation's offering. A destination binding's record_id derives from its full generation key, so offerings no longer share an identity: the SQL validator previously hashed only (account, consumer, id, version) and would have rejected every subscription binding. Binding lookups in the validators and the materializer scope carry the offering. - create_schema drops the account-only CHECKs on those three tables and widens the materializer discovery key to include offering_id, in place and idempotently, for schemas created by the previous release. Earlier workers refuse the upgraded schema: stop them first. Production generations keep their account-only CHECK, and the obligation trigger joins them on the offering too. - Status checkpoints are still keyed per (account, consumer, id, version), so both stores refuse a second offering of one subscription version for the same account and owner with reporting_production_offering_conflict. Under that rule the PostgreSQL production store serves owed sets with status notifications enabled. - The required-schema manifests and pinned catalog counts are regenerated. The managed-delivery tests run against both stores, with and without status notifications, and cover the second-offering refusal and the schema upgrade. Refs #1695 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — Slice 4b extends managed reporting-subscription delivery to the PostgreSQL production store.
The reviewer returned a clean approve with no blocking or Medium findings. Walking the decision table top-down:
- Rows 1 (critical/high): no findings — not triggered.
- Row 2 (gated_paths): false — not triggered.
- Rows 3–5 (high_risk deletions/modifications + medium): high_risk is false — not triggered.
- Row 6 (sticky escalate): no prior decision — not triggered.
- Row 7 (no-auto-approve team): no team match — not triggered.
- Row 8 (≥3 medium findings): zero medium findings — not triggered.
- Row 9: approve.
What was checked: the load-bearing SQL change widens the generation key 4→5 columns (offering_id) and the destination_binding identity hash conditionally appends offering_id only when non-empty, matching Python record_identity/ledger_asdict exactly — account-level hashes (offering_id='') stay byte-stable in canonical sorted-key order, so existing records are unaffected and subscription bindings stop being rejected. _check_context fail-closes on NULL producer_key; commit_subscription_obligation freezes constituents via ON CONFLICT DO NOTHING plus a read-back immutability guard; schema upgrade (DROP account-only CHECK, widen discovery PK) is guarded, idempotent and lossless. The feat(reporting-storage) semver signal is correct — additive, no public-surface break. Tests cover both stores, notifications on/off, second-offering refusal, and idempotent schema upgrade.
Summary
Slice 4b of #1695, stacked on #1733 (4a2): managed delivery of reporting subscriptions through the PostgreSQL production store. With this, the memory and PostgreSQL production stores both serve subscriptions with
bind_managed_generation, with or without status notifications.Store
PgReportingProductionStoreimplements the owed-set contract the memory store gained in feat(reporting): deliver reporting subscriptions through production managed delivery #1733:admit_subscription_generationsubscription_generation_admittedcommit_subscription_obligationproduction_owed_setsadcp_reporting_production_owed_setsholds the enrollment: producer key and frozen managed destination binding.adcp_reporting_production_owed_constituentsholds each owed obligation's frozen constituents. They are written in the obligation's transaction and never rewritten.producer_constituents) and the materializer context read the owed set. An owed set has no generation-wide source binding.The binding identity question
A destination binding's
record_idis the hash of its full generation key,offering_idincluded, in Python (record_identity). The SQL validator hashed only (account, consumer, id, version). So two offerings could never share an identity, but PostgreSQL would have rejected every subscription binding. The validator and every binding lookup now carry the offering. That covers the validator, the evidence check, the reference trigger, the materializer scope, discovery and the obligation trigger.Shared tables
delivery_pgwrites it.create_schemadrops feat(reporting): hold reporting-subscription owed sets in the PostgreSQL ledger #1729's account-onlyCHECKon those three tables and widens the discovery key to includeoffering_id. The upgrade is in place, lossless and idempotent, for schemas created between merges.docs/reporting-ledger-migration.mdsays so.CHECK.One offering per subscription version
Status checkpoints are still keyed per (account, consumer, id, version). Admission therefore refuses a second offering of one subscription version for the same account and owner, in both stores, with
reporting_production_offering_conflict. That fails the subscription's planning; nothing is written.Under that rule, the production store lifts the "status notifications refuse owed sets" check.
PgReportingLedgerStoreoutside production keeps it. Widening the status checkpoint and event keys is a follow-up.Pins
The required-schema manifests are regenerated: the materializer, outbox, status and production manifests. So are the pinned catalog counts:
Tests
test_reporting_subscription_production.pynow runs every scenario against memory and PostgreSQL, adding:test_reporting_subscription_ledger_pg.py: an account-only managed-delivery schema is upgraded idempotently.Local runs:
Failures under
-n 2were lease and lock-timing contention: production advisory locks are database-wide, so parallel workers contend. All 61 of them pass serially.Refs #1695
🤖 Generated with Claude Code