ADR-104: Stored Limits as Default Behavior¶
Status: Accepted Date: 2026-01-18 Issue: #130
Context¶
Currently, acquire() requires explicit limits or opt-in via use_stored_limits=True:
async with limiter.acquire(
entity_id="user-123",
resource="gpt-4",
limits=[Limit.per_minute("tpm", 10000)], # Required
use_stored_limits=False, # Default
):
pass
This creates friction: users must pass limits on every call or remember to opt-in. With centralized config (ADR-102), stored limits become the natural default.
Decision¶
Change the default behavior: always resolve limits from stored config (System > Resource > Entity hierarchy).
Resolution order: 1. Entity config → if exists, use it 2. Resource config → if exists, use it 3. System config → fallback 4. Error if no config found anywhere
Backward compatibility:
- limits parameter accepted as override (useful for testing, migration)
- use_stored_limits=False deprecated with warning in v0.5.0, removed in v1.0
Consequences¶
Positive: - Simpler API: no limits parameter needed in common case - Centralized control: ops can change limits without code deployment - Consistent behavior: all clients use same stored config
Negative:
- Breaking change for users relying on explicit limits only
- Requires config to be set up before use (or limits override)
Alternatives Considered¶
Keep Opt-In (use_stored_limits=True)¶
Rejected: Adds friction; stored config is the better default now that hierarchy exists.
Remove limits Parameter Entirely¶
Rejected: Useful for testing and gradual migration; keep as override option.