You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(webhook): distributed job scheduler with lease-based worker claiming (#149)
Replace the single-flight queue processor with a distributed job scheduler
(jobScheduler.ts) in which multiple workers claim due webhook jobs under
short-lived leases. Lease fencing prevents concurrent workers/replicas from
double-delivering the same job, heartbeat renewal keeps healthy long-running
deliveries from being stolen, and expired leases are reclaimed by other workers
for crash recovery (at-least-once on failure). Worker count is configurable via
WEBHOOK_WORKER_COUNT, scheduler metrics are exposed via /metrics, and
architecture/deployment/runbook docs are updated.
Closes#123
Co-authored-by: elizabetheonoja-art <elizabetheonoja@gmail.com>
Copy file name to clipboardExpand all lines: README.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -50,6 +50,7 @@ An enterprise-grade, high-performance off-chain delivery daemon for real-time So
50
50
-**Performance**: `< 100ms` P99 ingestion latency target via an asynchronous event-driven memory queue.
51
51
-**Robust Security**: Includes HMAC-SHA256 and Ed25519 signature headers, strict replay protection windowing, and thorough SSRF IP/DNS blacklisting.
52
52
-**Resiliency**: Built-in exponential backoff retry schedules with full randomized jitter to survive downstream subscriber downtimes and network drops.
53
+
-**Distributed Scheduling**: Lease-based worker claiming (`WEBHOOK_WORKER_COUNT`) prevents duplicate deliveries across concurrent workers and replicas, with heartbeat renewal and crash-recovery reclaim.
53
54
-**Operational Guides**: See [WEBHOOK_ARCHITECTURE.md](docs/WEBHOOK_ARCHITECTURE.md), [WEBHOOK_DEPLOYMENT.md](docs/WEBHOOK_DEPLOYMENT.md), and [WEBHOOK_RUNBOOK.md](docs/WEBHOOK_RUNBOOK.md).
-**Max Retries**: Defaulted to **5 attempts** before a webhook is classified as failed.
94
94
95
+
### 3.6 Distributed Job Scheduler with Lease-based Worker Claiming
96
+
97
+
Queue processing runs on a **distributed job scheduler** (`jobScheduler.ts`) where multiple worker loops compete to claim due webhook jobs under short-lived **leases**:
98
+
99
+
-**Claim protocol**: A job becomes claimable once its `runAt` time has elapsed. A worker acquires the job by claiming a lease from a shared lease registry (`LeaseStore`); while that lease is valid, no other worker can claim the same job, so concurrent replicas/workers can never double-deliver the same webhook.
100
+
-**Fencing across processes**: `LeaseStore.claim` is atomic (synchronous within the Node event loop and serialisable against a shared store such as Redis/etcd in production), which gives cross-process mutual exclusion. Each lease carries a monotonic fencing token.
101
+
-**Heartbeat / lease renewal**: while a job is executing, the owning worker renews its lease on a configurable interval, so a healthy long-running delivery is never stolen by a competing worker.
102
+
-**Crash recovery**: if a worker dies without renewing, its lease expires and another worker reclaims the job — exactly-once under normal operation, at-least-once on worker failure.
103
+
-**Retry via rescheduling**: a failed attempt with retries remaining calls `ctx.reschedule(nextAttemptTime)` (exponential backoff + jitter), returning the job to the claimable pool at a future time.
104
+
-**Horizontal scaling**: worker count is controlled by `WEBHOOK_WORKER_COUNT` (default `3`). Scaling replicas or raising the worker count increases delivery concurrency without risking duplicate deliveries.
105
+
95
106
---
96
107
97
108
## 4. Monitoring & Metrics
@@ -101,3 +112,9 @@ The service registers Prometheus counters and histograms to measure health indic
101
112
-`webhook_delivery_duration_seconds`: Histogram of endpoint response latency.
102
113
-`webhook_queue_size_current`: Gauge representing current queue occupancy.
103
114
-`webhook_failures_total`: Total dropped or exhausted delivery alerts.
115
+
-`webhook_scheduler_workers_current`: Gauge of active worker loops.
116
+
-`webhook_scheduler_active_leases_current`: Gauge of jobs currently executing under a worker lease.
117
+
-`webhook_scheduler_jobs_submitted_total`: Counter of jobs submitted to the scheduler.
118
+
-`webhook_scheduler_jobs_processed_total`: Counter of jobs executed by workers.
119
+
-`webhook_scheduler_jobs_failed_total`: Counter of jobs whose execution threw.
120
+
-`webhook_scheduler_lease_reclaimed_total`: Counter of expired leases reclaimed by another worker (crash recovery events).
Copy file name to clipboardExpand all lines: docs/WEBHOOK_DEPLOYMENT.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -57,6 +57,12 @@ Once the Green environment passes all sanity tests:
57
57
2. Monitor active connections on Blue and allow a **5-minute graceful drain window** to complete any outstanding retry attempts or delivery backlogs.
58
58
3. Shut down or idle the Blue infrastructure.
59
59
60
+
### Delivery Concurrency & Scheduler Workers
61
+
Queue processing is performed by a configurable pool of scheduler workers that claim jobs under short-lived leases, so multiple replicas can run concurrently without double-delivering webhooks:
62
+
- Set `WEBHOOK_WORKER_COUNT` (default `3`) per deployment to control in-process delivery concurrency.
63
+
- To scale out, increase the replica count of the webhook containers; each replica contributes its worker pool and lease-based claiming prevents duplicate deliveries across replicas.
64
+
- After scaling, verify `webhook_scheduler_workers_current` and `webhook_scheduler_active_leases_current` in `/metrics` and confirm `webhook_scheduler_lease_reclaimed_total` stays near zero (reclaimed leases indicate workers expiring mid-delivery and warrant investigation).
Copy file name to clipboardExpand all lines: docs/WEBHOOK_RUNBOOK.md
+20Lines changed: 20 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -79,3 +79,23 @@ To prevent breaking integrations during rotation:
79
79
To ensure the safety of the off-chain system, the SSRF (Server-Side Request Forgery) engine must be audited after any networking or DNS upgrades:
80
80
1. Verify that the URL parser correctly flags subnets by running integration tests.
81
81
2. Inspect server firewalls, ensuring egress traffic is strictly barred from routing to cloud provider private IP ranges and internal Kubernetes API service accounts.
82
+
83
+
---
84
+
85
+
## 5. Scheduler & Worker Operations
86
+
87
+
Queue processing runs on the distributed job scheduler, where workers claim jobs under short-lived leases. Diagnose scheduler health through the `webhook_scheduler_*` metrics on `/metrics` and the scheduler block on `/health`.
88
+
89
+
### Health Indicators
90
+
-`webhook_scheduler_workers_current`**0** → worker loops are not running; the scheduler cannot drain the queue. Restart the service.
91
+
-`webhook_scheduler_active_leases_current` sustained at the worker count → all workers are blocked on slow deliveries; inspect downstream endpoint latency and consider scaling out.
92
+
-`webhook_scheduler_lease_reclaimed_total` climbing → workers are expiring mid-delivery (lease not renewed). Investigate event-loop blocking / GC pauses, or increase the lease duration.
93
+
94
+
### Diagnosing duplicate or missed deliveries
95
+
1. Confirm workers are healthy: `curl -s http://webhook-service.internal/health` and check `scheduler.workers` is non-empty and `scheduler.pendingCount` is not climbing.
0 commit comments