cpursley / walex

Postgres change events (CDC) in Elixir
MIT License
276 stars 14 forks source link

Failed to start server: ** (EXIT) :function_clause #61

Closed cpursley closed 1 month ago

cpursley commented 3 months ago

Issue on master:

2024-05-03 14:10:57.440 [notice] Application my_app exited: MyApp.Application.start(:normal, []) returned an error: shutdown: failed to start child: WalEx.Supervisor
    ** (EXIT) shutdown: failed to start child: WalEx.Replication.Supervisor
        ** (EXIT) shutdown: failed to start child: WalEx.Replication.Server
            ** (EXIT) :function_clause

Originally posted by @cpursley in https://github.com/cpursley/walex/issues/57#issuecomment-2093525400

Ping @DaemonSnake

DaemonSnake commented 3 months ago

I had this a few times when starting implementation, usually happens when a handle_result doesn't have a matching case. One way I found to debug was to clone postgrex locally, use {"postgrex", path: "../postgres"} and modify the replication_connection module with the following patch

 lib/postgrex/replication_connection.ex | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/lib/postgrex/replication_connection.ex b/lib/postgrex/replication_connection.ex
index 8fcde20..faccaf3 100644
--- a/lib/postgrex/replication_connection.ex
+++ b/lib/postgrex/replication_connection.ex
@@ -546,8 +546,18 @@ defmodule Postgrex.ReplicationConnection do
     end
   end

+  defp sapply(mod, fun, args) do
+    try do
+      apply(mod, fun, args)
+    rescue
+      error ->
+        Logger.error("REPLICATION_CONNECTION:" <> Exception.format(:error, error, __STACKTRACE__))
+        raise error
+    end
+  end
+
   defp handle(mod, fun, args, from, %{streaming: streaming} = s) do
-    case apply(mod, fun, args) do
+    case sapply(mod, fun, args) do
       {:noreply, mod_state} ->
         {:keep_state, %{s | state: {mod, mod_state}}}

with this change it give the full stacktrace and exception details for the :function_clause error

DaemonSnake commented 3 months ago

trying to reproduce locally but haven't found a way so far

DaemonSnake commented 3 months ago

otherwise doing this as the last handle_result:

  @impl true
  def handle_result(results, state) do
    raise "Unexpected result: #{inspect(results)} for state: #{inspect(state)}"
  end

should help find what case is causing the crash