GrpcConnectionPool.Pool (grpc_connection_pool v0.4.0)

Copy Markdown View Source

High-performance gRPC connection pool with pluggable selection strategies.

This module provides a DynamicSupervisor-based pool with:

  • Zero GenServer.call hot path — channels stored in ETS for O(1) access
  • Pluggable strategies — round-robin (atomics), random, power-of-two-choices
  • :persistent_term for config — zero-copy reads
  • ETS with read/write concurrency — optimized for concurrent access
  • Registry for health tracking
  • Telemetry with proper GenServer-based status loop

Architecture

Pool.Supervisor (one_for_one)
 PoolState (GenServer  owns ETS, stores persistent_term)
 Registry (health tracking)
 DynamicSupervisor (manages workers)
 WorkerStarter (temporary Task)
 TelemetryReporter (GenServer  periodic status)

Hot Path

get_channel/1 does zero GenServer calls:

ETS.lookup(:channel_count)  Strategy.select  ETS.lookup({:channel, idx})  channel

Summary

Functions

Blocks until at least one channel is connected or timeout is reached.

Returns the child specification for supervision trees.

Gets all worker PIDs in the pool.

Gets a gRPC channel from the pool.

Dynamically remove workers from the pool.

Dynamically add workers to the pool.

Starts a new connection pool.

Gets pool status and statistics.

Functions

await_ready(pool_name \\ GrpcConnectionPool.Pool, timeout \\ 10000)

@spec await_ready(atom(), pos_integer()) :: :ok | {:error, :timeout}

Blocks until at least one channel is connected or timeout is reached.

Useful for application startup to ensure the pool is ready before serving traffic.

Examples

{:ok, _} = GrpcConnectionPool.Pool.start_link(config)
:ok = GrpcConnectionPool.Pool.await_ready(MyPool, 5_000)

child_spec(init_arg)

Returns the child specification for supervision trees.

get_all_pids(pool_name \\ GrpcConnectionPool.Pool)

@spec get_all_pids(atom()) :: [pid()]

Gets all worker PIDs in the pool.

get_channel(pool_name \\ GrpcConnectionPool.Pool)

@spec get_channel(atom()) :: {:ok, GRPC.Channel.t()} | {:error, :not_connected}

Gets a gRPC channel from the pool.

Zero GenServer calls — reads directly from ETS using the configured selection strategy (default: atomics-based round-robin).

Returns

  • {:ok, channel} — Successfully retrieved a connected channel
  • {:error, :not_connected} — No healthy connections available

Examples

case GrpcConnectionPool.Pool.get_channel() do
  {:ok, channel} -> MyService.Stub.call(channel, request)
  {:error, :not_connected} -> {:error, :unavailable}
end

resize(pool_name \\ GrpcConnectionPool.Pool, target_size)

@spec resize(atom(), pos_integer()) :: {:ok, non_neg_integer()} | {:error, term()}

Resize pool to exact size.

scale_down(pool_name \\ GrpcConnectionPool.Pool, count)

@spec scale_down(atom(), pos_integer()) :: {:ok, non_neg_integer()} | {:error, term()}

Dynamically remove workers from the pool.

Returns

  • {:ok, new_size} — Successfully removed workers
  • {:error, reason} — Failed to remove workers

scale_up(pool_name \\ GrpcConnectionPool.Pool, count)

@spec scale_up(atom(), pos_integer()) :: {:ok, non_neg_integer()} | {:error, term()}

Dynamically add workers to the pool.

Returns

  • {:ok, new_size} — Successfully added workers
  • {:error, reason} — Failed to add workers

start_link(config_or_opts, opts \\ [])

Starts a new connection pool.

Options

  • :name — Pool name (overrides config name)

Examples

{:ok, config} = GrpcConnectionPool.Config.production(host: "api.example.com", pool_size: 8)
{:ok, pid} = GrpcConnectionPool.Pool.start_link(config)

status(pool_name \\ GrpcConnectionPool.Pool)

@spec status(atom()) :: map()

Gets pool status and statistics.

stop(pool_name \\ GrpcConnectionPool.Pool)

@spec stop(atom()) :: :ok

Stops a connection pool.