Skip to content

Commit 1217862

Browse files
add more cases
1 parent 30f019a commit 1217862

File tree

2 files changed

+87
-36
lines changed

2 files changed

+87
-36
lines changed

lib/postgrex/protocol.ex

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,7 @@ defmodule Postgrex.Protocol do
376376
parse_describe_flush(s, status, query, comment)
377377
end
378378

379-
with {:disconnect, %{reason: :closed} = err, s} <- result do
380-
{:disconnect_and_retry, err, s}
381-
end
379+
handle_disconnect_retry(result)
382380
end
383381

384382
def handle_prepare(%Query{} = query, opts, %{queries: nil} = s) do
@@ -401,14 +399,8 @@ defmodule Postgrex.Protocol do
401399
end
402400

403401
case result do
404-
{:ok, query, s} ->
405-
{:ok, query, %{s | messages: []}}
406-
407-
{:disconnect, %{reason: :closed} = err, s} ->
408-
{:disconnect_and_retry, err, s}
409-
410-
other ->
411-
other
402+
{:ok, query, s} -> {:ok, query, %{s | messages: []}}
403+
other -> handle_disconnect_retry(other)
412404
end
413405
end
414406
end
@@ -434,11 +426,15 @@ defmodule Postgrex.Protocol do
434426
| {:error, %ArgumentError{} | Postgrex.Error.t(), state}
435427
| {:error, %DBConnection.TransactionError{}, state}
436428
| {:disconnect, %RuntimeError{}, state}
437-
| {:disconnect, %DBConnection.ConnectionError{}, state}
429+
| {:disconnect | :disconnect_and_retry, %DBConnection.ConnectionError{}, state}
438430
def handle_execute(%Query{} = query, params, opts, s) do
439431
case Keyword.get(opts, :postgrex_copy, false) do
440-
true -> handle_execute_copy(query, params, opts, s)
441-
false -> handle_execute_result(query, params, opts, s)
432+
true ->
433+
handle_execute_copy(query, params, opts, s)
434+
435+
false ->
436+
result = handle_execute_result(query, params, opts, s)
437+
handle_disconnect_retry(result)
442438
end
443439
end
444440

@@ -515,17 +511,19 @@ defmodule Postgrex.Protocol do
515511
{:ok, Postgrex.Result.t(), state}
516512
| {:error, %ArgumentError{} | Postgrex.Error.t(), state}
517513
| {:disconnect, %RuntimeError{}, state}
518-
| {:disconnect, %DBConnection.ConnectionError{}, state}
514+
| {:disconnect | :disconnect_and_retry, %DBConnection.ConnectionError{}, state}
519515
def handle_close(%Query{ref: ref} = query, opts, %{postgres: {_, ref}} = s) do
520-
flushed_close(s, new_status(opts), query)
516+
result = flushed_close(s, new_status(opts), query)
517+
handle_disconnect_retry(result)
521518
end
522519

523520
def handle_close(%Query{} = query, _, %{postgres: {_, _}} = s) do
524521
lock_error(s, :close, query)
525522
end
526523

527524
def handle_close(%Query{} = query, opts, s) do
528-
close(s, new_status(opts), query)
525+
result = close(s, new_status(opts), query)
526+
handle_disconnect_retry(result)
529527
end
530528

531529
@impl true
@@ -604,11 +602,8 @@ defmodule Postgrex.Protocol do
604602
case Keyword.get(opts, :mode, :transaction) do
605603
:transaction when postgres == :idle ->
606604
statement = "BEGIN"
607-
608-
with {:disconnect, %{reason: :closed} = err, s} <-
609-
handle_transaction(statement, opts, s) do
610-
{:disconnect_and_retry, err, s}
611-
end
605+
result = handle_transaction(statement, opts, s)
606+
handle_disconnect_retry(result)
612607

613608
:savepoint when postgres == :transaction ->
614609
statement = "SAVEPOINT postgrex_savepoint"
@@ -2098,7 +2093,7 @@ defmodule Postgrex.Protocol do
20982093
bind_execute_close(s, status, query, params)
20992094

21002095
{error, _, _} = other when error in [:error, :disconnect] ->
2101-
other
2096+
handle_disconnect_retry(other)
21022097
end
21032098
end
21042099

@@ -2110,7 +2105,7 @@ defmodule Postgrex.Protocol do
21102105
bind_execute(s, status, query, params)
21112106

21122107
{error, _, _} = other when error in [:error, :disconnect] ->
2113-
other
2108+
handle_disconnect_retry(other)
21142109
end
21152110
end
21162111

@@ -2322,7 +2317,9 @@ defmodule Postgrex.Protocol do
23222317
recv_execute(s, status, query, rows, buffer)
23232318

