docs(api): correct cooldownLimiter doc for its OTP reuse
cooldownLimiter began as the wake-only throttle; the OTP-start hardening reused it via the atomic reserve/release. Its type comment still called it a per-server wake limiter and justified the per-replica behaviour as "acceptable because the operator reconcile is idempotent" -- true for wake, false for OTP, whose every admitted send is a non-idempotent email. Rewrite the comment to describe the shared per-key limiter and record the honest KNOWN-LIMITATION: the atomic reserve/release closes the intra-replica concurrent burst, but the in-memory map throttles per replica, so cross-replica bounding still needs a shared store. No behaviour change.
This commit is contained in:
1 file changed
+12
-4
+12
-4
@@ -379,11 +379,19 @@ func principalFromContext(ctx context.Context) *Principal {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---- wake cooldown ----
|
||||
// ---- per-key cooldown (wake + OTP) ----
|
||||
|
||||
// cooldownLimiter is an in-memory per-server rate limiter for the wake lever.
|
||||
// It is process-local; with multiple api replicas the effective cooldown is
|
||||
// per-replica, which is acceptable because the operator reconcile is idempotent.
|
||||
// cooldownLimiter is an in-memory per-key cooldown. It backs two throttles with
|
||||
// separate keyspaces: the wake lever (key = server name, via allowed/record) and
|
||||
// the email-OTP start (keys = principal and recipient, via the atomic
|
||||
// reserve/release). It is process-local, so with multiple api replicas the
|
||||
// effective cooldown is per-replica. For wake that is acceptable — the operator
|
||||
// reconcile is idempotent, so a burst slipping through is harmless. For OTP it is a
|
||||
// real KNOWN-LIMITATION: each admitted send is a non-idempotent email, so across N
|
||||
// replicas a determined caller could draw up to N codes per window. The atomic
|
||||
// reserve/release pair closes the intra-replica concurrent burst (the bug fixed in
|
||||
// #35); cross-replica bounding would need a shared store (out of scope for the
|
||||
// single-replica demo).
|
||||
type cooldownLimiter struct {
|
||||
mu sync.Mutex
|
||||
now func() time.Time
|
||||
|
||||
Reference in new issue
Block a user