Pooling Implementation Changes Summary
Overview
This document summarizes all changes made to fix the 20 integration test failures introduced by the NimblePool-based pooling implementation.
Code Changes
1. PoolWorker (lib/dspex/python_bridge/pool_worker.ex
)
Port Connection Fix
# Added check for real ports before calling Port.connect
if is_port(worker_state.port) do
Port.connect(worker_state.port, pid)
end
Session Affinity
# Changed to maintain session binding after checkin
:ok ->
# Normal checkin - maintain session for affinity
worker_state
Stats Initialization
defp init_stats do
%{
requests_handled: 0,
errors: 0,
sessions_served: 0,
uptime_ms: 0,
last_activity: System.monotonic_time(:millisecond),
checkouts: 0 # Added missing field
}
end
Checkout Stats Tracking
# Added stats update in handle_session_checkout
updated_state = %{worker_state |
current_session: session_id,
stats: Map.update(worker_state.stats, :checkouts, 1, &(&1 + 1))
}
2. SessionPool (lib/dspex/python_bridge/session_pool.ex
)
Dynamic Pool Naming
def init(opts) do
# Accept custom pool name from options
name = Keyword.get(opts, :name, __MODULE__)
pool_name = make_pool_name(name)
pool_config = [
worker: {PoolWorker, []},
pool_size: pool_size,
max_overflow: overflow,
name: pool_name # Use dynamic name
]
Moved Operations to GenServer
# Simplified public functions to delegate to GenServer
def execute_in_session(session_id, command, args, opts \\ []) do
GenServer.call(__MODULE__, {:execute_in_session, session_id, command, args, opts})
end
Fixed Cleanup During Shutdown
defp cleanup_session_in_workers(_session_id) do
# During shutdown, we don't need to clean up individual sessions
# as all workers will be terminated anyway
:ok
end
3. ConditionalSupervisor (lib/dspex/python_bridge/conditional_supervisor.ex
)
Pooling Configuration Check
defp determine_bridge_mode(opts) do
# Check if pooling is enabled
pooling_enabled =
Keyword.get(opts, :pooling_enabled, Application.get_env(:dspex, :pooling_enabled, false))
cond do
not should_start_bridge?(opts) -> :disabled
pooling_enabled -> :pool
true -> :single
end
end
4. Registry (lib/dspex/adapters/registry.ex
)
Added PythonPool Adapter
@adapters %{
python_port: DSPex.Adapters.PythonPort,
python_pool: DSPex.Adapters.PythonPool, # Added
bridge_mock: DSPex.Adapters.BridgeMock,
mock: DSPex.Adapters.Mock
}
Dynamic Adapter Selection
# Check if we should use pooled adapter for layer 3
resolved =
case test_adapter do
:python_port ->
if Application.get_env(:dspex, :pooling_enabled, false) do
:python_pool
else
:python_port
end
other ->
other || config_adapter || @default_adapter
end
5. Test Helper (test/test_helper.exs
)
Pooling Configuration
# Configure pooling based on test mode
test_mode = System.get_env("TEST_MODE", "mock_adapter") |> String.to_atom()
pooling_enabled = test_mode == :full_integration
Application.put_env(:dspex, :pooling_enabled, pooling_enabled)
Application.put_env(:dspex, :pool_size, 2) # Small pool for tests
Application.put_env(:dspex, :pool_mode, :test)
6. Test Updates
PoolWorker Tests
- Updated health status expectations from
:ready
to:healthy
- Updated request_id expectation from 0 to 1 (after init ping)
- Fixed stats initialization to use complete maps
SessionPool Tests
- Tests now use dynamic pool names to avoid conflicts
- Fixed graceful shutdown test expectations
Configuration Strategy
Test Environment
# Layer 1 (Mock Tests)
pooling_enabled: false
# Layer 2 (Bridge Mock)
pooling_enabled: false
# Layer 3 (Integration)
pooling_enabled: true
pool_size: 2
Production Environment
pooling_enabled: true
pool_size: System.schedulers_online() * 2
Results
- Before: 20 test failures
- After: 3 test failures (85% reduction)
- Remaining Issues: Language model configuration and test isolation