ForgeRateLimit is a production-grade rate limiting and queueing system extension built for ForgeScript, providing enterprise-level token bucket rate limiting across multiple scopes with advanced features like circuit breakers, surge guards, and weighted fair queuing.
β¨ Multi-Scope Token Buckets - Global, Guild, User, Flow, and Action-Type buckets
π Weighted Fair Queuing - Fair scheduling across flows with priority classes
β‘ Circuit Breakers - Auto-pause flows with high error rates
π‘οΈ Surge Guards - Automatic policy degradation during overload
π Real-time Events - Comprehensive event system for monitoring
π― ETA Calculation - Multi-bucket ETA reasoning with queue wait estimates
βοΈ Concurrency Control - Per-guild and per-flow concurrency limits
π Priority Classes - Moderation > System > Messaging > Heavy
npm install github:xloxn69/ForgeRateLimitconst { ForgeClient } = require("@tryforge/forgescript");
const { ForgeRateLimit } = require("forge-ratelimit");
const rateLimit = new ForgeRateLimit({
events: ["tokenReserved", "throttled", "circuitBreaker"] // Optional events to load
});
const client = new ForgeClient({
extensions: [rateLimit],
events: ["ready", "messageCreate", "tokenReserved", "throttled", "circuitBreaker"], // Include rate limit events
intents: ["Guilds", "GuildMessages", "MessageContent"],
prefixes: ["!"]
});ForgeRateLimit emits events that you can listen to in your bot. Events are triggered automatically when rate limiting occurs.
- Include events in extension options:
const rateLimit = new ForgeRateLimit({
events: ["tokenReserved", "throttled", "queued", "circuitBreaker", "surgeGuard"]
});- Add event names to ForgeClient events array:
const client = new ForgeClient({
extensions: [rateLimit],
events: ["ready", "messageCreate", "tokenReserved", "throttled", "queued", "circuitBreaker", "surgeGuard"],
// ... other options
});- Create event handler files in your bot's
events/directory:
events/tokenReserved.js:
module.exports = {
name: "tokenReserved",
code: `$log[ποΈ Tokens reserved: $eventData[tokensReserved] for flow $eventData[flowId]]`
};events/throttled.js:
module.exports = {
name: "throttled",
code: `$log[β οΈ Throttled: $throttleReason - ETA: $eventData[eta]s]
$sendMessage[CHANNEL_ID;Rate limit hit! Reason: $throttleReason]`
};events/circuitBreaker.js:
module.exports = {
name: "circuitBreaker",
code: `$log[π₯ Circuit breaker $eventData[action] for flow $eventData[flowId]]
$sendMessage[CHANNEL_ID;β‘ Flow $eventData[flowId] circuit breaker activated: $eventData[reason]]`
};Estimates the token cost for a list of actions.
Parameters:
actions(String) - Comma-separated list of action types
Returns: Number (total estimated cost)
Example:
$estimateCost[send_message,role_edit]
$c[Returns: 3 - send_message (1) + role_edit (2) = 3 tokens]
Reserves tokens from rate limiting buckets.
Parameters:
guildId(String) - Guild IDuserId(String) - User IDflowId(String) - Flow IDcost(Number) - Token costactionTypes(String, optional) - Action types
Returns: Boolean (true if reservation successful)
Environment Variables Set:
$env[rateLimitSuccess]- "true" or "false"$env[rateLimitReserved]- Number of tokens reserved$env[rateLimitReason]- Reason if failed$env[rateLimitEta]- ETA in seconds until tokens available
Example:
$reserveTokens[$guildId;$authorId;test-flow;3;send_message]
$if[$env[rateLimitSuccess]==true]
Reserved: $env[rateLimitReserved] tokens
$else
Blocked: $env[rateLimitReason] (ETA: $env[rateLimitEta]s)
$endif
Refunds tokens to buckets after operation completion.
Parameters:
guildId(String) - Guild IDuserId(String) - User IDflowId(String) - Flow IDamount(Number) - Amount to refund
Returns: Boolean (true if successful)
Example:
$refundTokens[$guildId;$authorId;test-flow;1]
$c[Returns: true - Refunded 1 token to all relevant buckets]
Gets information about a rate limiting bucket.
Parameters:
scope(String) - Bucket scope: "global", "guild", "user", "flow"id(String, optional) - Bucket ID (format depends on scope)
Returns: Boolean (true if bucket exists)
Environment Variables Set:
$env[bucketExists]- "true" or "false"$env[bucketTokens]- Current tokens$env[bucketCapacity]- Maximum capacity$env[bucketFillPercentage]- Fill percentage (0-100)
Example:
$getBucketInfo[global]
$if[$env[bucketExists]==true]
Global: $env[bucketTokens]/$env[bucketCapacity] ($env[bucketFillPercentage]%)
$endif
Adds a job to the rate limiting queue with priority support.
Parameters:
guildId(String) - Guild IDjobData(String) - Job data to queuepriority(Number, optional) - Priority class (1=moderation, 2=system, 3=messaging, 4=heavy)flowId(String, optional) - Flow ID for fairness schedulinguserId(String, optional) - User ID
Returns: Boolean (true if successfully queued)
Environment Variables Set:
$env[jobId]- Generated job ID$env[queuePosition]- Position in queue$env[queueSize]- Total queue size
Example:
$addToQueue[$guildId;{"action":"sendMessage","data":"Hello"};1;moderation-flow;$authorId]
$c[Added moderation job with highest priority (1)]
Queue position: $env[queuePosition]
Processes queued jobs using weighted fair queuing by flow.
Parameters:
guildId(String) - Guild ID to process queue for
Returns: Boolean (true if job was processed)
Environment Variables Set:
$env[processedJobId]- ID of processed job$env[processedFlow]- Flow that was selected$env[waitTime]- How long job waited in queue (ms)
Marks a job as finished and removes it from running jobs.
Parameters:
guildId(String) - Guild IDjobId(String) - Job ID to finishsuccess(Boolean, optional) - Whether job completed successfully
Returns: Boolean (true if successful)
Environment Variables Set:
$env[executionTime]- Job execution time (ms)$env[jobSuccess]- Whether job succeeded
Gets comprehensive rate limiting statistics.
Returns: String (JSON statistics)
Example:
$getRateLimitStats[]
$c[Returns: {"totalBuckets":5,"globalTokens":950,"globalCapacity":1000,"activeBuckets":4}]
Opens a circuit breaker for a specific flow to prevent execution.
Parameters:
guildId(String) - Guild IDflowId(String) - Flow ID to breakreason(String, optional) - Reason for opening circuit breaker
Returns: Boolean (true if successful)
Checks if a circuit breaker is open for a specific flow.
Parameters:
guildId(String) - Guild IDflowId(String) - Flow ID to check
Returns: Boolean (true if circuit breaker is open)
Environment Variables Set:
$env[circuitBreakerState]- "open", "closed", or "halfOpen"$env[circuitBreakerReason]- Reason for circuit breaker state$env[circuitBreakerOpenedAt]- Timestamp when opened
Calculates ETA across multiple rate limiting buckets.
Parameters:
guildId(String) - Guild IDuserId(String) - User IDflowId(String) - Flow IDcost(Number) - Token cost required
Returns: Number (ETA in seconds)
Environment Variables Set:
$env[totalETA]- Total ETA across all buckets$env[limitingBucket]- Which bucket is limiting (global/guild/user/flow)$env[limitingReason]- Detailed reason for limitation$env[queueETA]- ETA due to queue wait$env[bucketETAs]- JSON array of all bucket ETAs
Checks if concurrency limits allow execution.
Parameters:
guildId(String) - Guild IDflowId(String, optional) - Flow ID for per-flow limits
Returns: Boolean (true if execution allowed)
Environment Variables Set:
$env[concurrencyBlocked]- "true" or "false"$env[blockReason]- "guild_concurrency" or "flow_concurrency"$env[currentRuns]- Current running jobs count$env[maxRuns]- Maximum allowed concurrent runs$env[availableSlots]- Available concurrency slots
Starts a new run and tracks it in concurrency system.
Parameters:
guildId(String) - Guild IDuserId(String) - User IDflowId(String) - Flow IDcost(Number) - Estimated token cost
Returns: String (run ID if successful, empty if failed)
Environment Variables Set:
$env[runId]- Generated run ID$env[runStarted]- Timestamp when run started$env[activeConcurrency]- Current active runs count
Sets priority for an action type.
Parameters:
actionType(String) - Type of action to prioritize
Returns: Number (priority class: 1-4)
Environment Variables Set:
$env[actionPriority]- Priority number (1=highest, 4=lowest)$env[priorityClass]- Priority class name
Checks and manages surge guard system for queue wait times.
Parameters:
guildId(String, optional) - Guild ID to check (empty for global)
Returns: Boolean (true if surge guard is active)
Environment Variables Set:
$env[surgeGuardActive]- "true" or "false"$env[queueWaitP95]- 95th percentile queue wait time (ms)$env[surgeThreshold]- Surge guard activation threshold
Returns the event data for rate limiting events.
Parameters:
property(String, optional) - Specific property to retrieve
Returns: String (JSON data or specific property value)
Returns the reason for throttling in throttled events.
Returns: String (throttle reason)
Returns the timestamp of the rate limiting event.
Returns: Number (timestamp)
## Rate Limiting Policies
ForgeRateLimit uses a **Balanced** policy by default with these limits:
### Token Buckets
- **Global**: 1000 capacity, 10 tokens/sec refill
- **Guild**: 150 capacity, 2.5 tokens/sec refill
- **User**: 30 capacity, 0.5 tokens/sec refill
- **Flow**: 80 capacity, 1.33 tokens/sec refill
### Action Costs
- `send_message`: 1 token
- `send_embed`: 2 tokens
- `role_edit`: 2 tokens
- `timeout`: 3 tokens
- `kick_ban`: 4 tokens
- `create_delete`: 5 tokens
- `http_request`: 3 tokens
### Concurrency Limits
- Max 12 concurrent runs per guild
- Max 4 concurrent runs per flow
- Queue threshold: 15 seconds
- Max queue size: 200 jobs
### Priority Classes
1. **Moderation** (Priority 1) - timeout, kick_ban, role_edit
2. **System** (Priority 2) - create_delete, db_write, db_read
3. **Messaging** (Priority 3) - send_message, send_embed
4. **Heavy** (Priority 4) - http_request
## Complete Events Reference
ForgeRateLimit provides **9 event types** that trigger automatically during rate limiting operations:
### Available Events
#### `tokenReserved`
Triggered when tokens are successfully reserved from buckets.
- **Event Data**: `tokensReserved`, `flowId`, `guildId`, `userId`, `actionType`, `remainingTokens`
- **When**: After successful `$reserveTokens` call
#### `tokenRefunded`
Triggered when tokens are refunded back to buckets.
- **Event Data**: `tokensRefunded`, `flowId`, `guildId`, `userId`, `actionType`, `newTokenCount`
- **When**: After `$refundTokens` call or automatic refund
#### `bucketRefilled`
Triggered when token buckets are automatically refilled.
- **Event Data**: `bucketKey`, `tokensAdded`, `currentTokens`, `capacity`
- **When**: During automatic bucket refill process (every second)
#### `throttled`
Triggered when a request is rate limited and blocked.
- **Event Data**: `flowId`, `guildId`, `userId`, `actionType`, `retryAfter`, `eta`
- **When**: When `$reserveTokens` fails due to insufficient tokens
- **Special**: Use `$throttleReason` to get detailed reason
#### `queued`
Triggered when a request is added to the rate limiting queue.
- **Event Data**: `flowId`, `guildId`, `userId`, `actionType`, `queuePosition`, `eta`, `priority`
- **When**: After successful `$addToQueue` call
#### `queueExecuted`
Triggered when a queued request starts execution.
- **Event Data**: `flowId`, `guildId`, `userId`, `actionType`, `waitTime`, `result`
- **When**: After `$processQueue` selects and executes a job
#### `circuitBreaker`
Triggered when circuit breaker state changes for a flow.
- **Event Data**: `flowId`, `guildId`, `state`, `reason`, `duration`, `action`
- **When**: Circuit breaker opens, closes, or goes half-open
#### `surgeGuard`
Triggered when surge protection activates or deactivates.
- **Event Data**: `guildId`, `p95WaitTime`, `threshold`, `queueSize`, `state`
- **When**: System detects high queue wait times
#### `policyChanged`
Triggered when rate limiting policy is updated.
- **Event Data**: `oldPolicy`, `newPolicy`, `changedBy`, `guildId`
- **When**: Rate limiting policy configuration changes
### Event Handler Examples
**Complete monitoring setup:**
```javascript
// events/tokenReserved.js
module.exports = {
name: "tokenReserved",
code: `$log[β
Reserved $eventData[tokensReserved] tokens for $eventData[actionType] in flow $eventData[flowId]]`
};
// events/throttled.js
module.exports = {
name: "throttled",
code: `$log[π΄ THROTTLED: $throttleReason - Retry in $eventData[retryAfter]ms]
$sendMessage[ADMIN_CHANNEL;β οΈ **Rate Limited**
Flow: \`$eventData[flowId]\`
Reason: $throttleReason
ETA: $eventData[eta]s]`
};
// events/circuitBreaker.js
module.exports = {
name: "circuitBreaker",
code: `$log[β‘ Circuit breaker $eventData[action]: $eventData[flowId] ($eventData[reason])]
$if[$eventData[action]==opened]
$sendMessage[ADMIN_CHANNEL;π¨ **Circuit Breaker Opened**
Flow: \`$eventData[flowId]\`
Reason: $eventData[reason]
Duration: $eventData[duration]ms]
$endif`
};
// events/surgeGuard.js
module.exports = {
name: "surgeGuard",
code: `$log[π Surge guard active - P95: $eventData[p95WaitTime]ms]
$if[$eventData[state]==activated]
$sendMessage[ADMIN_CHANNEL;π **Surge Protection Activated**
Guild: $eventData[guildId]
Queue wait P95: $eventData[p95WaitTime]ms
Threshold: $eventData[threshold]ms]
$endif`
};
$c[Check if we can send a message]
$reserveTokens[$guildId;$authorId;welcome-flow;1;send_message]
$if[$env[rateLimitSuccess]==true]
$sendMessage[Welcome! You have been rate limited successfully.]
$else
$sendMessage[Rate limited! Reason: $env[rateLimitReason]. Try again in $env[rateLimitEta] seconds.]
$endif
$c[Reserve tokens for complex operation]
$reserveTokens[$guildId;$authorId;mod-flow;7;timeout,send_embed]
$if[$env[rateLimitSuccess]==true]
$c[Do the work - if it fails, we can refund]
$try[
$timeout[$mentioned[1];60000;Automated timeout]
$sendEmbed[Timeout applied successfully]
]
$catch[
$c[Operation failed, refund tokens]
$refundTokens[$guildId;$authorId;mod-flow;7]
$sendMessage[Operation failed, tokens refunded]
]
$else
$sendMessage[Cannot perform action: $env[rateLimitReason]]
$endif
$c[Add high-priority moderation job]
$addToQueue[$guildId;{"action":"timeout","user":"123456789"};1;moderation-flow;$authorId]
$c[Process next job from queue]
$processQueue[$guildId]
$if[$env[processedJobId]]
Processing job: $env[processedJobId] from flow: $env[processedFlow]
Wait time: $env[waitTime]ms
$endif[]
$c[Check if flow is circuit broken]
$checkCircuitBreaker[$guildId;problematic-flow]
$if[$env[circuitBreakerState]==open]
$sendMessage[β οΈ Flow is circuit broken: $env[circuitBreakerReason]]
$else[]
$c[Flow is healthy, proceed with operation]
$reserveTokens[$guildId;$authorId;problematic-flow;5]
$endif[]
$c[Calculate ETA for expensive operation]
$calculateETA[$guildId;$authorId;heavy-flow;15]
$if[$env[totalETA]>0]
$sendMessage[β Rate limited!
ETA: $env[totalETA]s
Limiting bucket: $env[limitingBucket]
Reason: $env[limitingReason]]
$else[]
$sendMessage[β
Ready to proceed immediately]
$endif[]
$c[Check if we can start new automation]
$checkConcurrency[$guildId;automation-flow]
$if[$env[concurrencyBlocked]==true]
$sendMessage[π« Cannot start: $env[blockReason]
Current runs: $env[currentRuns]/$env[maxRuns]]
$else[]
$c[Start the automation]
$startRun[$guildId;$authorId;automation-flow;10]
$sendMessage[π Started automation with ID: $env[runId]]
$endif[]
$c[Check system load]
$checkSurgeGuard[$guildId]
$if[$env[surgeGuardActive]==true]
$sendMessage[β οΈ System under load!
Queue wait P95: $env[queueWaitP95]ms
Threshold: $env[surgeThreshold]ms]
$endif[]
ForgeRateLimit provides 21 functions across 7 categories:
$estimateCost- Estimate token costs for actions$reserveTokens- Reserve tokens from buckets$refundTokens- Refund tokens after completion$getBucketInfo- Get bucket status and capacity$addToQueue- Add jobs to priority queue$getRateLimitStats- Get comprehensive statistics
$processQueue- Process jobs with fair scheduling$finishJob- Complete jobs and update circuit breakers
$openCircuitBreaker- Manually open circuit breaker$checkCircuitBreaker- Check circuit breaker state
$calculateETA- Multi-bucket ETA calculation$checkConcurrency- Concurrency limit checking$startRun- Start tracked runs$checkSurgeGuard- Surge protection system
$setPriority- Set action priority classes$checkSurgeGuard- Monitor system load
$eventData- Access event data in handlers$throttleReason- Get throttling reasons$eventTimestamp- Get event timestamps
tokenReserved,tokenRefunded,bucketRefilledthrottled,queued,queueExecutedcircuitBreaker,surgeGuard,policyChanged
ForgeRateLimit implements a multi-scope token bucket system with:
ποΈ 5 Bucket Scopes - Global β Guild β User β Flow β Action-Type
βοΈ Weighted Fair Queuing - Prevents flow starvation
π Circuit Breakers - Auto-pause problematic flows
π‘οΈ Surge Guards - Graceful degradation under load
π Real-time Events - Comprehensive monitoring
π― Priority Classes - Critical operations first
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see LICENSE file for details.
ForgeRateLimit - Production-grade rate limiting for ForgeScript
Made with β€οΈ by xloxn69