diff --git a/lib/xandra/cluster.ex b/lib/xandra/cluster.ex index ac025ee9..5916301b 100644 --- a/lib/xandra/cluster.ex +++ b/lib/xandra/cluster.ex @@ -434,12 +434,7 @@ defmodule Xandra.Cluster do cluster, options, fn conn -> - try do - Xandra.execute(conn, batch, options_without_retry_strategy) - catch - :exit, {:noproc, _} -> - {:error, ConnectionError.new("execute", {:cluster, :pool_closed})} - end + Xandra.execute(conn, batch, options_without_retry_strategy) end ) end @@ -463,12 +458,7 @@ defmodule Xandra.Cluster do cluster, options, fn conn -> - try do - Xandra.execute(conn, query, params, options_without_retry_strategy) - catch - :exit, {:noproc, _} -> - {:error, ConnectionError.new("execute", {:cluster, :pool_closed})} - end + Xandra.execute(conn, query, params, options_without_retry_strategy) end ) end diff --git a/lib/xandra/connection.ex b/lib/xandra/connection.ex index 5f15b6f9..cc4e7dda 100644 --- a/lib/xandra/connection.ex +++ b/lib/xandra/connection.ex @@ -65,7 +65,7 @@ defmodule Xandra.Connection do telemetry_metadata = Keyword.fetch!(options, :telemetry_metadata) - case :gen_statem.call(conn_pid, {:checkout_state_for_next_request, req_alias}) do + case gen_statem_call_trapping_noproc(conn_pid, {:checkout_state_for_next_request, req_alias}) do {:ok, checked_out_state() = state} -> checked_out_state( protocol_module: protocol_module, @@ -159,7 +159,7 @@ defmodule Xandra.Connection do conn_pid = GenServer.whereis(conn) req_alias = Process.monitor(conn_pid, alias: :reply_demonitor) - case :gen_statem.call(conn_pid, {:checkout_state_for_next_request, req_alias}) do + case gen_statem_call_trapping_noproc(conn_pid, {:checkout_state_for_next_request, req_alias}) do {:ok, checked_out_state() = checked_out_state} -> checked_out_state( transport: %Transport{} = transport, @@ -226,6 +226,12 @@ defmodule Xandra.Connection do end end + defp gen_statem_call_trapping_noproc(pid, call) do + :gen_statem.call(pid, call) + catch + :exit, {:noproc, _} -> {:error, :no_connection_process} + end + defp hydrate_query(%Simple{} = simple, checked_out_state() = response, options) do %Simple{ simple diff --git a/lib/xandra/connection_error.ex b/lib/xandra/connection_error.ex index 89ea959d..98df71d8 100644 --- a/lib/xandra/connection_error.ex +++ b/lib/xandra/connection_error.ex @@ -67,6 +67,10 @@ defmodule Xandra.ConnectionError do "connection dropped in the middle of a request" end + defp format_reason(:no_connection_process) do + "the connection process doesn't exist" + end + defp format_reason({:connection_process_crashed, reason}) do "connection process crashed before sending a response with reason: #{inspect(reason)}" end diff --git a/test/integration/errors_test.exs b/test/integration/errors_test.exs index e2bc224e..e1314707 100644 --- a/test/integration/errors_test.exs +++ b/test/integration/errors_test.exs @@ -94,7 +94,8 @@ defmodule ErrorsTest do test "noproc errors are caught" do {:ok, cluster} = start_supervised(Xandra.Cluster.PoolMock) - assert {:error, %ConnectionError{action: "execute", reason: {:cluster, :pool_closed}}} = + assert {:error, + %ConnectionError{action: "check out connection", reason: :no_connection_process}} = Cluster.execute(cluster, "select * from system.peers") end end diff --git a/test/xandra_test.exs b/test/xandra_test.exs index ba8dd9ec..32062851 100644 --- a/test/xandra_test.exs +++ b/test/xandra_test.exs @@ -307,6 +307,22 @@ defmodule XandraTest do assert {:ok, prepared} = Xandra.prepare(conn, "SELECT * FROM system.local") assert {:ok, %Xandra.Page{}} = Xandra.execute(conn, prepared, []) end + + test "returns an error when the Connection process doesn't exist" do + dead_pid = dead_pid() + + assert {:error, + %ConnectionError{action: "check out connection", reason: :no_connection_process}} = + Xandra.prepare(dead_pid, "SELECT * FROM system.local") + + refute_receive {:DOWN, _ref, :process, _pid, :noproc}, 5 + + assert {:error, + %ConnectionError{action: "check out connection", reason: :no_connection_process}} = + Xandra.execute(dead_pid, "SELECT * FROM system.local") + + refute_receive {:DOWN, _ref, :process, _pid, :noproc}, 5 + end end describe "failure handling" do @@ -390,4 +406,12 @@ defmodule XandraTest do send(pid, {ref, options}) Keyword.replace!(options, :nodes, original_start_options[:nodes]) end + + defp dead_pid do + {pid, ref} = spawn_monitor(fn -> :ok end) + + receive do + {:DOWN, ^ref, _, _, _} -> pid + end + end end