AI Infrastructure

Building Resilient LLM Gateways: Implementing Circuit Breakers and Rate Limiting

As Large Language Models (LLMs) transition from experimental playgrounds to mission-critical infrastructure, the demand for robust, scalable, and cost-effective serving layers has never been higher. However, relying directly on external LLM APIs introduces significant risks: unpredictable latency, sudden rate limit bans, cascading failures, and runaway token consumption. To mitigate these risks, modern AI infrastructure requires a sophisticated gateway layer that sits between your application and the model providers.

In this post, we will explore how to implement two fundamental resilience patterns—Circuit Breakers and Rate Limiting—to create an LLM gateway that is both stable and economical.

The Problem with Direct LLM Integration

When an application calls an LLM endpoint directly without protection, it is vulnerable to two primary failure modes. First, the upstream provider may experience downtime or severe latency spikes. If thousands of your users trigger these calls simultaneously, your entire backend can become saturated waiting for responses, leading to a cascading failure of your own services. Second, cost can spiral out of control if a bug causes an infinite loop of requests, hammering the provider's rate limits and generating unexpected bills.

A dedicated gateway layer acts as a buffer, allowing you to manage these risks proactively rather than reactively.

Implementing Circuit Breakers for Fail-Fast Behavior

A circuit breaker prevents your system from trying to perform an operation that is likely to fail. In the context of an LLM gateway, this means monitoring the health of the upstream model provider. If the provider starts returning errors (e.g., 503 Service Unavailable) or exceeding latency thresholds, the circuit "trips," and subsequent requests are immediately rejected or served from a cache without hitting the upstream service.

This pattern is crucial for maintaining system availability. Instead of hanging threads waiting for a timeout, your gateway fails fast, allowing the user experience to degrade gracefully (e.g., by returning a friendly error message or falling back to a smaller, more robust model).

// Pseudo-code for a simple Circuit Breaker Implementation
class LLMCircuitBreaker:
    def __init__(self, failure_threshold=5, recovery_timeout=60):
        self.failure_count = 0
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
        self.last_failure_time = 0

    def can_execute(self):
        if self.state == "OPEN":
            if time.time() - self.last_failure_time > self.recovery_timeout:
                self.state = "HALF_OPEN"
                return True
            return False
        return True

    def record_success(self):
        self.failure_count = 0
        self.state = "CLOSED"

    def record_failure(self):
        self.failure_count += 1
        self.last_failure_time = time.time()
        if self.failure_count >= self.failure_threshold:
            self.state = "OPEN"
            print("Circuit tripped! Protecting upstream LLM provider.")

Rate Limiting to Control Costs and Prevent Throttling

While circuit breakers handle availability, rate limiting handles capacity and cost. LLM providers typically enforce strict requests-per-minute (RPM) or tokens-per-minute (TPM) limits. Without local rate limiting, a sudden surge in traffic can cause your application to exceed these limits, resulting in HTTP 429 errors and blocked API keys.

Implementing a rate limiter at the gateway level allows you to smooth out traffic spikes. You can enforce limits based on user ID, API key, or overall system load. This not only prevents throttling from upstream providers but also helps you manage your own budget by capping the maximum number of tokens processed per hour.

// Example: Token Bucket Rate Limiter Logic
class TokenBucketRateLimiter:
    def __init__(self, max_tokens, refill_rate):
        self.max_tokens = max_tokens
        self.refill_rate = refill_rate
        self.current_tokens = max_tokens
        self.last_refill_time = time.time()

    def acquire(self, token_cost):
        self._refill()
        if self.current_tokens >= token_cost:
            self.current_tokens -= token_cost
            return True
        return False

    def _refill(self):
        now = time.time()
        tokens_to_add = (now - self.last_refill_time) * self.refill_rate
        self.current_tokens = min(self.max_tokens, self.current_tokens + tokens_to_add)
        self.last_refill_time = now

Combining Patterns for Maximum Resilience

The true power of an LLM gateway emerges when you combine these patterns. Rate limiting ensures you don't overwhelm the provider or your own budget, while the circuit breaker ensures that when the provider is down, your application remains responsive. Together, they provide a defensive layer that absorbs shocks, manages resources efficiently, and ensures that your AI-powered features remain reliable even when the underlying infrastructure is volatile.

Conclusion

Building resilient LLM gateways is no longer optional; it is a requirement for production-grade AI applications. By implementing circuit breakers to handle failures and rate limiters to manage load, developers can protect their systems from cascading outages and cost overruns. As AI workloads continue to grow, investing in this infrastructure today will save significant technical debt and operational headaches tomorrow.

Share: