Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Usage ingestion and database guards

UsageSink::ingest classifies each input as accepted, duplicate or rejected. Only accepted events affect billing, lease usage and credential activity. Duplicate detection precedes payload validation, including when a replay changes its units or funding source. A rejected input does not claim its request ID; a corrected event with that ID can still be accepted.

MemoryStore's unit domain is 0..=u64::MAX. PostgreSQL uses nonnegative BIGINT, 0..=i64::MAX. Both admit zero-unit events. PostgreSQL rejects an individual event outside its domain while retaining valid neighbors. Memory's larger range remains supported; moving workloads between backends requires values that fit the destination's existing domain.

A batch of individually representable events can still overflow an aggregate or a monotonic accounting counter. Both backends then return IngestError::Refused and apply none of the batch, including request IDs and credential activity. This is terminal for the unchanged batch: the usage writer accounts for undelivered events through its existing loss reporting instead of retrying forever. Operational errors and stored corruption remain retryable; corruption needs explicit repair before retries can succeed.

The request path, wire DTOs and accepted-event ledger equations are unchanged. PostgreSQL's oversized-event outcome changes from a failed batch to an individual rejection. Its accounting-overflow outcome becomes a permanent refusal. MemoryStore's leased-usage overflow becomes a refusal instead of a panic.

Migration and rollout

0015 installs checks for nonnegative tollgate_usage_events.units and positive fences in usage events, leases and the account's next_fence counter. Overage retains a null lease and fence; the existing all-or-nothing capability check still applies. All legitimate prior writers already use positive fences.

0015 uses NOT VALID to install guards without scanning usage history under an exclusive lock. It still needs brief exclusive locks for the catalogue changes; plan deployment around long-running transactions that could delay them. 0016 validates the guards in a separate transaction with SHARE UPDATE EXCLUSIVE, which permits ordinary row reads and writes during the scan. Validation time and I/O scale with existing table contents. These lock modes follow PostgreSQL's constraint validation rules.

Before deployment, inspect existing data without changing it:

SELECT request_id, units, fencing_token FROM tollgate_usage_events
WHERE units < 0 OR fencing_token <= 0;
SELECT lease_id, fencing_token FROM tollgate_leases WHERE fencing_token <= 0;
SELECT account_id, next_fence FROM tollgate_accounts WHERE next_fence <= 0;

Prepare migration-aware deployment and rollback binaries with both unchanged migration files. PostgresStore::connect runs SQLx migration validation before serving. A binary whose embedded catalogue ends at 0014 refuses a cold start after 0015 is applied. Already-running old binaries retain compatible SQL write shapes, but keep the old batch-failure behavior until upgraded. Prevent old binaries from restarting or scaling out during the transition.

If 0016 finds invalid history, startup fails and 0015 remains committed. Its guards continue checking new writes; the invalid historical rows are preserved. Reconcile affected rows from authoritative usage and lease evidence, preserving the audit trail, then rerun startup to validate. Do not clamp negative units, invent replacement fences, drop guards or delete migration history to make startup pass. In particular, repairing a counter requires establishing the account's allocation sequence, not merely replacing zero with one.

Rollback retains the schema and migration history and uses a rollback build that knows both migrations. An unmodified older binary is not a cold-rollback artifact. Any later schema change requires a new forward migration.

Evidence

The mirrored store suites test mixed-batch classification, duplicate precedence, zero and maximum representable units, and atomic refusal in both event orders. PostgreSQL cases additionally exercise out-of-range inputs, negative and zero stored fences, and schema write rejection. Isolated-schema migration tests upgrade populated 0014 tables, preserve leased/overage rows, reject an old startup catalogue, and exercise validation failure followed by evidence-based fixture repair without removing migration history.

The natural-number conservation proofs in formal/lean/Tollgate/Conservation.lean continue to describe accepted transitions. These changes do not add a ledger term or change those equations. The finite-width limits, rejection partition and transaction rollback are implementation obligations covered by these tests and the mutation gate; the exact model does not prove SQL locking or overflow behavior.