9.2 KiB
| title | summary | read_when | ||||
|---|---|---|---|---|---|---|
| Rate Limiting & Circuit Breakers | Enhanced rate limiting system with circuit breaker pattern for reliable messaging across all channels |
|
Rate Limiting & Circuit Breakers
Moltbot includes an intelligent rate limiting system that prevents rate limit errors proactively while maintaining high throughput across all messaging channels.
Overview
The rate limiting system provides:
- Sliding Window Algorithm with token bucket pattern for smooth rate limiting
- Circuit Breaker Pattern for fault tolerance and graceful degradation
- Per-channel, Per-account Isolation to prevent one channel from affecting others
- Comprehensive Metrics for monitoring and debugging
- Adaptive Backoff that learns from provider responses
How It Works
Sliding Window + Token Bucket
The system combines two proven algorithms:
- Token Bucket: Allows bursts up to a configured size, then refills tokens at a steady rate
- Sliding Window: Tracks requests over time to enforce hard limits per time window
This hybrid approach provides both burst handling (for quick replies) and sustained throughput (for long conversations).
// Example: 10 requests per second with burst of 20
{
maxRequests: 10,
windowMs: 1000,
burstSize: 20,
refillRate: 10
}
Circuit Breaker Pattern
When a channel experiences repeated failures (network errors, API errors, etc.), the circuit breaker:
- Opens after N failures → reject new requests immediately
- Half-opens after timeout → test with limited requests
- Closes after successes → resume normal operation
This prevents cascading failures and gives providers time to recover.
Configuration
Per-Channel Rate Limits
Rate limits are configured per channel in config.yaml:
channels:
discord:
accounts:
default:
rateLimit:
# Global account limit
global:
requestsPerSecond: 50
burstSize: 100
# DM-specific limits
dm:
requestsPerSecond: 5
burstSize: 10
# Guild-specific limits
guild:
requestsPerSecond: 1
burstSize: 10
circuitBreaker:
failureThreshold: 5
resetTimeoutMs: 30000
successThreshold: 2
telegram:
accounts:
default:
rateLimit:
global:
requestsPerSecond: 30
burstSize: 60
chat:
requestsPerSecond: 1
burstSize: 3
dm:
requestsPerSecond: 1
burstSize: 10
circuitBreaker:
failureThreshold: 3
resetTimeoutMs: 30000
successThreshold: 2
Global Defaults
If not configured, channels use sensible defaults based on provider documentation:
| Provider | Global RPS | Burst | Circuit Breaker |
|---|---|---|---|
| Discord | 50 | 100 | Yes (5/30s/2) |
| Telegram | 30 | 60 | Yes (3/30s/2) |
| Slack | 1 | 5 | Yes (5/30s/2) |
| 20 | 40 | Yes (3/30s/2) |
Usage
Automatic (Recommended)
Rate limiting is automatic for all channel providers. No code changes needed.
// Sends respect rate limits automatically
await sendMessageDiscord("channel:123", "Hello!");
await sendMessageTelegram("123456", "Hi there!");
Manual Integration
For custom integrations, use the rate limiter directly:
import { RateLimiter, createProviderRateLimiterOptions } from "moltbot/infra/rate-limiter";
const limiter = new RateLimiter(
createProviderRateLimiterOptions({
provider: "custom",
accountId: "my-bot",
requestsPerSecond: 10,
burstSize: 20,
})
);
// Try to acquire permission
const result = limiter.tryAcquire();
if (!result.allowed) {
console.log(`Rate limited. Retry after ${result.retryAfter}ms`);
return;
}
// Make API call
try {
await myApiCall();
limiter.recordSuccess();
} catch (err) {
limiter.recordFailure();
throw err;
}
Wait-based Approach
For less time-sensitive operations, use waitAndAcquire:
// Automatically waits for rate limit to clear (up to 30s)
const acquired = await limiter.waitAndAcquire(30_000);
if (acquired) {
await myApiCall();
} else {
throw new Error("Rate limit timeout");
}
Monitoring
CLI Status Command
Check rate limiting status across all channels:
moltbot channels status --rate-limits
Output:
Rate Limiting Status
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Discord (default)
Global: 45/50 available (circuit: closed)
Guild #123: 8/10 available (circuit: closed)
DM @user456: 4/5 available (circuit: closed)
Telegram (default)
Global: 28/30 available (circuit: closed)
Chat -100...: 0/1 available (circuit: open ⚠️)
└─ Retry in: 850ms
Overall Stats:
Total Requests: 1,247
Allowed: 1,198 (96%)
Rejected: 49 (4%)
Circuit Blocks: 12 (1%)
Avg Wait: 420ms
Programmatic Access
import { rateLimiterRegistry } from "moltbot/infra/rate-limiter";
// Get aggregated metrics
const metrics = rateLimiterRegistry.getAggregatedMetrics();
console.log(`Total limiters: ${metrics.totalLimiters}`);
console.log(`Success rate: ${metrics.metrics.allowedRequests / metrics.metrics.totalRequests}`);
// Get specific limiter
const limiter = rateLimiterRegistry.get("discord:default:global");
const state = limiter?.getState();
console.log(`Tokens: ${state.limiter.tokens}`);
console.log(`Circuit: ${state.circuit?.state}`);
Logging
Enable verbose rate limiting logs:
logging:
subsystems:
"rate-limiter": "debug"
"discord/rate-limited": "debug"
"telegram/rate-limited": "debug"
Best Practices
1. Configure Based on Your Usage
High-volume bots should tune rate limits:
channels:
discord:
accounts:
high-volume:
rateLimit:
global:
requestsPerSecond: 40 # Leave headroom
burstSize: 80
2. Monitor Circuit Breaker State
Circuit breakers opening frequently indicate:
- Provider issues (outages, degraded performance)
- Network problems
- Misconfigured rate limits (too aggressive)
3. Use DM-Specific Limits
DMs often have different limits than group chats:
channels:
telegram:
accounts:
default:
rateLimit:
dm:
requestsPerSecond: 1
burstSize: 15 # Higher burst for DMs
4. Tune Circuit Breaker Thresholds
For flaky networks:
circuitBreaker:
failureThreshold: 10 # More tolerant
resetTimeoutMs: 60000 # Longer recovery
For critical services:
circuitBreaker:
failureThreshold: 3 # Fail fast
resetTimeoutMs: 15000 # Quick recovery
Troubleshooting
"Circuit breaker is open" Errors
Cause: Too many consecutive failures
Solution:
- Check channel status:
moltbot channels status --probe - Review logs for underlying errors
- Wait for auto-recovery or manually reset
- Increase
failureThresholdif network is flaky
High Rejection Rate
Cause: Traffic exceeds configured limits
Solution:
- Increase
requestsPerSecondif provider allows - Increase
burstSizefor temporary spikes - Implement message queuing in your application
- Consider multiple bot accounts for load distribution
Metrics Show Zero Requests
Cause: Rate limiter not initialized for channel
Solution:
- Verify channel is active:
moltbot channels status - Send a test message to initialize limiter
- Check configuration syntax in
config.yaml
API Reference
RateLimiter
class RateLimiter {
constructor(options: RateLimiterOptions);
// Try to acquire permission (non-blocking)
tryAcquire(): RateLimitResult;
// Wait for permission (blocking with timeout)
waitAndAcquire(maxWaitMs?: number): Promise<boolean>;
// Record operation result for circuit breaker
recordSuccess(): void;
recordFailure(): void;
// Introspection
getState(): RateLimiterState;
getMetrics(): RateLimitMetrics;
}
rateLimiterRegistry
// Global registry of all rate limiters
const rateLimiterRegistry: {
getOrCreate(key: string, options: RateLimiterOptions): RateLimiter;
get(key: string): RateLimiter | undefined;
getAll(): Map<string, RateLimiter>;
getAggregatedMetrics(): AggregatedMetrics;
clear(): void;
}
Helper Functions
// Create standard provider rate limiter options
function createProviderRateLimiterOptions(params: {
provider: string;
accountId?: string;
requestsPerSecond: number;
burstSize?: number;
circuitBreaker?: boolean | CircuitBreakerConfig;
}): RateLimiterOptions;