Ratelimit Architecture #352
Replies: 4 comments
-
|
Without special casing RataLimit, I thought to include the Envoy ratelimiter in the policy engine as a Policy Implementation (A user can include a gRPC or REST service as a policy implementation as well). So this fits well with our Policy story. In this case, envoy/ratelimit works as a library for Policy Engine. During request processing, the Policy Engine would invoke it via an in-process function call instead of making an external gRPC request. If we are not using the Envoy rate limit filter (Envoy is not directly call the envoy/ratelimit service), there is no value in using envoy/ratelimit service. Anyway, this is going to be a library for Policy Engine if we are going as described above. If we go with our own implementation, we can use any rate limit algorithm as well. envoy/ratelimit is using the Fixed Windows algorithm. In my opinion going with a rate limit library or our own implementation is better. |
Beta Was this translation helpful? Give feedback.
-
Rate Limit Policy Implementation - v0.1.0Following up on the discussion about the rate limiting architecture, I've implemented the rate limit policy as proposed - integrating it directly into the Policy Engine rather than using the envoy/ratelimit gRPC service. Implementation OverviewThe rate limit policy ( 1. No Extra Network LatencyRate limiting logic runs in-process within the Policy Engine. There's no inter-service gRPC call to an external rate limiter - the check happens directly during request processing via the extproc filter. 2. No Additional Container RequiredThe rate limiter is compiled into the Policy Engine binary. For single-instance deployments, no Redis is needed - it uses in-memory storage. 3. Multiple Algorithm SupportUnlike Envoy's built-in Fixed Windows approach, this implementation supports:
# System configuration to select algorithm
systemParameters:
algorithm: "gcra" # or "fixed-window"4. Flexible Key ExtractionSupports multiple key components that can be combined: keyExtraction:
- type: header
key: "x-api-key"
- type: ip
- type: apiname
- type: apiversion
- type: routename
- type: metadata
key: "user-id"Multiple components are joined with 5. Multiple Concurrent LimitsSupports enforcing multiple limits simultaneously: limits:
- limit: 10
duration: "1s"
- limit: 1000
duration: "1h"All limits are evaluated, and the most restrictive one is enforced. 6. Weighted Rate Limiting (Cost Parameter)Different operations can consume different amounts of quota: # Expensive operation consumes 10 tokens
- method: POST
path: /analytics/report
requestPolicies:
- name: ratelimit
params:
cost: 10
limits:
- limit: 100
duration: "1h"
# Simple query consumes 1 token (default)
- method: GET
path: /users/{id}
requestPolicies:
- name: ratelimit
params:
limits:
- limit: 100
duration: "1h"7. Dual Backend Support
Redis backend features:
systemParameters:
backend: "redis"
redis:
host: "redis.example.com"
port: 6379
failureMode: "open" # Allow requests if Redis is unavailable8. Comprehensive Rate Limit HeadersSupports multiple header standards:
Example UsageBasic Rate Limiting (per route)operations:
- method: GET
path: /api/users
requestPolicies:
- name: ratelimit
params:
limits:
- limit: 100
duration: "1m"Per-User Rate Limiting with API Keyoperations:
- method: GET
path: /api/users
requestPolicies:
- name: ratelimit
params:
keyExtraction:
- type: header
key: "x-api-key"
limits:
- limit: 1000
duration: "1h"IP-Based Rate Limiting with Multiple Limitsoperations:
- method: POST
path: /api/login
requestPolicies:
- name: ratelimit
params:
keyExtraction:
- type: ip
limits:
- limit: 5
duration: "1m"
burst: 10
- limit: 100
duration: "1h" |
Beta Was this translation helpful? Give feedback.
-
Go Distributed Rate Limiting LibrariesOverviewThis document provides a comprehensive comparison of open-source Go libraries that support global distributed rate limiting using Redis or other cache backends. Complete Library Comparison Table
Top Recommendations by Use Case1. Best Overall for Distributed Rate Limitingulule/limiter (2.3k ⭐, 158 forks)
2. Best for Redis-Only Solutionsgo-redis/redis_rate (970 ⭐, 102 forks)
3. Best for HTTP APIsthrottled/throttled (1.6k ⭐, 97 forks)
4. Best for Microservices Architecturemailgun/gubernator (960 ⭐, 96 forks)
5. Most Backend Optionsmennanov/limiters (604 ⭐, 59 forks)
6. Best for Single-Instance Performancejuju/ratelimit (2.9k ⭐, 317 forks)
Algorithm ComparisonGCRA (Generic Cell Rate Algorithm)
Token Bucket
Sliding Window
Leaky Bucket
Feature Matrix
Decision GuideChoose ulule/limiter if:
Choose go-redis/redis_rate if:
Choose throttled/throttled if:
Choose mailgun/gubernator if:
Choose mennanov/limiters if:
Choose juju/ratelimit if:
Maintenance and Community HealthActively Maintained (2024-2025):
Moderately Active:
Lower Activity:
Final RecommendationFor distributed rate limiting with Redis, the top 3 choices are:
For cloud-native/Kubernetes deployments without external cache:
For maximum backend flexibility:
Sources
|
Beta Was this translation helpful? Give feedback.
-
Rate Limiting AlgorithmsOverviewThis document provides an in-depth explanation of the major rate limiting algorithms used in distributed systems, particularly in Go libraries for Redis-backed rate limiting. Table of Contents
1. Token Bucket AlgorithmDescriptionThe Token Bucket algorithm maintains a bucket that holds tokens. Tokens are added to the bucket at a fixed rate up to a maximum capacity. Each request consumes one or more tokens. If tokens are available, the request is allowed; otherwise, it's denied or delayed. How It WorksVisual RepresentationCharacteristicsPros:
Cons:
Use Cases
Implementation in Go LibrariesLibraries using Token Bucket:
Example Configuration// Bucket capacity: 100 tokens
// Refill rate: 10 tokens/second
// Allows bursts of 100 requests
// Sustained rate: 10 req/s
capacity := 100
refillRate := 10 // per secondMathematical Formula2. Leaky Bucket AlgorithmDescriptionThe Leaky Bucket algorithm models a bucket with a hole at the bottom. Requests are added to the bucket, and they "leak out" at a constant rate. If the bucket overflows, excess requests are discarded. This ensures a smooth, constant output rate. How It WorksVisual RepresentationCharacteristicsPros:
Cons:
Use Cases
Implementation in Go LibrariesLibraries using Leaky Bucket:
Leaky Bucket vs Token Bucket
Mathematical Formula3. GCRA (Generic Cell Rate Algorithm)DescriptionGCRA (Generic Cell Rate Algorithm), also known as the Virtual Scheduling Algorithm, is a sophisticated rate limiting algorithm originally designed for ATM networks. It's mathematically equivalent to the leaky bucket but uses a different approach: instead of tracking bucket level, it tracks the "theoretical arrival time" (TAT) of the next allowed request. How It WorksVisual RepresentationCharacteristicsPros:
Cons:
Use Cases
Implementation in Go LibrariesLibraries using GCRA:
GCRA vs Leaky BucketWhile mathematically equivalent, they differ in implementation:
Mathematical FormulaRedis ImplementationGCRA is particularly well-suited for Redis because it requires only a single value: -- Redis Lua script for GCRA
local tat = redis.call('GET', KEYS[1])
local now = tonumber(ARGV[1])
local emission_interval = tonumber(ARGV[2])
local burst_capacity = tonumber(ARGV[3])
if tat == false then
tat = now
else
tat = tonumber(tat)
end
tat = math.max(tat, now)
local allow_at = tat - (burst_capacity * emission_interval)
if now >= allow_at then
local new_tat = tat + emission_interval
redis.call('SET', KEYS[1], new_tat)
return 1 -- allowed
else
return 0 -- denied
end4. Fixed Window CounterDescriptionThe Fixed Window Counter divides time into fixed windows (e.g., 1-minute intervals) and counts requests within each window. When a window ends, the counter resets. How It WorksVisual RepresentationCharacteristicsPros:
Cons:
Use Cases
Boundary Problem ExampleMathematical Formula5. Sliding Window LogDescriptionThe Sliding Window Log keeps a timestamp log of all requests within the time window. For each new request, it removes expired timestamps and checks if the count is below the limit. How It WorksVisual RepresentationCharacteristicsPros:
Cons:
Use Cases
Memory ConsiderationsMathematical Formula6. Sliding Window CounterDescriptionThe Sliding Window Counter (also called Sliding Window with Counter) is a hybrid approach that combines Fixed Window efficiency with Sliding Window accuracy. It uses two counters (current and previous window) and interpolates between them. How It WorksVisual RepresentationVisual TimelineCharacteristicsPros:
Cons:
Use Cases
Implementation in Go LibrariesLibraries using Sliding Window Counter:
Accuracy AnalysisMathematical FormulaRedis Implementation-- Two Redis keys: current and previous window
local current_key = KEYS[1]
local previous_key = KEYS[2]
local limit = tonumber(ARGV[1])
local window_size = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local current_count = tonumber(redis.call('GET', current_key) or 0)
local previous_count = tonumber(redis.call('GET', previous_key) or 0)
local position = (now % window_size) / window_size
local estimate = previous_count * (1 - position) + current_count
if estimate < limit then
redis.call('INCR', current_key)
redis.call('EXPIRE', current_key, window_size * 2)
return 1 -- allowed
else
return 0 -- denied
end7. Comparison MatrixQuick Reference Table
Detailed ComparisonAccuracy SpectrumMemory UsagePerformance RankingUse Case Matrix
8. Algorithm Selection GuideDecision TreeBy Deployment TypeSingle Instance
Distributed (Redis-backed)
Kubernetes/Microservices
Multi-Cloud/Multi-Region
By Use Case1. Public API (like Stripe, GitHub)2. Internal Microservice3. User Dashboard/Web App4. Background Job Queue5. DDoS ProtectionPerformance CharacteristicsLatency RequirementsThroughput RequirementsImplementation ComplexityDevelopment TimeMaintenance BurdenReal-World ExamplesExample 1: GitHub API
Example 2: Stripe API
Example 3: Kong API Gateway
Example 4: Cloudflare
Testing Rate LimitersTest Scenarios1. Burst Test2. Sustained Load Test3. Boundary Test4. Distributed TestSummaryQuick RecommendationsFor most use cases: Sliding Window Counter (ulule/limiter)
For strict rate limiting: GCRA (go-redis/redis_rate)
For allowing bursts: Token Bucket (juju/ratelimit)
For simplicity: Fixed Window
Avoid: Sliding Window Log (unless very low volume)
9. Remaining Request TrackingOverviewA critical feature of rate limiting APIs is the ability to inform clients about how many requests they have remaining in the current time window. This section explains how each algorithm calculates and exposes this information. Why Tracking Remaining Requests MattersBenefits:
Standard HTTP Headers (RFC 6585): X-RateLimit-Limit: 100 # Maximum requests allowed
X-RateLimit-Remaining: 75 # Requests remaining in window
X-RateLimit-Reset: 1638360000 # When the limit resets (Unix timestamp)
Retry-After: 30 # Seconds until next request allowedAlgorithm-Specific Tracking Capabilities1. Token Bucket - Excellent TrackingWhat it tracks:
Calculation: def get_remaining_info():
# Calculate current tokens
elapsed = now() - last_update
current_tokens = min(capacity,
tokens + elapsed * refill_rate)
return {
'remaining': floor(current_tokens),
'limit': capacity,
'reset': now() + (capacity - current_tokens) / refill_rate,
'retry_after': 0 if current_tokens >= 1 else
(1 - current_tokens) / refill_rate
}Response Headers: X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1638360050
Retry-After: 0Pros:
Cons:
Go Library Support: // juju/ratelimit example
bucket := ratelimit.NewBucket(time.Second/10, 100)
remaining := bucket.Available() // Get remaining tokens
waitTime := bucket.Take(1) // Time to wait for token2. Leaky Bucket - Moderate TrackingWhat it tracks:
Calculation: def get_remaining_info():
# Calculate current level after leaking
elapsed = now() - last_update
current_level = max(0, level - elapsed * leak_rate)
return {
'remaining': capacity - current_level,
'limit': capacity,
'reset': now() + current_level / leak_rate,
'retry_after': max(0, (current_level + 1 - capacity) / leak_rate)
}Response Headers: X-RateLimit-Limit: 100
X-RateLimit-Remaining: 23
X-RateLimit-Reset: 1638360100
Retry-After: 5Pros:
Cons:
3. GCRA - Excellent but Complex TrackingWhat it tracks:
Calculation: def get_remaining_info():
now_time = now()
tat_current = max(tat, now_time)
# Calculate how many requests can be made immediately
allow_at = tat_current - burst_allowance
time_to_wait = max(0, allow_at - now_time)
# Remaining burst capacity
used_burst = max(0, tat_current - now_time)
remaining_burst = burst_capacity - (used_burst / emission_interval)
return {
'remaining': floor(remaining_burst),
'limit': burst_capacity,
'reset': tat_current,
'retry_after': ceil(time_to_wait)
}Response Headers: X-RateLimit-Limit: 100
X-RateLimit-Remaining: 12
X-RateLimit-Reset: 1638360075
Retry-After: 0Pros:
Cons:
Go Library Support: // go-redis/redis_rate example
limiter := redis_rate.NewLimiter(redisClient)
res, err := limiter.Allow(ctx, "key", redis_rate.PerMinute(100))
if err != nil {
panic(err)
}
fmt.Println("Allowed:", res.Allowed)
fmt.Println("Remaining:", res.Remaining)
fmt.Println("Retry after:", res.RetryAfter)
fmt.Println("Reset after:", res.ResetAfter)4. Fixed Window Counter - Simple TrackingWhat it tracks:
Calculation: def get_remaining_info():
window_id = floor(now() / window_size)
current_count = counter[window_id]
window_start = window_id * window_size
window_end = window_start + window_size
return {
'remaining': max(0, limit - current_count),
'limit': limit,
'reset': window_end,
'retry_after': 0 if current_count < limit else
(window_end - now())
}Response Headers: X-RateLimit-Limit: 100
X-RateLimit-Remaining: 37
X-RateLimit-Reset: 1638360060
Retry-After: 0Pros:
Cons:
Example Issue: 5. Sliding Window Log - Perfect TrackingWhat it tracks:
Calculation: def get_remaining_info():
now_time = now()
# Remove expired entries
valid_log = [ts for ts in log if ts > now_time - window_size]
remaining = limit - len(valid_log)
# Time until oldest entry expires (space becomes available)
if len(valid_log) >= limit:
oldest = min(valid_log)
retry_after = oldest + window_size - now_time
else:
retry_after = 0
return {
'remaining': remaining,
'limit': limit,
'reset': now_time + window_size,
'retry_after': retry_after
}Response Headers: X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1638360100
Retry-After: 0Pros:
Cons:
6. Sliding Window Counter - Good ApproximationWhat it tracks:
Calculation: def get_remaining_info():
now_time = now()
# Calculate position in current window
position = (now_time % window_size) / window_size
# Estimate current usage
estimated_count = previous_count * (1 - position) + current_count
remaining = max(0, limit - ceil(estimated_count))
# Next window reset
current_window_start = floor(now_time / window_size) * window_size
next_reset = current_window_start + window_size
return {
'remaining': remaining,
'limit': limit,
'reset': next_reset,
'retry_after': 0 if remaining > 0 else (next_reset - now_time)
}Response Headers: X-RateLimit-Limit: 100
X-RateLimit-Remaining: 38
X-RateLimit-Reset: 1638360060
Retry-After: 0Pros:
Cons:
Go Library Support: // ulule/limiter example
import "github.com/ulule/limiter/v3"
limiter := limiter.New(store, rate)
context, err := limiter.Get(ctx, "key")
fmt.Println("Limit:", context.Limit)
fmt.Println("Remaining:", context.Remaining)
fmt.Println("Reset:", context.Reset)
fmt.Println("Reached:", context.Reached)Comparison: Remaining Request Tracking
*Exact within current window, but has boundary issues Implementation PatternsPattern 1: Return Metadata with Every RequestBest for: All algorithms type RateLimitResult struct {
Allowed bool // Was request allowed?
Limit int // Total limit
Remaining int // Remaining requests
Reset time.Time // When limit resets
RetryAfter int // Seconds to wait if denied
}
func CheckRateLimit(key string) RateLimitResult {
// Algorithm-specific logic
return RateLimitResult{
Allowed: true,
Limit: 100,
Remaining: 42,
Reset: time.Now().Add(60 * time.Second),
RetryAfter: 0,
}
}Pattern 2: Separate Query MethodBest for: High-performance scenarios // Check limit (modifies state)
allowed := limiter.Allow(ctx, key)
// Query status (read-only)
status := limiter.GetStatus(ctx, key)
fmt.Println("Remaining:", status.Remaining)Pattern 3: HTTP MiddlewareBest for: Web APIs func RateLimitMiddleware(limiter RateLimiter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
key := getClientKey(r)
result := limiter.Check(key)
// Always set headers
w.Header().Set("X-RateLimit-Limit", strconv.Itoa(result.Limit))
w.Header().Set("X-RateLimit-Remaining", strconv.Itoa(result.Remaining))
w.Header().Set("X-RateLimit-Reset", strconv.FormatInt(result.Reset.Unix(), 10))
if !result.Allowed {
w.Header().Set("Retry-After", strconv.Itoa(result.RetryAfter))
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
}Library-Specific Examplesgo-redis/redis_rate (GCRA)import "github.com/go-redis/redis_rate/v10"
limiter := redis_rate.NewLimiter(rdb)
res, err := limiter.Allow(ctx, "user:123", redis_rate.PerMinute(100))
if err != nil {
panic(err)
}
fmt.Printf("Allowed: %v\n", res.Allowed)
fmt.Printf("Limit: %d\n", res.Limit)
fmt.Printf("Remaining: %d\n", res.Remaining)
fmt.Printf("Reset after: %v\n", res.ResetAfter)
fmt.Printf("Retry after: %v\n", res.RetryAfter)
// Output:
// Allowed: true
// Limit: 100
// Remaining: 42
// Reset after: 45s
// Retry after: 0sulule/limiter (Sliding Window Counter)import "github.com/ulule/limiter/v3"
import "github.com/ulule/limiter/v3/drivers/store/memory"
rate := limiter.Rate{
Period: 1 * time.Minute,
Limit: 100,
}
store := memory.NewStore()
instance := limiter.New(store, rate)
context, err := instance.Get(ctx, "user:123")
if err != nil {
panic(err)
}
fmt.Printf("Limit: %d\n", context.Limit)
fmt.Printf("Remaining: %d\n", context.Remaining)
fmt.Printf("Reset: %v\n", context.Reset)
fmt.Printf("Reached: %v\n", context.Reached)
// Output:
// Limit: 100
// Remaining: 38
// Reset: 2025-12-07 15:45:00 +0000 UTC
// Reached: falsejuju/ratelimit (Token Bucket)import "github.com/juju/ratelimit"
// 100 requests per second with burst of 100
bucket := ratelimit.NewBucketWithRate(100, 100)
// Get remaining tokens
remaining := bucket.Available()
fmt.Printf("Remaining: %d\n", remaining)
// Try to take a token
if bucket.TakeAvailable(1) > 0 {
fmt.Println("Request allowed")
} else {
fmt.Println("Rate limited")
}
// Get wait time for next token
waitDuration := bucket.Take(1)
fmt.Printf("Wait time: %v\n", waitDuration)
// Output:
// Remaining: 45
// Request allowed
// Wait time: 0sthrottled/throttled (GCRA)import "github.com/throttled/throttled/v2"
import "github.com/throttled/throttled/v2/store/memstore"
store, err := memstore.New(65536)
quota := throttled.RateQuota{
MaxRate: throttled.PerMin(100),
MaxBurst: 10,
}
rateLimiter, err := throttled.NewGCRARateLimiter(store, quota)
limited, result, err := rateLimiter.RateLimit("user:123", 1)
fmt.Printf("Limited: %v\n", limited)
fmt.Printf("Limit: %d\n", result.Limit)
fmt.Printf("Remaining: %d\n", result.Remaining)
fmt.Printf("Reset after: %v\n", result.ResetAfter)
fmt.Printf("Retry after: %v\n", result.RetryAfter)
// Output:
// Limited: false
// Limit: 100
// Remaining: 12
// Reset after: 45s
// Retry after: 0smennanov/limiters (Multiple algorithms)import "github.com/mennanov/limiters"
// Token bucket
limiter := limiters.NewTokenBucket(
100, // capacity
10*time.Second, // refill interval
limiters.NewSystemClock(),
)
// Check capacity
capacity := limiter.Capacity()
fmt.Printf("Remaining: %d\n", capacity)
// Try to take tokens
err := limiter.Limit(context.Background())
if err != nil {
fmt.Println("Rate limited")
} else {
fmt.Println("Request allowed")
}Best Practices for Exposing Remaining Requests1. Always Include Standard HeadersX-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 16383600602. Add Retry-After on Rate LimitHTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1638360060
{
"error": "rate_limit_exceeded",
"message": "You have exceeded the rate limit. Please retry after 30 seconds."
}3. Provide Multiple Time Formats{
"rate_limit": {
"limit": 100,
"remaining": 42,
"reset": 1638360060,
"reset_iso": "2025-12-07T15:41:00Z",
"reset_relative": "in 30 seconds"
}
}4. Document Conservative vs Exact Counts5. Handle Clock Skew in Distributed Systems// Add safety margin for distributed clock differences
safeRemaining := max(0, calculatedRemaining - 2)
w.Header().Set("X-RateLimit-Remaining", strconv.Itoa(safeRemaining))Common PitfallsPitfall 1: Race ConditionsProblem: In distributed systems, remaining count can decrease between read and write Solution: Use atomic operations or accept slight inaccuracy // Redis atomic operation
remaining, err := rdb.Eval(ctx, `
local current = redis.call('GET', KEYS[1]) or 0
if tonumber(current) < tonumber(ARGV[1]) then
redis.call('INCR', KEYS[1])
return tonumber(ARGV[1]) - tonumber(current) - 1
end
return -1
`, []string{key}, limit).Int()Pitfall 2: Negative Remaining CountsProblem: Showing -5 remaining is confusing Solution: Always floor at 0 remaining := max(0, limit - current)Pitfall 3: Misleading Reset TimesProblem: Fixed window shows reset time that might mislead users Solution: Clarify in documentation Pitfall 4: Not Accounting for BurstProblem: Token bucket shows capacity, not sustained rate Solution: Expose both metrics X-RateLimit-Limit: 100
X-RateLimit-Burst: 100
X-RateLimit-Remaining: 45
X-RateLimit-Sustained-Rate: 10Summary: Which Algorithm to Choose for Remaining Tracking?Best tracking accuracy:
Best for user experience:
Best for distributed systems:
Recommendation:
References and Further Reading
Appendix: Algorithm PseudocodeToken Bucket Pseudocodeclass TokenBucket:
def __init__(self, capacity, refill_rate):
self.capacity = capacity
self.tokens = capacity
self.refill_rate = refill_rate
self.last_update = now()
def allow_request(self, cost=1):
# Refill tokens
elapsed = now() - self.last_update
self.tokens = min(self.capacity,
self.tokens + elapsed * self.refill_rate)
self.last_update = now()
# Check and consume
if self.tokens >= cost:
self.tokens -= cost
return True
return FalseGCRA Pseudocodeclass GCRA:
def __init__(self, rate, burst):
self.emission_interval = 1.0 / rate
self.burst_allowance = burst * self.emission_interval
self.tat = 0 # Theoretical Arrival Time
def allow_request(self):
now = time.now()
self.tat = max(self.tat, now)
allow_at = self.tat - self.burst_allowance
if now >= allow_at:
self.tat += self.emission_interval
return True
return FalseSliding Window Counter Pseudocodeclass SlidingWindowCounter:
def __init__(self, limit, window_size):
self.limit = limit
self.window_size = window_size
self.previous_count = 0
self.current_count = 0
self.current_window_start = now()
def allow_request(self):
now = time.now()
# Check if we need to rotate windows
if now - self.current_window_start >= self.window_size:
self.previous_count = self.current_count
self.current_count = 0
self.current_window_start = now
# Calculate position in current window
position = (now - self.current_window_start) / self.window_size
# Estimate current count using interpolation
estimate = (self.previous_count * (1 - position) +
self.current_count)
if estimate < self.limit:
self.current_count += 1
return True
return FalseDocument Version: 1.0 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Instead of going with the envoy/ratelimt gRPC service, we can use a library or our own implementation. This eliminates the extra network hop and extra container, and allows rate limiting to be handled like any other policy.
Beta Was this translation helpful? Give feedback.
All reactions