23242319
{:disconnect, _, _} = dis ->
2325-
dis
2320+
with {_, %{reason: :closed} = err, s} <- dis do
2321+
{:disconnect, %{err | reason: :execute_closed}, s}
2322+
end
23262323
end
23272324
end
23282325

@@ -3443,6 +3440,14 @@ defmodule Postgrex.Protocol do
34433440
{:disconnect, err, %{s | buffer: buffer}}
34443441
end
34453442

3443+
defp handle_disconnect_retry({:disconnect, %{reason: :closed} = err, s}),
3444+
do: {:disconnect_and_retry, err, s}
3445+
3446+
defp handle_disconnect_retry({:disconnect, err, s}),
3447+
do: {:disconnect, err, s}
3448+
3449+
defp handle_disconnect_retry(other), do: other
3450+
34463451
defp sync_recv(s, status, buffer) do
34473452
%{postgres: postgres, transactions: transactions} = s
34483453

test/query_test.exs

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,23 +1966,69 @@ defmodule QueryTest do
19661966
end
19671967
end
19681968

1969-
test "disconnect_and_retry", context do
1969+
test "disconnect_and_retry with prepare" do
19701970
# Start new connection so we can retry on disconnect
19711971
opts = [database: "postgrex_test", backoff_min: 1, backoff_max: 1]
19721972
{:ok, pid} = P.start_link(opts)
19731973

1974-
# Have to get socket by knowing DBConnection specifics
1975-
fun = fn conn ->
1976-
{:pool_ref, _, _, _, holder, _} = conn.pool_ref
1977-
[{:conn, _, _, state, _, _, _, _}] = :ets.lookup(holder, :conn)
1978-
{:gen_tcp, sock} = state.sock
1979-
sock
1980-
end
1974+
# Drop socket
1975+
disconnect(pid)
1976+
1977+
# Assert preparation happens instead of returning error
1978+
assert {:ok, _} = P.prepare(pid, "42", "SELECT 42")
1979+
end
1980+
1981+
test "disconnect_and_retry with transaction" do
1982+
# Start new connection so we can retry on disconnect
1983+
opts = [database: "postgrex_test", backoff_min: 1, backoff_max: 1]
1984+
{:ok, pid} = P.start_link(opts)
1985+
1986+
# Drop socket
1987+
disconnect(pid)
1988+
1989+
# Assert transaction happens instead of returning error
1990+
assert {:ok, _} = P.transaction(pid, fn conn -> P.query(conn, "SELECT 1", []) end)
1991+
end
1992+
1993+
test "disconnect_and_retry with closing prepared statement" do
1994+
# Start new connection so we can retry on disconnect
1995+
opts = [database: "postgrex_test", backoff_min: 1, backoff_max: 1]
1996+
{:ok, pid} = P.start_link(opts)
19811997

1982-
sock = DBConnection.run(pid, fun)
1998+
# Prepare query that we wil try to close after disconnecting
1999+
{:ok, query} = P.prepare(pid, "42", "SELECT 42")
2000+
2001+
# Drop socket
2002+
disconnect(pid)
2003+
2004+
# Assert close happens instead of returning error
2005+
assert :ok = P.close(pid, query)
2006+
end
2007+
2008+
test "disconnect_and_retry on attempting execution of prepared statement" do
2009+
# Start new connection so we can retry on disconnect
2010+
opts = [database: "postgrex_test", backoff_min: 1, backoff_max: 1]
2011+
{:ok, pid} = P.start_link(opts)
2012+
2013+
# Prepare query that we wil try to close after disconnecting
2014+
{:ok, query} = P.prepare(pid, "42", "SELECT 42")
2015+
2016+
# Drop socket
2017+
disconnect(pid)
2018+
2019+
# Assert execute happens instead of returning error
2020+
assert {:ok, _, _} = P.execute(pid, query, [])
2021+
end
2022+
2023+
defp disconnect(pid) do
2024+
sock = DBConnection.run(pid, &get_socket/1)
19832025
:gen_tcp.shutdown(sock, :read_write)
2026+
end
19842027

1985-
# Assert preparation happens instead of returning error
1986-
assert {:ok, %Postgrex.Query{}} = P.prepare(pid, "42", "SELECT 42")
2028+
defp get_socket(conn) do
2029+
{:pool_ref, _, _, _, holder, _} = conn.pool_ref
2030+
[{:conn, _, _, state, _, _, _, _}] = :ets.lookup(holder, :conn)
2031+
{:gen_tcp, sock} = state.sock
2032+
sock
19872033
end
19882034
end

0 commit comments

Comments
 (0)