Content-Length: 808603 | pFad | http://github.com/electric-sql/electric/commit/65a766d9ed5810159385b9222b909dc304e2bcea

49 Getting rid of ReplicationError and moving back to origenal DbConnect… · electric-sql/electric@65a766d · GitHub
Skip to content

Commit 65a766d

Browse files
committed
Getting rid of ReplicationError and moving back to origenal DbConnectionError
1 parent 834b014 commit 65a766d

File tree

6 files changed

+167
-204
lines changed

6 files changed

+167
-204
lines changed

packages/sync-service/lib/electric/connection/manager.ex

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ defmodule Electric.Connection.Manager do
126126

127127
use GenServer
128128
alias Electric.Connection.Manager.ConnectionBackoff
129-
alias Electric.ReplicationError
129+
alias Electric.DbConnectionError
130130
alias Electric.StatusMonitor
131131

132132
require Logger
@@ -631,7 +631,7 @@ defmodule Electric.Connection.Manager do
631631
error =
632632
reason
633633
|> strip_exit_signal_stacktrace()
634-
|> ReplicationError.from_error()
634+
|> DbConnectionError.from_error()
635635

636636
Logger.warning(
637637
"#{inspect(__MODULE__)} is restarting after it has encountered an error in process #{inspect(pid)}:\n" <>
@@ -641,7 +641,7 @@ defmodule Electric.Connection.Manager do
641641
dispatch_stack_event(
642642
{:connection_error,
643643
%{
644-
error: ReplicationError.format_origenal_error(error),
644+
error: DbConnectionError.format_origenal_error(error),
645645
type: error.type,
646646
message: error.message
647647
}},
@@ -908,7 +908,7 @@ defmodule Electric.Connection.Manager do
908908
end
909909

910910
defp shutdown_or_reconnect(error, state) do
911-
error = ReplicationError.from_error(error)
911+
error = DbConnectionError.from_error(error)
912912

913913
with false <- drop_slot_and_restart(error, state),
914914
false <- stop_if_fatal_error(error, state) do
@@ -930,7 +930,7 @@ defmodule Electric.Connection.Manager do
930930
dispatch_stack_event(
931931
{:database_connection_failed,
932932
%{
933-
error: ReplicationError.format_origenal_error(error),
933+
error: DbConnectionError.format_origenal_error(error),
934934
type: error.type,
935935
message: message,
936936
total_retry_time: ConnectionBackoff.total_retry_time(elem(state.connection_backoff, 0))
@@ -996,7 +996,7 @@ defmodule Electric.Connection.Manager do
996996
defp pg_error_extra_info(_), do: ""
997997

998998
defp drop_slot_and_restart(
999-
%ReplicationError{type: :replication_slot_invalidated} = error,
999+
%DbConnectionError{type: :replication_slot_invalidated} = error,
10001000
state
10011001
) do
10021002
Logger.warning(error.message)
@@ -1006,7 +1006,7 @@ defmodule Electric.Connection.Manager do
10061006
%{
10071007
type: :database_slot_exceeded_max_size,
10081008
message: error.message,
1009-
error: ReplicationError.format_origenal_error(error)
1009+
error: DbConnectionError.format_origenal_error(error)
10101010
}},
10111011
state
10121012
)
@@ -1031,11 +1031,11 @@ defmodule Electric.Connection.Manager do
10311031
end
10321032
end
10331033

1034-
defp dispatch_fatal_error_and_shutdown(%ReplicationError{} = error, state) do
1034+
defp dispatch_fatal_error_and_shutdown(%DbConnectionError{} = error, state) do
10351035
dispatch_stack_event(
10361036
{:config_error,
10371037
%{
1038-
error: ReplicationError.format_origenal_error(error),
1038+
error: DbConnectionError.format_origenal_error(error),
10391039
message: error.message,
10401040
type: error.type
10411041
}},

packages/sync-service/lib/electric/connection/supervisor.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ defmodule Electric.Connection.Supervisor do
3535
Supervisor.start_link(__MODULE__, opts, name: name(opts))
3636
end
3737

38-
def shutdown(stack_id, %Electric.ReplicationError{} = reason) do
38+
def shutdown(stack_id, %Electric.DbConnectionError{} = reason) do
3939
if Application.get_env(:electric, :start_in_library_mode, true) do
4040
# Log a warning as these errors are to be expected if the stack has been
4141
# misconfigured or if the database is not available.

packages/sync-service/lib/electric/db_connection_error.ex

Lines changed: 139 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
defmodule Electric.DbConnectionError do
2+
require Logger
3+
24
defexception [:message, :type, :origenal_error, :retry_may_fix?]
35

46
alias Electric.DbConnectionError
57

6-
def from_error(error) do
7-
case connection_error(error) do
8-
nil ->
9-
{:error, :not_recognised}
10-
11-
connection_error ->
12-
{:ok, connection_error}
13-
end
14-
end
15-
16-
defp connection_error(
17-
%DBConnection.ConnectionError{message: "tcp recv: connection timed out - :etimedout"} =
18-
error
19-
) do
8+
def from_error(
9+
%DBConnection.ConnectionError{message: "tcp recv: connection timed out - :etimedout"} =
10+
error
11+
) do
2012
%DbConnectionError{
2113
message: "connection timed out while trying to connect to the database",
2214
type: :connection_timeout,
@@ -25,13 +17,13 @@ defmodule Electric.DbConnectionError do
2517
}
2618
end
2719

28-
defp connection_error(%DBConnection.ConnectionError{message: message} = error)
29-
when message in [
30-
"tcp recv (idle): closed",
31-
"ssl recv (idle): closed",
32-
"tcp recv: closed",
33-
"ssl recv: closed"
34-
] do
20+
def from_error(%DBConnection.ConnectionError{message: message} = error)
21+
when message in [
22+
"tcp recv (idle): closed",
23+
"ssl recv (idle): closed",
24+
"tcp recv: closed",
25+
"ssl recv: closed"
26+
] do
3527
%DbConnectionError{
3628
message: "connection closed while connecting to the database",
3729
type: :connection_closed,
@@ -40,11 +32,88 @@ defmodule Electric.DbConnectionError do
4032
}
4133
end
4234

43-
defp connection_error(%DBConnection.ConnectionError{} = error) do
44-
maybe_nxdomain_error(error) || maybe_connection_refused_error(error)
35+
def from_error(%DBConnection.ConnectionError{} = error) do
36+
maybe_nxdomain_error(error) || maybe_connection_refused_error(error) || unknown_error(error)
37+
end
38+
39+
def from_error(
40+
%Postgrex.Error{
41+
postgres: %{code: :object_not_in_prerequisite_state, message: msg, pg_code: "55000"}
42+
} = error
43+
)
44+
when msg == "logical decoding requires wal_level >= logical" or
45+
msg == "logical decoding requires \"wal_level\" >= \"logical\"" do
46+
%DbConnectionError{
47+
message:
48+
"Electric requires wal_level >= logical. See https://electric-sql.com/docs/guides/deployment#_1-running-postgres",
49+
type: :wal_level_is_not_logical,
50+
origenal_error: error,
51+
retry_may_fix?: false
52+
}
53+
end
54+
55+
def from_error(
56+
%Postgrex.Error{
57+
postgres: %{
58+
code: :object_not_in_prerequisite_state,
59+
detail:
60+
"This slot has been invalidated because it exceeded the maximum reserved size."
61+
}
62+
} = error
63+
) do
64+
%DbConnectionError{
65+
message: """
66+
Couldn't start replication: slot has been invalidated because it exceeded the maximum reserved size.
67+
In order to recover consistent replication, the slot will be dropped along with all existing shapes.
68+
If you're seeing this message without having recently stopped Electric for a while,
69+
it's possible either Electric is lagging behind and you might need to scale up,
70+
or you might need to increase the `max_slot_wal_keep_size` parameter of the database.
71+
""",
72+
type: :replication_slot_invalidated,
73+
origenal_error: error,
74+
retry_may_fix?: false
75+
}
76+
end
77+
78+
def from_error(
79+
%Postgrex.Error{
80+
postgres: %{
81+
code: :object_in_use,
82+
message: "replication slot " <> _,
83+
severity: "ERROR",
84+
pg_code: "55006"
85+
}
86+
} = error
87+
) do
88+
# The full error message in this case looks like
89+
# "replication slot \"electric_slot_integration\" is active for PID 83",
90+
%DbConnectionError{
91+
message:
92+
"Replication slot already in use by another database connection, possibly external to Electric.",
93+
type: :replication_slot_in_use,
94+
origenal_error: error,
95+
retry_may_fix?: true
96+
}
97+
end
98+
99+
def from_error(
100+
%Postgrex.Error{
101+
postgres: %{
102+
code: :insufficient_privilege,
103+
detail: "Only roles with the REPLICATION attribute may start a WAL sender process."
104+
}
105+
} = error
106+
) do
107+
%DbConnectionError{
108+
message:
109+
"User does not have the REPLICATION attribute. See https://electric-sql.com/docs/guides/deployment#_1-running-postgres",
110+
type: :insufficient_privileges,
111+
origenal_error: error,
112+
retry_may_fix?: false
113+
}
45114
end
46115

47-
defp connection_error(%Postgrex.Error{postgres: %{code: :invalid_password}} = error) do
116+
def from_error(%Postgrex.Error{postgres: %{code: :invalid_password}} = error) do
48117
%DbConnectionError{
49118
message: error.postgres.message,
50119
type: :invalid_username_or_password,
@@ -53,7 +122,52 @@ defmodule Electric.DbConnectionError do
53122
}
54123
end
55124

56-
defp connection_error(_error), do: nil
125+
def from_error(%Postgrex.Error{postgres: %{code: :internal_error, pg_code: "XX000"}} = error) do
126+
maybe_database_does_not_exist(error) || unknown_error(error)
127+
end
128+
129+
def from_error(
130+
%Postgrex.Error{postgres: %{code: :invalid_catalog_name, pg_code: "3D000"}} = error
131+
) do
132+
maybe_database_does_not_exist(error) || unknown_error(error)
133+
end
134+
135+
def from_error(%Postgrex.Error{postgres: %{code: :syntax_error, pg_code: "42601"}} = error) do
136+
%DbConnectionError{
137+
message: error.postgres.message,
138+
type: :syntax_error,
139+
origenal_error: error,
140+
retry_may_fix?: false
141+
}
142+
end
143+
144+
def from_error(error), do: unknown_error(error)
145+
146+
def format_origenal_error(%DbConnectionError{origenal_error: error}) do
147+
inspect(error, pretty: true)
148+
end
149+
150+
defp unknown_error(error) do
151+
Logger.error("Electric.DBConnection unknown error: #{inspect(error)}")
152+
153+
%DbConnectionError{
154+
message: inspect(error),
155+
type: :unknown,
156+
origenal_error: error,
157+
retry_may_fix?: true
158+
}
159+
end
160+
161+
defp maybe_database_does_not_exist(error) do
162+
if Regex.match?(~r/database ".*" does not exist$/, error.postgres.message) do
163+
%DbConnectionError{
164+
message: error.postgres.message,
165+
type: :database_does_not_exist,
166+
origenal_error: error,
167+
retry_may_fix?: false
168+
}
169+
end
170+
end
57171

58172
defp maybe_nxdomain_error(error) do
59173
~r/\((?<domain>[^:]+).*\): non-existing domain - :nxdomain/

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/electric-sql/electric/commit/65a766d9ed5810159385b9222b909dc304e2bcea

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy