V2 Pool Remaining Fixes
Issue Analysis
The fixes implemented so far have addressed the basic infrastructure issues, but we’re still seeing failures due to:
- Task.async Process Death: The concurrent test creates tasks using
Task.async
which complete quickly, causing the process to die before the pool can connect the port - Pool Name Resolution: The adapter tests are failing because the pool name isn’t being resolved correctly
- Worker Initialization Timing: Workers are taking too long to initialize, causing timeouts
Critical Fix: Task.await_many Pattern
The test uses Task.async
and Task.await_many
which creates short-lived processes:
tasks = for i <- 1..5 do
Task.async(fn ->
# This process dies as soon as the function returns!
result = SessionPoolV2.execute_in_session(...)
{i, result, duration}
end)
end
# By the time NimblePool tries to connect the port,
# the Task process is already dead
results = Task.await_many(tasks, 10_000)
Solution 1: Use Longer-Lived Processes
Instead of Task.async
, we need processes that stay alive during the checkout:
# Use spawn_link with receive block
tasks = for i <- 1..5 do
parent = self()
spawn_link(fn ->
result = SessionPoolV2.execute_in_session(...)
send(parent, {:result, i, result})
# Keep process alive until told to exit
receive do
:exit -> :ok
end
end)
end
# Collect results
results = for _ <- 1..5 do
receive do
{:result, i, result} -> {i, result}
after
10_000 -> {:error, :timeout}
end
end
# Clean up processes
for task <- tasks, do: send(task, :exit)
Solution 2: Fix execute_in_session to Handle Sync Calls
The real issue is that execute_in_session
is doing async work but being called from a sync context. We need to ensure the checkout happens in the calling process:
# In session_pool_v2.ex
def execute_in_session(session_id, command, args, opts \\ []) do
pool_name = get_pool_name(opts)
# This checkout MUST happen in the calling process
# NOT in a Task or spawned process
NimblePool.checkout!(
pool_name,
{:session, session_id},
fn _from, worker_state ->
# Port is now connected to THIS process
# which is the test process, not a Task
result = execute_command(worker_state, command, args)
{result, :ok}
end,
opts
)
end
Fix Implementation Plan
- Update concurrent test to use proper process management
- Fix pool name resolution in adapter
- Add process lifetime management helpers
- Ensure checkout happens in calling process
Test Pattern for Concurrent Operations
test "concurrent operations with proper process management" do
# Create a process supervisor for test processes
{:ok, task_sup} = Task.Supervisor.start_link()
# Use Task.Supervisor which keeps processes alive
tasks = for i <- 1..5 do
Task.Supervisor.async(task_sup, fn ->
SessionPoolV2.execute_in_session(...)
end)
end
# This ensures processes stay alive during checkout
results = Task.await_many(tasks, 30_000)
# Cleanup
Supervisor.stop(task_sup)
end
Pool Name Resolution Fix
The adapter is trying to use a pool that doesn’t exist. We need to ensure the pool name is correctly resolved:
# In python_pool_v2.ex
def with_pool_name(pool_name) do
%{
configure_lm: fn config ->
# Ensure we're using the right pool
actual_pool = SessionPoolV2.get_pool_name_for(pool_name)
# ... rest of implementation
end
}
end