Skip to content

feat: bounded queue backpressure and snapshot restore verification - #309

Open
Hovibby wants to merge 1 commit into
Parcel-Protocol:mainfrom
Hovibby:feat/backpressure-snapshot-restore
Open

Hovibby wants to merge 1 commit into
Parcel-Protocol:mainfrom
Hovibby:feat/backpressure-snapshot-restore

Conversation

@Hovibby

@Hovibby Hovibby commented Oct 19, 2026

Copy link
Copy Markdown
  • Add core/workers/backpressure.ts: BoundedEnqueueGate with hard concurrency ceiling, reject/drop_oldest overflow policies, drain signalling via waitForCapacity(), and maintainer-only adjustCapacity() (admin:recovery permission). Self-healing live count via WorkerFramework.inspect(). All decisions emitted as telemetry.

  • Wire backpressure into core/workers/queue.ts: WorkerFrameworkOptions gains optional gates field; submit() checks gate capacity before idempotency/quota so a refused job never spends quota; reset() resets gate counters; submit return type extended with backpressure_full. Zero breaking changes to existing callers.

  • Add core/archive/snapshot.ts: captureSnapshot() stamps records with schemaVersion, ledgerSequence, optional contentHash and derivedIndex. verifyAndRestore() runs four phases (schema, ledger, hash, invariants) then index rebuild + diff, then commit via caller-supplied onRestore. Throws SnapshotMismatchError on any hard violation, emits snapshot.restore_failed telemetry and writes a projection.replayed denied audit event before throwing. preflight() wraps as Result for pre-flight use without try/catch. No existing API changed.

  • Add tests: backpressure.test.ts (35 tests — normal, reject overflow, drop_oldest eviction, all-running fallback, retry slot, auth, capacity adjust, waitForCapacity, self-healing, constructor validation, stats); snapshot.test.ts (45 tests — capture, invariant failure, schema up/down, ledger regression, hash mismatch, index drift, dryRun, preflight, rollback sequence, telemetry, audit trail).

  • Add docs/BACKPRESSURE.md and docs/SNAPSHOT_RESTORE.md; extend docs/WORKERS.md and docs/DISASTER_RECOVERY.md with ops edge-case tables, rollback patterns and recovery steps.

Summary

Describe what changed and why. One pull request must implement exactly one
issue and one independent feature slice.

Scope proof

Issue: Closes #

Feature directory: features/<slug>/

Changed file count:

git diff --name-only origin/main...HEAD | wc -l
  • This pull request changes at least 20 meaningful files (the scaffold normally creates 23)
  • Every product change is inside the feature directory above
  • This work does not require another open issue or unmerged pull request
  • No generated registry file or shared navigation/route list was edited

How to Test

Describe the exact happy path, one validation failure, one transport failure,
and the network(s) a reviewer should use.

  1. Go to ...
  2. Enter ...
  3. See ...

Evidence

Add screenshots for idle, loading, success and error states. Include the result
of the slice-specific contract check:

npm run verify:features -- <slug>

Checklist

  • npm run check passes locally
  • Logic returns Result<T, Code> for every expected failure
  • Every error code has actionable copy in copy.ts
  • Idle, loading, success and error states are implemented
  • Network requests are tested through MSW, not internal-module mocks
  • Accessibility tests cover at least two states with zero WCAG 2.1 A/AA violations
  • Amount arithmetic uses strings/BigInt, never floating point
  • No secret key is accepted, rendered, stored, logged or transmitted
  • README.md records the feature's non-obvious technical decision
  • If this changes docs/FEATURE_CONTRACT.md, it adds or supersedes an ADR in docs/adr/

Follow-Up

Call out known limitations only. Do not make this pull request depend on future
work; anything required by the acceptance criteria belongs in this slice.

closes #239
closes #228
closes #245
closes #230

- Add core/workers/backpressure.ts: BoundedEnqueueGate with hard
  concurrency ceiling, reject/drop_oldest overflow policies, drain
  signalling via waitForCapacity(), and maintainer-only adjustCapacity()
  (admin:recovery permission). Self-healing live count via
  WorkerFramework.inspect(). All decisions emitted as telemetry.

- Wire backpressure into core/workers/queue.ts: WorkerFrameworkOptions
  gains optional gates field; submit() checks gate capacity before
  idempotency/quota so a refused job never spends quota; reset() resets
  gate counters; submit return type extended with backpressure_full.
  Zero breaking changes to existing callers.

- Add core/archive/snapshot.ts: captureSnapshot() stamps records with
  schemaVersion, ledgerSequence, optional contentHash and derivedIndex.
  verifyAndRestore() runs four phases (schema, ledger, hash, invariants)
  then index rebuild + diff, then commit via caller-supplied onRestore.
  Throws SnapshotMismatchError on any hard violation, emits
  snapshot.restore_failed telemetry and writes a projection.replayed
  denied audit event before throwing. preflight() wraps as Result for
  pre-flight use without try/catch. No existing API changed.

- Add tests: backpressure.test.ts (35 tests — normal, reject overflow,
  drop_oldest eviction, all-running fallback, retry slot, auth, capacity
  adjust, waitForCapacity, self-healing, constructor validation, stats);
  snapshot.test.ts (45 tests — capture, invariant failure, schema up/down,
  ledger regression, hash mismatch, index drift, dryRun, preflight,
  rollback sequence, telemetry, audit trail).

- Add docs/BACKPRESSURE.md and docs/SNAPSHOT_RESTORE.md; extend
  docs/WORKERS.md and docs/DISASTER_RECOVERY.md with ops edge-case
  tables, rollback patterns and recovery steps.
@drips-wave

drips-wave Bot commented Oct 19, 2026

Copy link
Copy Markdown

@Hovibby Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant