Circuit Breakers
Automatically detect failing backends and stop sending traffic to them. Circuit breakers prevent cascade failures and give your upstream services time to recover.
How It Works
Nolxy implements a per-route circuit breaker with three states:
CLOSED
Normal operation. All requests pass through. Failures are counted.
OPEN
Circuit tripped. All requests immediately return 503. Backend gets time to recover.
HALF-OPEN
Probe phase. One request is allowed through. Success → CLOSED. Failure → OPEN.
Configuration
Circuit breakers are configured per-route in the dashboard or via Gateway as Code:
Failure Threshold
Number of failures before the circuit opens (default: 5)
Monitoring Window
Time window for counting failures in milliseconds (default: 60,000ms)
Recovery Timeout
How long the circuit stays open before transitioning to half-open (default: 30,000ms)
Success Threshold
Successful requests needed in half-open to close the circuit (default: 2)
Implementation Details
- Atomic state management — Circuit state transitions use Redis Lua scripts to prevent race conditions across multiple gateway workers.
- In-memory caching — An LRU cache stores circuit state locally to reduce Redis calls on the hot path. Cache entries have short TTLs to stay fresh.
- Notifications — When a circuit opens or closes, Nolxy sends notifications via email and webhooks (if configured).
- Retry-After header — When a circuit is open, the 503 response includes a
Retry-Afterheader indicating when the client should retry.
Lua Script (Simplified)
The CAN_REQUEST script runs atomically on Redis:
-- State check
local state = redis.call('GET', KEYS[1]) or 'CLOSED'
if state == 'CLOSED' then
return {1, 'CLOSED', 0} -- allowed
end
if state == 'HALF_OPEN' then
return {1, 'HALF_OPEN', 0} -- probe allowed
end
-- OPEN: check if timeout elapsed
local openedAt = tonumber(redis.call('GET', KEYS[2]))
local elapsed = tonumber(ARGV[2]) - openedAt
local timeout = tonumber(ARGV[1])
if elapsed >= timeout then
redis.call('SET', KEYS[1], 'HALF_OPEN')
return {1, 'HALF_OPEN', 0} -- transition to probe
end
local retryAfter = math.ceil((timeout - elapsed) / 1000)
return {0, 'OPEN', retryAfter} -- blockedPlan Requirements
Circuit Breakers are available on Pro plans and above.