livery_client_balance_store (livery v0.2.0)

View Source

Owns the ETS table backing the client load balancers.

A gen_server that creates a public named table at startup; the selection logic in livery_client_balance reads and writes it directly (no per-request round trip through this process). One pool per name, with one row per endpoint:

{{ep, Name, Endpoint}, Status, Fails, Until, Inflight}

Status is up | ejected. Inflight is a per-endpoint atomics ref holding the in-flight request count (the P2C load metric). Until is an erlang:monotonic_time(millisecond) deadline: while ejected, the endpoint rejoins only after Until has passed, and recovery is a single probe leased with an atomic compare-and-swap so concurrent callers cannot all probe at once. A {{meta, Name}, RoundRobinRef} row marks the pool's existence (for create-once ensure/2) and carries the round-robin counter.

Summary

Functions

Add an endpoint to a pool at runtime.

Seed a pool from Endpoints, once. If the pool already exists this is a no-op, so a lazy call on a later request never undoes a runtime remove/2 or re-adds a drained node. The cheap membership check runs in the caller; only a miss talks to the gen_server.

Pick an endpoint for one request, incrementing its in-flight count. Returns {ok, Endpoint, Token} (pass Token to release/1 when the request finishes) or {error, no_endpoint}. A recovering endpoint whose cooldown has expired is leased for a single probe via an atomic CAS; otherwise selection is over the healthy endpoints by Policy.

Record a request outcome against the endpoint. ok resets the failure streak and reinstates an ejected endpoint; err increments the streak and ejects at EjectAfter, or re-ejects a failed probe. A missing row (removed mid-flight) is a no-op.

Release the in-flight slot taken by pick/3 (safe if removed).

Remove an endpoint from a pool at runtime.

Forget a pool entirely.

Types

policy()

-type policy() :: p2c | round_robin.

state()

-type state() :: #state{}.

Functions

add(Name, Endpoint)

-spec add(term(), binary()) -> ok.

Add an endpoint to a pool at runtime.

ensure(Name, Endpoints)

-spec ensure(term(), [binary()]) -> ok.

Seed a pool from Endpoints, once. If the pool already exists this is a no-op, so a lazy call on a later request never undoes a runtime remove/2 or re-adds a drained node. The cheap membership check runs in the caller; only a miss talks to the gen_server.

handle_call/3

-spec handle_call(term(), {pid(), term()}, state()) -> {reply, ok, state()}.

handle_cast(Msg, State)

-spec handle_cast(term(), state()) -> {noreply, state()}.

handle_info(Info, State)

-spec handle_info(term(), state()) -> {noreply, state()}.

init/1

-spec init([]) -> {ok, state()}.

pick(Name, Policy, EjectFor)

-spec pick(term(), policy(), non_neg_integer()) ->
              {ok, binary(), atomics:atomics_ref()} | {error, no_endpoint}.

Pick an endpoint for one request, incrementing its in-flight count. Returns {ok, Endpoint, Token} (pass Token to release/1 when the request finishes) or {error, no_endpoint}. A recovering endpoint whose cooldown has expired is leased for a single probe via an atomic CAS; otherwise selection is over the healthy endpoints by Policy.

record(Name, Endpoint, Outcome, EjectAfter, EjectFor)

-spec record(term(), binary(), ok | err, pos_integer(), non_neg_integer()) -> ok.

Record a request outcome against the endpoint. ok resets the failure streak and reinstates an ejected endpoint; err increments the streak and ejects at EjectAfter, or re-ejects a failed probe. A missing row (removed mid-flight) is a no-op.

release(Ref)

-spec release(atomics:atomics_ref()) -> ok.

Release the in-flight slot taken by pick/3 (safe if removed).

remove(Name, Endpoint)

-spec remove(term(), binary()) -> ok.

Remove an endpoint from a pool at runtime.

reset(Name)

-spec reset(term()) -> ok.

Forget a pool entirely.

start_link()

-spec start_link() -> {ok, pid()} | {error, term()}.