Class: LiveKit::RegionFailoverMiddleware

Inherits:
Faraday::Middleware
  • Object
show all
Defined in:
lib/livekit/failover.rb

Overview

Faraday middleware that fails over to alternative LiveKit Cloud regions on a retryable error: any transport error or HTTP 5xx. A 4xx is returned immediately. The request body and headers are replayed intact against the next untried region, with exponential backoff.

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ RegionFailoverMiddleware

Returns a new instance of RegionFailoverMiddleware.



147
148
149
150
151
152
153
# File 'lib/livekit/failover.rb', line 147

def initialize(app, options = {})
  super(app)
  @failover = options.fetch(:failover, true)
  # +force+ / +backoff_base+ are internal test-only knobs.
  @force = options.fetch(:force, false)
  @backoff_base = options.fetch(:backoff_base, Failover::BACKOFF_BASE)
end

Instance Method Details

#call(env) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/livekit/failover.rb', line 155

def call(env)
  original_url = env.url.dup
  request_body = env.body

  # A per-request timeout override (e.g. for SIP dialing) travels as an
  # internal header; consume it so it isn't sent to the server. Otherwise
  # use the connection's configured timeout.
  timeout = env.request_headers.delete(Failover::TIMEOUT_HEADER)&.to_f
  timeout ||= env.request.timeout
  env.request.timeout = timeout if timeout

  request_headers = env.request_headers.dup
  max_attempts = Failover.attempts(@failover, original_url.host, @force, timeout)

  attempted = [Failover.host_key(original_url)]
  regions = nil
  current_url = original_url
  attempt = 0

  loop do
    is_last = attempt + 1 >= max_attempts
    env.url = current_url
    env.body = request_body

    response = nil
    error = nil
    begin
      response = @app.call(env)
    rescue Faraday::Error => e
      error = e
    end

    # Success or a non-retryable 4xx is terminal.
    return response if response && response.status < 500

    nxt = nil
    unless is_last
      regions ||= Failover.region_uris(original_url, request_headers)
      nxt = Failover.pick_next(regions, attempted)
    end

    if nxt.nil?
      return response if response
      raise error
    end

    reason = response ? "status #{response.status}" : (error&.message || 'error')
    warn("livekit API request to #{current_url.host} failed (#{reason}), " \
         "retrying with fallback url #{nxt}")
    sleep(@backoff_base * (2**attempt))
    attempted << Failover.host_key(nxt)
    # Swap only the region's scheme/host/port, preserving the request path.
    current_url = original_url.dup
    current_url.scheme = nxt.scheme
    current_url.host = nxt.host
    current_url.port = nxt.port
    attempt += 1
  end
end