• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / supavisor / 24423669880

14 Apr 2026 09:24PM UTC coverage: 79.177% (+0.3%) from 78.854%
24423669880

Pull #905

github

web-flow
Merge 3a3ec28db into c4da84611
Pull Request #905: perf: optimizations around auth

9 of 9 new or added lines in 3 files covered. (100.0%)

94 existing lines in 7 files now uncovered.

2502 of 3160 relevant lines covered (79.18%)

58353.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.5
/lib/supavisor/client_handler/error.ex
1
defmodule Supavisor.ClientHandler.Error do
2
  @moduledoc """
3
  Error handling and message formatting for ClientHandler.
4
  """
5

6
  alias Supavisor.{HandlerHelpers, Monitoring.Telem, Protocol.Server}
7

8
  require Supavisor.Protocol.PreparedStatements, as: PreparedStatements
9
  require Logger
10

11
  @type context :: :handshake | :authenticated
12

13
  @doc """
14
  Handles error by logging, sending appropriate error message to the client socket,
15
  recording telemetry failure, and returning gen_statem termination action.
16

17
  Context values:
18
  - `:handshake` - Error during connection setup/auth (records client_join(:fail) telemetry, no ReadyForQuery)
19
  - `:authenticated` - Error after successful auth (no telemetry, sends ReadyForQuery)
20
  """
21
  @spec terminate_with_error(map(), Exception.t(), context()) ::
22
          :gen_statem.handle_event_result()
23
  def terminate_with_error(data, exception, context)
24
      when context in [:handshake, :authenticated] do
25
    error_actions = process(exception, context)
371✔
26
    message = Map.get(error_actions, :error)
371✔
27
    log_message = Map.get(error_actions, :log_message)
371✔
28
    log_level = Map.get(error_actions, :log_level, :error)
371✔
29
    send_ready_for_query = Map.get(error_actions, :send_ready_for_query, false)
371✔
30

31
    if log_message do
371✔
32
      Logger.log(log_level, "ClientHandler: #{log_message}")
371✔
33
    end
34

35
    # Only send message if one exists (some errors like socket closed can't send)
36
    if message do
371✔
37
      if send_ready_for_query do
43✔
UNCOV
38
        HandlerHelpers.sock_send(data.sock, [message, Server.ready_for_query()])
×
39
      else
40
        HandlerHelpers.sock_send(data.sock, message)
43✔
41
      end
42
    end
43

44
    # Record telemetry failure only during handshake phase
45
    if context == :handshake do
371✔
46
      Telem.client_join(:fail, data.id)
55✔
47
    end
48

49
    {:stop, :normal}
50
  end
51

52
  @spec process(term(), term()) :: map()
53
  defp process(e, stage) when is_exception(e) do
54
    postgres_error = e.__struct__.postgres_error(e)
371✔
55

56
    error =
371✔
57
      case postgres_error do
58
        nil -> nil
328✔
59
        postgres_error -> Server.encode_error_message(postgres_error)
43✔
60
      end
61

62
    %{
371✔
63
      error: error,
64
      log_message: e.__struct__.log_message(e),
371✔
65
      log_level: e.__struct__.log_level(e),
371✔
66
      # It's very important for the protocol implementation that we send ReadyForQuery after
67
      # non-fatal errors in authenticated connections. In non authneticated connections, we should
68
      # close without sending ReadyForQuery
69
      send_ready_for_query:
70
        stage == :authenticated &&
55✔
71
          postgres_error && postgres_error["S"] not in ["PANIC", "FATAL"]
316✔
72
    }
73
  end
74

75
  defp process(error, context) do
UNCOV
76
    message = "Internal error (#{context}): #{inspect(error)}"
×
77

78
    %{
×
79
      error: Server.error_message("XX000", message),
80
      log_message: message
81
    }
82
  end
83
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc