class Backend

Represents a live backend client and its admission state.

Definitions

def initialize(endpoint, client, exchange_limit:, &available)

Initialize a backend.

Signature

parameter endpoint Endpoint

The endpoint served by this backend.

parameter client Interface(:call, :close)

The HTTP client for the endpoint.

parameter exchange_limit Integer

The maximum number of outstanding response exchanges.

yields {|backend| ...}

Invoked when the backend can accept another request.

Implementation

def initialize(endpoint, client, exchange_limit:, &available)
	raise ArgumentError, "Exchange limit must be positive!" unless exchange_limit.positive?
	
	@endpoint = endpoint
	@client = client
	@exchange_limit = exchange_limit
	@available = available
	
	@guard = Thread::Mutex.new
	@active = true
	@processing = false
	@exchanges = 0
	@queued = false
	@closed = false
end

attr :endpoint

Signature

attribute Endpoint

The endpoint served by this backend.

def name

Signature

attribute String

The stable backend name.

Implementation

def name
	@endpoint.name
end

attr :exchange_limit

Signature

attribute Integer

The maximum number of outstanding response exchanges.

def start

Start advertising this backend as available.

Implementation

def start
	notify_available
end

def reserve

Reserve the processing slot and one response exchange.

Signature

returns Boolean

Whether the backend was successfully reserved.

Implementation

def reserve
	@guard.synchronize do
		@queued = false
		
		if @active && !@processing && @exchanges < @exchange_limit
			@processing = true
			@exchanges += 1
			return true
		end
	end
	
	return false
end

def call(request)

Send a request to the backend.

Signature

parameter request Protocol::HTTP::Request

The upstream request.

returns Protocol::HTTP::Response

The upstream response.

Implementation

def call(request)
	@client.call(request)
end

def processed

Release the request-processing slot after response headers arrive.

Implementation

def processed
	@guard.synchronize do
		raise RuntimeError, "Backend is not processing a request!" unless @processing
		@processing = false
	end
	
	notify_available
end

def failed

Release both reservations when a request fails before response headers.

Implementation

def failed
	close = @guard.synchronize do
		raise RuntimeError, "Backend is not processing a request!" unless @processing
		@processing = false
		@exchanges -= 1
		should_close?
	end
	
	notify_available
	close_client if close
end

def release

Release an outstanding response exchange.

Implementation

def release
	close = @guard.synchronize do
		raise RuntimeError, "Backend has no outstanding exchange!" unless @exchanges.positive?
		@exchanges -= 1
		should_close?
	end
	
	notify_available
	close_client if close
end

def retire

Retire this backend without interrupting outstanding responses.

Implementation

def retire
	close = @guard.synchronize do
		@active = false
		should_close?
	end
	
	close_client if close
end

def active?

Signature

returns Boolean

Whether this backend still accepts new requests.

Implementation

def active?
	@guard.synchronize{@active}
end

def exchanges

Signature

returns Integer

The number of outstanding response exchanges.

Implementation

def exchanges
	@guard.synchronize{@exchanges}
end

def processing?

Signature

returns Boolean

Whether a request is waiting for response headers.

Implementation

def processing?
	@guard.synchronize{@processing}
end