# `Snakepit`
[🔗](https://github.com/nshkrdotcom/snakepit/blob/v0.13.0/lib/snakepit.ex#L1)

Snakepit - A generalized high-performance pooler and session manager.

Extracted from DSPex V3 pool implementation, Snakepit provides:
- Concurrent worker initialization and management
- Stateless pool system with session affinity (hint by default, strict modes available)
- Generalized adapter pattern for any external process
- High-performance OTP-based process management

## Basic Usage

    # Configure in config/config.exs
    config :snakepit,
      pooling_enabled: true,
      adapter_module: YourAdapter

    # Execute commands on any available worker
    {:ok, result} = Snakepit.execute("ping", %{test: true})

    # Session-based execution with worker affinity
    {:ok, result} = Snakepit.execute_in_session("my_session", "command", %{})

# `args`

```elixir
@type args() :: map()
```

# `callback_fn`

```elixir
@type callback_fn() :: (term() -&gt; any())
```

# `command`

```elixir
@type command() :: String.t()
```

# `pool_name`

```elixir
@type pool_name() :: atom() | pid()
```

# `result`

```elixir
@type result() :: term()
```

# `session_id`

```elixir
@type session_id() :: String.t()
```

# `cleanup`

```elixir
@spec cleanup() :: :ok | {:timeout, list()}
```

Manually trigger cleanup of external worker processes for the current run.

Useful for library embedding or scripts that control the lifecycle directly.

# `execute`

```elixir
@spec execute(command(), args(), keyword()) ::
  {:ok, result()} | {:error, Snakepit.Error.t()}
```

Convenience function to execute commands on the pool.

## Examples

    {:ok, result} = Snakepit.execute("ping", %{test: true})

## Options

  * `:pool` - The pool to use (default: `Snakepit.Pool`)
  * `:timeout` - Request timeout in ms (default: 60000)
  * `:session_id` - Execute with session affinity
  * `:affinity` - Override affinity mode (`:hint`, `:strict_queue`, `:strict_fail_fast`)

# `execute_in_session`

```elixir
@spec execute_in_session(session_id(), command(), args(), keyword()) ::
  {:ok, result()} | {:error, Snakepit.Error.t()}
```

Executes a command in session context with worker affinity.

This function executes commands with session-based worker affinity,
ensuring that subsequent calls with the same session_id prefer
the same worker when possible for state continuity.

By default, affinity is a hint: if the preferred worker is busy or tainted,
the pool can fall back to another worker. To guarantee pinning for in-memory
refs, configure `affinity: :strict_queue` or `:strict_fail_fast` at the pool level.

Args are passed through unchanged - no domain-specific enhancement.

# `execute_in_session_stream`

```elixir
@spec execute_in_session_stream(
  session_id(),
  command(),
  args(),
  callback_fn(),
  keyword()
) ::
  :ok | {:error, Snakepit.Error.t()}
```

Executes a command in a session with a callback function.

# `execute_stream`

```elixir
@spec execute_stream(command(), args(), callback_fn(), keyword()) ::
  :ok | {:error, Snakepit.Error.t()}
```

Executes a streaming command with a callback function.

## Examples

    Snakepit.execute_stream("batch_inference", %{items: [...]}, fn chunk ->
      handle_chunk(chunk)
    end)

## Options

  * `:pool` - The pool to use (default: `Snakepit.Pool`)
  * `:timeout` - Request timeout in ms (default: 300000)
  * `:session_id` - Run in a specific session
  * `:affinity` - Override affinity mode (`:hint`, `:strict_queue`, `:strict_fail_fast`)

## Returns

Returns `:ok` on success or `{:error, %Snakepit.Error{}}` on failure.

Note: Streaming is only supported with gRPC adapters.

# `get_stats`

```elixir
@spec get_stats(pool_name()) :: map()
```

Get pool statistics.

Returns aggregate stats across all pools or stats for a specific pool.

# `list_workers`

```elixir
@spec list_workers(pool_name()) :: [String.t()]
```

List workers from the pool.

Returns a list of worker IDs.

# `run_as_script`

```elixir
@spec run_as_script(
  (-&gt; any()),
  keyword()
) :: any() | {:error, term()}
```

Starts the Snakepit application, executes a given function,
and ensures graceful shutdown.

This is the recommended way to use Snakepit for short-lived scripts or
Mix tasks to prevent orphaned processes.

It handles the full OTP application lifecycle (start, run, stop)
automatically.

## Examples

    # In a Mix task
    Snakepit.run_as_script(fn ->
      {:ok, result} = Snakepit.execute("my_command", %{data: "value"})
      handle_result(result)
    end)

    # For demos or scripts
    Snakepit.run_as_script(fn ->
      MyApp.run_load_test()
    end)

## Options

  * `:timeout` - Maximum time to wait for pool initialization (default: 15000ms)
  * `:shutdown_timeout` - Time to wait for supervisor shutdown confirmation (default: 15000ms)
  * `:cleanup_timeout` - Time to wait for worker process cleanup before forcing cleanup (default: 5000ms).
    When greater than zero, cleanup runs even if Snakepit was already started; set to 0 to skip cleanup.
    Cleanup is bounded; if it exceeds `cleanup_timeout + shutdown margin` the script continues.
  * `:restart` - Restart Snakepit if already started to apply script config (`:auto` | true | false)
  * `:await_pool` - Wait for pool readiness (default: `pooling_enabled` setting)
  * `:exit_mode` - Exit behavior (`:none` | `:halt` | `:stop` | `:auto`, default: `:none`).
    May also be set with `SNAKEPIT_SCRIPT_EXIT`.
  * `:stop_mode` - Stop behavior (`:if_started` | `:always` | `:never`, default: `:if_started`).
  * `:halt` - Legacy boolean for `System.halt/1` after cleanup (default: false,
    or set `SNAKEPIT_SCRIPT_HALT=true`). Ignored when `:exit_mode` is set.

## Returns

Returns the result of the provided function, or `{:error, reason}` if
the pool fails to initialize.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
