Rate Limit Operations¶
This guide covers troubleshooting rate limit enforcement issues and procedures for adjusting limits at runtime.
Decision Tree¶
Troubleshooting¶
Symptoms¶
- Requests succeed when they should be rate limited
RateLimitExceededraised unexpectedly- Cascade to parent entity not working
- Bucket state appears incorrect
Diagnostic Steps¶
Check entity and bucket state:
# Query entity metadata
aws dynamodb get-item --table-name <name> \
--key '{"PK": {"S": "ENTITY#<entity_id>"}, "SK": {"S": "#META"}}'
# Query bucket state for a specific resource (shard 0)
# v0.9.0+ bucket key format: PK={ns}/BUCKET#{entity}#{resource}#{shard}, SK=#STATE
aws dynamodb get-item --table-name <name> \
--key '{"PK": {"S": "{ns}/BUCKET#<entity_id>#<resource>#0"}, "SK": {"S": "#STATE"}}'
Verify stored limits:
aws dynamodb query --table-name <name> \
--key-condition-expression "PK = :pk AND begins_with(SK, :sk)" \
--expression-attribute-values '{":pk": {"S": "ENTITY#<entity_id>"}, ":sk": {"S": "#LIMIT#"}}'
Unexpected RateLimitExceeded¶
Possible causes:
| Cause | Diagnosis | Solution |
|---|---|---|
| Bucket depleted | Check bucket tokens value |
Wait for refill or increase limit |
| Limit too restrictive | Compare limit capacity vs usage | Increase capacity |
| Cascade triggered | Check violations array |
Increase parent limit |
| Clock skew | Compare server time with bucket last_update |
Sync NTP |
Check bucket state:
# v0.9.0+ bucket key format
aws dynamodb get-item --table-name <name> \
--key '{"PK": {"S": "{ns}/BUCKET#<entity_id>#<resource>#0"}, "SK": {"S": "#STATE"}}' \
--projection-expression "b_<limit_name>_tk, rf, b_<limit_name>_cp, shard_count"
Interpret the response:
tokens: Current token count (in millitokens, divide by 1000)last_update: Unix timestamp of last accesscapacity: Maximum bucket capacity (in millitokens)
Limits Not Enforcing¶
Common causes:
| Cause | Solution |
|---|---|
| Entity not created | Create entity before rate limiting: await limiter.create_entity(...) |
| No stored limits configured | Set limits via set_system_defaults(), set_resource_defaults(), or set_limits() |
| Stale bucket state | Bucket refills over time; tokens may have refilled |
| Limit configuration mismatch | Verify limit capacity and refill_rate match expectations |
Verify entity exists:
aws dynamodb get-item --table-name <name> \
--key '{"PK": {"S": "ENTITY#<entity_id>"}, "SK": {"S": "#META"}}'
Cascade Not Working¶
If cascade to parent is not enforced:
Step 1: Verify parent entity exists:
aws dynamodb get-item --table-name <name> \
--key '{"PK": {"S": "ENTITY#<parent_id>"}, "SK": {"S": "#META"}}'
Step 2: Verify child has parent_id set:
aws dynamodb get-item --table-name <name> \
--key '{"PK": {"S": "ENTITY#<child_id>"}, "SK": {"S": "#META"}}'
# Check the "parent_id" attribute in response
Step 3: Ensure entity was created with cascade=True:
# Check the entity's cascade setting in its metadata
aws dynamodb get-item --table-name <name> \
--key '{"PK": {"S": "ENTITY#<child_id>"}, "SK": {"S": "#META"}}'
# Check the "cascade" attribute in response — should be true
If cascade is not enabled, recreate the entity with cascade:
await limiter.create_entity(
entity_id="child-id",
parent_id="parent-id",
cascade=True, # Entity property — applies to all acquire() calls
)
Verification¶
Test that rate limiting is working correctly:
from zae_limiter import Repository, RateLimiter, Limit, RateLimitExceeded
repo = await Repository.open()
limiter = RateLimiter(repository=repo)
# Consume all capacity
for i in range(100):
try:
async with limiter.acquire(
entity_id="test-entity",
resource="test",
limits=[Limit.per_minute("rpm", 100)],
consume={"rpm": 1},
):
pass
except RateLimitExceeded as e:
print(f"Rate limited after {i} requests, retry_after={e.retry_after_seconds}s")
break
Procedures¶
Adjust Limits at Runtime¶
Programmatic Adjustment¶
Update stored limits for an entity without redeployment:
from zae_limiter import Repository, RateLimiter, Limit
repo = await Repository.open()
limiter = RateLimiter(repository=repo)
# Update limits for an entity
await limiter.set_limits(
entity_id="api-key-123",
resource="gpt-4", # Resource these limits apply to
limits=[
Limit.per_minute("rpm", 1000), # Requests per minute
Limit.per_minute("tpm", 100_000), # Tokens per minute
],
)
print("Limits updated successfully")
Direct DynamoDB Update¶
Advanced
Direct DynamoDB updates bypass validation. Use programmatic API when possible.
# Update a stored limit
aws dynamodb put-item --table-name <name> \
--item '{
"PK": {"S": "ENTITY#<entity_id>"},
"SK": {"S": "#LIMIT#<resource>#<limit_name>"},
"capacity": {"N": "1000000"},
"refill_amount": {"N": "1000000"},
"refill_period": {"N": "60"}
}'
Reset Bucket State¶
Reset a bucket to restore full capacity (will be recreated on next acquire):
# v0.9.0+ bucket key format: PK={ns}/BUCKET#{entity}#{resource}#{shard}, SK=#STATE
aws dynamodb delete-item --table-name <name> \
--key '{"PK": {"S": "{ns}/BUCKET#<entity_id>#<resource>#0"}, "SK": {"S": "#STATE"}}'
Debug Bucket State¶
Query all buckets for an entity using GSI3:
# Discover all bucket PKs via GSI3 (KEYS_ONLY)
aws dynamodb query --table-name <name> \
--index-name GSI3 \
--key-condition-expression "GSI3PK = :gsi3pk" \
--expression-attribute-values '{":gsi3pk": {"S": "{ns}/ENTITY#<entity_id>"}}' \
--output json
Then fetch individual bucket items using the PKs returned above.
Interpret bucket values:
All token values are stored as millitokens (multiply by 1000). Bucket attributes
use the b_{limit_name}_{field} naming convention:
| Field | Attribute | Description | Example |
|---|---|---|---|
| Tokens | b_rpm_tk |
Current available tokens × 1000 | 50000 = 50 tokens |
| Capacity | b_rpm_cp |
Maximum bucket size × 1000 | 100000 = 100 tokens |
| Refill timestamp | rf |
Last refill (epoch ms) | 1705312800000 |
| Shard count | shard_count |
Number of shards for this bucket | 2 |
Verification After Changes¶
After adjusting limits, verify:
# Check available capacity
available = await limiter.available(
entity_id="<entity_id>",
resource="<resource>",
limits=[Limit.per_minute("<limit_name>", <capacity>)],
)
print(f"Available: {available}")
# Output: {'<limit_name>': <available_tokens>}
DynamoDB Key Patterns¶
| Pattern | Key | Description |
|---|---|---|
| Entity metadata | PK={ns}/ENTITY#<id>, SK=#META |
Entity configuration |
| Bucket state (v0.9.0+) | PK={ns}/BUCKET#<id>#<resource>#<shard>, SK=#STATE |
Token bucket (per shard) |
| Entity config | PK={ns}/ENTITY#<id>, SK=#CONFIG#<resource> |
Stored limit config |
| Resource config | PK={ns}/RESOURCE#<resource>, SK=#CONFIG |
Resource default limits |
| System config | PK={ns}/SYSTEM#, SK=#CONFIG |
System default limits |
Related¶
- Recovery & Rollback - Reset corrupted buckets
- Performance Tuning - Capacity planning for rate limits
- Hierarchical Limits - Cascade configuration