Skip to content
Sachith77Public

About

Transactional webhook delivery with retries, receiver deduplication, crash-injection experiments, and a monitoring dashboard.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Reliable Webhook Delivery — Transactional Outbox Pattern

The One-Idea Explanation:
Never let an event exist only in a place delivery can lose it.

When an order is created, the business entity and the webhook event are committed in the exact same database transaction. Delivery is delegated to an independent background worker with exponential backoff and crash recovery, while the receiver enforces deterministic deduplication. Together: $$\textbf{At-Least-Once Delivery} + \textbf{Receiver Idempotency} \Longrightarrow \textbf{Effectively Exactly-Once Side Effects}$$


1. Verified Results Summary

Every number below is empirically measured and directly reproducible via the commands listed in Section 3.

Metric Specification Target Empirically Measured Status
Crash-Injection Runs 5 consecutive runs 5 runs completed Verified
Total Orders Tested 1,250 orders (250/run) 1,250 orders Verified
Events Lost Across Suite Exactly 0 0 lost (100% delivered) Verified
Events Double-Processed Exactly 0 0 double-processed Verified
Worker Recovery Time Under 15.0s 4.49s – 7.67s (Mean: 5.26s) Verified
Interrupted In-Flight Events > 0 killed mid-batch 71 events killed & recovered Verified
Attempt Distribution Monitored Attempt 1: 1,179 | Attempt 2: 71 Verified
Normal Operation P99 Latency 5-run median 194.06 ms (range: 173.37ms – 344.90ms) Verified
Dedup Rate Under Forced Redelivery 100% duplicate suppression 50.0% suppressed (100/200) Verified

2. Architecture & Core Mechanisms

+-----------------------------------------------------------------------------------+
| 1. POST /orders                                                                  |
|    BEGIN TRANSACTION;                                                             |
|      INSERT INTO orders (id, customer_email, amount) VALUES (...);               |
|      INSERT INTO outbox (id, event_type, payload, status) VALUES (..., 'pending');|
|    COMMIT;                                                                        |
+-----------------------------------------------------------------------------------+
                                       │
                                       ▼ (PostgreSQL DB)
+-----------------------------------------------------------------------------------+
| 2. Outbox Worker (Safe Concurrency & Crash Recovery)                             |
|    - Startup: Reset stale 'delivering' rows to 'pending' (recover after SIGKILL)  |
|    - Poll: SELECT ... FOR UPDATE SKIP LOCKED (enables horizontal worker scaling)  |
|    - Backoff: min(60s, 1.0s * 2^(attempt - 1))                                    |
|    - Dead-Letter: Quarantines after 5 failed attempts                             |
+-----------------------------------------------------------------------------------+
                                       │ HTTP POST /webhook (X-Idempotency-Key: <event_id>)
                                       ▼
+-----------------------------------------------------------------------------------+
| 3. Receiver (Independent Non-Circular Deduplication)                             |
|    - ON CONFLICT (idempotency_key) DO UPDATE SET attempts_seen = attempts_seen + 1|
|    - If is_new_insert: Execute side-effect (charge card, record inventory)        |
|    - If duplicate: Suppress side-effect, log warning, return HTTP 200             |
+-----------------------------------------------------------------------------------+

Key Components

  • app/main.py: Order creation API and Next.js Dashboard API (GET /events, GET /events/{id}, POST /events/{id}/replay).
  • worker/main.py: Background poll loop, batching, backoff scheduling, and recover_stale_delivering_rows().
  • worker/delivery.py: Capped exponential backoff algorithm $[1, 2, 4, 8, 16, 32, 60, 60]$ and HTTP dispatch.
  • receiver/main.py: External webhook target with atomic upsert into receiver_processed_events.
  • dashboard/: Dark-theme Next.js dashboard for event telemetry, status filtering, and one-click replay.

3. Reproduction Commands

To reproduce every metric and verify the system from a clean clone:

Step 1: Start PostgreSQL

docker-compose up -d

Step 2: Run Unit & Integration Tests (9/9 passing)

pytest -v

Step 3: Reproduce 5-Run Crash-Injection Suite

Runs 5 rounds of 250 orders, injecting uncontrolled SIGKILL signals into active in-flight worker processes and asserting 0 lost and 0 duplicated events:

python scripts/run_stage2_suite.py

(Results written to results/stage2.json)

Step 4: Reproduce Normal-Operation Latency Distribution (5 Runs)

Executes 5 runs of 100 orders each to produce a statistically sound P99 median and min-max distribution:

python scripts/benchmark_latency.py

(Results written to results/latency_benchmark.json)

Step 5: Reproduce Receiver Deduplication Invariant (1 Run)

Delivers 100 events, forces outbox rows back to pending, redelivers all 100 events, and verifies that the independent receiver table executed 0 duplicate side effects:

python scripts/benchmark_dedup.py

(Results written to results/dedup_benchmark.json)


4. Running the Complete System & Dashboard

Terminal 1: Order & Dashboard API

uvicorn app.main:app --host 127.0.0.1 --port 8000 --reload

Terminal 2: External Webhook Receiver

uvicorn receiver.main:app --host 127.0.0.1 --port 8001 --reload

Terminal 3: Outbox Polling Worker

python worker/main.py

Terminal 4: Next.js Dashboard

cd dashboard
npm run dev

Open http://localhost:3000 in your browser to inspect the dashboard.


5. Dashboard Features

  • Dark Theme Interface: Base surfaces from #0a0a0c to #0f0f12, restrained high-contrast typography, and accent highlights.
  • Real-Time Telemetry: Live polling indicator (3s refresh) tracking Total, Delivered, Pending/Delivering, and Dead-Lettered events.
  • Status Filter Toolbar: Filter instantly by All, pending, delivering, delivered, or dead_lettered.
  • Event Inspection Drawer: Click any event row to inspect full JSON payload, attempt history, delivery timestamps, and independent receiver audit records.
  • One-Click Replay: Instantly unstick failed or dead-lettered events without touching the database via POST /events/{id}/replay.
  • Integrated Test Order Generator: Create mock orders with randomized amounts directly from the UI to observe end-to-end delivery in real time.

6. Project Documentation Directory

  • DECISIONS.md: In-depth rationale covering PostgreSQL vs SQLite, FOR UPDATE SKIP LOCKED, backoff progression, dead-letter thresholds, non-circular proofs, and failure boundaries.
  • RESULTS.md: Complete tabular benchmark results, latency distributions, and methodology explanations.

About

Transactional webhook delivery with retries, receiver deduplication, crash-injection experiments, and a monitoring dashboard.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages