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
Debounce and Cooldown admission control dependencies (#355)
## Summary
Reworked the Debounce and Cooldown semantics from #322 after realizing
what we originally called "Debounce" was really a cooldown, and we were
missing true debounce (wait for things to settle, then fire).
**Cooldown** (formerly Debounce) — execute first, drop duplicates within
a window. Sets a Redis key on entry with TTL (`SET NX PX`). Blocked
tasks are silently dropped.
**Debounce** (new) — wait for submissions to settle, then fire once.
Uses a Lua script with two Redis keys (winner + last_seen). The first
submission becomes the "winner" and gets rescheduled for the settle
window. Subsequent submissions reset the timer and are dropped. Once the
settle window passes with no new activity, the winner proceeds.
Also adds a `retry_delay` field to `AdmissionBlocked` so Debounce can
tell the worker exactly how long to wait before rescheduling (the
remaining settle time), rather than using the fixed default delay.
The old success-anchored Cooldown (check on entry, set on successful
exit) is removed — you can get the same effect with Cooldown + Retry.
Closes#322, closes#161.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@@ -305,18 +305,18 @@ For more details on progress monitoring patterns and real-time observation, see
305
305
306
306
## Concurrency Control
307
307
308
-
Docket provides fine-grained concurrency control that allows you to limit the number of concurrent tasks based on specific argument values. This is essential for protecting shared resources, preventing overwhelming external services, and managing database connections.
308
+
Docket provides fine-grained concurrency control that limits how many tasks can run at the same time, based on specific argument values. This is useful for protecting shared resources, preventing overwhelming external services, and managing database connections.
309
309
310
-
### Basic Concurrency Limits
310
+
### Per-Argument Concurrency
311
311
312
-
Use `ConcurrencyLimit` to restrict concurrent execution based on task arguments:
312
+
Annotate a parameter with `ConcurrencyLimit` to limit concurrency based on its value. Each distinct value gets its own independent limit:
Prior to 0.18, `ConcurrencyLimit` required passing the argument name as a
419
+
string: `ConcurrencyLimit("customer_id", max_concurrent=1)`. This style
420
+
still works but `Annotated` is preferred — it avoids the string-name
421
+
duplication and is consistent with Debounce, Cooldown, and other
422
+
dependencies.
423
+
424
+
## Cooldown
425
+
426
+
Cooldown executes the first submission immediately, then drops duplicates within a window. If another submission arrives before the window expires, it's quietly dropped with an INFO-level log.
1.**Choose appropriate argument names**: Use arguments that represent the resource you want to protect (database name, customer ID, API endpoint).
471
+
Debounce waits for submissions to settle before firing. When rapid-fire events arrive, only one task runs — after a quiet period equal to the settle window. This is the classic "trailing-edge" debounce: keep resetting the timer on each new event, then fire once things calm down.
4.**Monitor blocked tasks**: Tasks that can't start due to concurrency limits are automatically rescheduled with small delays.
513
+
### Debounce vs. Cooldown
514
+
515
+
|| Cooldown | Debounce |
516
+
|---|---|---|
517
+
|**Behavior**| Execute first, drop duplicates | Wait for quiet, then execute |
518
+
|**Window anchored to**| First execution | Last submission |
519
+
|**Good for**| Deduplicating rapid-fire events | Batching bursts into one action |
520
+
521
+
### Multiple Cooldowns
522
+
523
+
You can annotate multiple parameters with `Cooldown` on the same task. Each gets its own independent window scoped to that parameter's value. A task must pass *all* of its cooldown checks to start — if any one blocks, the task is dropped:
0 commit comments