Repository: general-CbIC/poolex (elixir)

Issue:
Provide new idle workers to waiting callers after extending pool size
Sourced from Elixir Forum: [link](https://elixirforum.com/t/poolex-a-library-for-managing-pools-of-workers/54800/22?u=general-cbic)

> Consider this situation: we have a pool, all workers are busy and there is a queue of waiting callers. Then pool receives a call to create more idle workers, but these workers won’t be returned to waiting callers.

Pull request description:
New workers providing to waiting callers
The function `add_idle_workers!/2` now provides new workers to waiting_callers if they exist.

Closes #122 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ba78b1d..9f0da2f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Fixed
 
 - Now the `busy_worker` will be restarted after the `caller's` death. Read more in [issue](https://github.com/general-CbIC/poolex/issues/114).
+- The function `add_idle_workers!/2` now provides new workers to waiting callers if they exist. Read more in [issue](https://github.com/general-CbIC/poolex/issues/122).
 
 ## [1.2.0] - 2025-01-07
 
diff --git a/lib/poolex.ex b/lib/poolex.ex
index 90a903d..70ebf75 100644
--- a/lib/poolex.ex
+++ b/lib/poolex.ex
@@ -413,7 +413,14 @@ defmodule Poolex do
 
     state =
       Enum.reduce(workers, state, fn worker, acc_state ->
-        IdleWorkers.add(acc_state, worker)
+        if WaitingCallers.empty?(acc_state) do
+          IdleWorkers.add(acc_state, worker)
+        else
+          acc_state
+          |> Monitoring.add(worker, :worker)
+          |> BusyWorkers.add(worker)
+          |> provide_worker_to_waiting_caller(worker)
+        end
       end)
 
     {:reply, :ok, state}


Test patch:
diff --git a/test/poolex_test.exs b/test/poolex_test.exs
index 389714b..eb7da0f 100644
--- a/test/poolex_test.exs
+++ b/test/poolex_test.exs
@@ -284,6 +284,7 @@ defmodule PoolexTest do
       assert debug_info.idle_workers_count == 2
 
       # Busy worker should be restarted if caller dies
+      # NOTE: may be I should write a test using :do_some_work_with_delay
       refute Enum.any?(debug_info.idle_workers_pids, fn pid ->
                pid == busy_worker_pid
              end)
@@ -638,6 +639,39 @@ defmodule PoolexTest do
       assert %DebugInfo{idle_workers_count: 10} = Poolex.get_debug_info(pool_name)
     end
 
+    test "provides new workers to waiting callers", %{pool_options: pool_options} do
+      pool_name = pool_options |> Keyword.put(:workers_count, 0) |> start_pool()
+
+      test_process = self()
+
+      spawn(fn ->
+        Process.send(test_process, nil, [])
+
+        Poolex.run(pool_name, fn _pid ->
+          Process.send(test_process, :started_work, [])
+          :timer.sleep(:timer.seconds(5))
+        end)
+      end)
+
+      receive do
+        _message -> nil
+      end
+
+      debug_info = Poolex.get_debug_info(pool_name)
+      assert debug_info.busy_workers_count == 0
+      assert debug_info.idle_workers_count == 0
+      assert Enum.count(debug_info.waiting_callers) == 1
+      refute_received :started_work
+
+      assert :ok = Poolex.add_idle_workers!(pool_name, 1)
+
+      debug_info = Poolex.get_debug_info(pool_name)
+      assert debug_info.busy_workers_count == 1
+      assert debug_info.idle_workers_count == 0
+      assert Enum.empty?(debug_info.waiting_callers)
+      assert_receive :started_work, 1000
+    end
+
     test "raises error on non positive workers_count", %{pool_options: pool_options} do
       pool_name = start_pool(pool_options)