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

supabase / realtime / c5c8f617a3ad63a2c3506a1b61ecd513ebad1ed3

07 May 2025 08:44PM UTC coverage: 82.461% (+0.4%) from 82.079%
c5c8f617a3ad63a2c3506a1b61ecd513ebad1ed3

push

github

web-flow
fix: remove region from syn conflict handling & non found process on register_process (#1363)

7 of 10 new or added lines in 4 files covered. (70.0%)

8 existing lines in 3 files now uncovered.

1749 of 2121 relevant lines covered (82.46%)

1349.51 hits per line

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

32.84
/lib/realtime/nodes.ex
1
defmodule Realtime.Nodes do
2
  @moduledoc """
3
  Handles common needs for :syn module operations
4
  """
5
  require Logger
6
  alias Realtime.Api.Tenant
7

8
  @doc """
9
  Gets the node to launch the Postgres connection on for a tenant.
10
  """
11
  @spec get_node_for_tenant(Tenant.t()) :: {:ok, node()} | {:error, term()}
12
  def get_node_for_tenant(nil), do: {:error, :tenant_not_found}
2✔
13

14
  def get_node_for_tenant(%Tenant{extensions: extensions, external_id: tenant_id}) do
15
    with region <- get_region(extensions),
101✔
16
         tenant_region <- platform_region_translator(region),
101✔
17
         node <- launch_node(tenant_id, tenant_region, node()) do
101✔
18
      {:ok, node}
19
    end
20
  end
21

22
  defp get_region(extensions) do
23
    extensions
24
    |> Enum.map(fn %{settings: %{"region" => region}} -> region end)
101✔
25
    |> Enum.uniq()
26
    |> hd()
101✔
27
  end
28

29
  @doc """
30
  Translates a region from a platform to the closest Supabase tenant region
31
  """
32
  @spec platform_region_translator(String.t() | nil) :: nil | binary()
NEW
33
  def platform_region_translator(nil), do: nil
×
34

35
  def platform_region_translator(tenant_region) when is_binary(tenant_region) do
36
    platform = Application.get_env(:realtime, :platform)
106✔
37
    region_mapping(platform, tenant_region)
106✔
38
  end
39

40
  defp region_mapping(:aws, tenant_region) do
41
    case tenant_region do
×
42
      "ap-east-1" -> "ap-southeast-1"
×
43
      "ap-northeast-1" -> "ap-southeast-1"
×
44
      "ap-northeast-2" -> "ap-southeast-1"
×
45
      "ap-south-1" -> "ap-southeast-1"
×
46
      "ap-southeast-1" -> "ap-southeast-1"
×
47
      "ap-southeast-2" -> "ap-southeast-2"
×
48
      "ca-central-1" -> "us-east-1"
×
49
      "eu-central-1" -> "eu-west-2"
×
50
      "eu-central-2" -> "eu-west-2"
×
51
      "eu-north-1" -> "eu-west-2"
×
52
      "eu-west-1" -> "eu-west-2"
×
53
      "eu-west-2" -> "eu-west-2"
×
54
      "eu-west-3" -> "eu-west-2"
×
55
      "sa-east-1" -> "us-east-1"
×
56
      "us-east-1" -> "us-east-1"
×
57
      "us-east-2" -> "us-east-1"
×
58
      "us-west-1" -> "us-west-1"
×
59
      "us-west-2" -> "us-west-1"
×
60
      _ -> nil
×
61
    end
62
  end
63

64
  defp region_mapping(:fly, tenant_region) do
65
    case tenant_region do
×
66
      "us-east-1" -> "iad"
×
67
      "us-west-1" -> "sea"
×
68
      "sa-east-1" -> "iad"
×
69
      "ca-central-1" -> "iad"
×
70
      "ap-southeast-1" -> "syd"
×
71
      "ap-northeast-1" -> "syd"
×
72
      "ap-northeast-2" -> "syd"
×
73
      "ap-southeast-2" -> "syd"
×
74
      "ap-east-1" -> "syd"
×
75
      "ap-south-1" -> "syd"
×
76
      "eu-west-1" -> "lhr"
×
77
      "eu-west-2" -> "lhr"
×
78
      "eu-west-3" -> "lhr"
×
79
      "eu-central-1" -> "lhr"
×
80
      _ -> nil
×
81
    end
82
  end
83

84
  defp region_mapping(_, tenant_region), do: tenant_region
106✔
85

86
  @doc """
87
  Lists the nodes in a region. Sorts by node name in case the list order
88
  is unstable.
89
  """
90

91
  @spec region_nodes(String.t() | nil) :: [atom()]
92
  def region_nodes(region) when is_binary(region) do
93
    :syn.members(RegionNodes, region)
94
    |> Enum.map(fn {_pid, [node: node]} -> node end)
3✔
95
    |> Enum.sort()
106✔
96
  end
97

98
  def region_nodes(nil), do: []
×
99

100
  @doc """
101
  Picks the node to launch the Postgres connection on.
102

103
  If there are not two nodes in a region the connection is established from
104
  the `default` node given.
105
  """
106
  @spec launch_node(String.t(), String.t() | nil, atom()) :: atom()
107
  def launch_node(tenant_id, region, default) do
108
    case region_nodes(region) do
106✔
109
      [node] ->
110
        Logger.warning("Only one region node (#{inspect(node)}) for #{region} using default #{inspect(default)}")
1✔
111

112
        default
1✔
113

114
      [] ->
115
        Logger.warning("Zero region nodes for #{region} using #{inspect(default)}")
104✔
116
        default
104✔
117

118
      regions_nodes ->
119
        member_count = Enum.count(regions_nodes)
1✔
120
        index = :erlang.phash2(tenant_id, member_count)
1✔
121

122
        Enum.fetch!(regions_nodes, index)
1✔
123
    end
124
  end
125

126
  @doc """
127
  Gets a short node name from a node name when a node name looks like `realtime-prod@fdaa:0:cc:a7b:b385:83c3:cfe3:2`
128

129
  ## Examples
130

131
      iex> node = Node.self()
132
      iex> Realtime.Helpers.short_node_id_from_name(node)
133
      "nohost"
134

135
      iex> node = :"realtime-prod@fdaa:0:cc:a7b:b385:83c3:cfe3:2"
136
      iex> Realtime.Helpers.short_node_id_from_name(node)
137
      "83c3cfe3"
138

139
      iex> node = :"pink@127.0.0.1"
140
      iex> Realtime.Helpers.short_node_id_from_name(node)
141
      "127.0.0.1"
142

143
      iex> node = :"pink@10.0.1.1"
144
      iex> Realtime.Helpers.short_node_id_from_name(node)
145
      "10.0.1.1"
146

147
      iex> node = :"realtime@host.name.internal"
148
      iex> Realtime.Helpers.short_node_id_from_name(node)
149
      "host.name.internal"
150
  """
151

152
  @spec short_node_id_from_name(atom()) :: String.t()
153
  def short_node_id_from_name(name) when is_atom(name) do
154
    [_, host] = name |> Atom.to_string() |> String.split("@", parts: 2)
311✔
155

156
    case String.split(host, ":", parts: 8) do
311✔
157
      [_, _, _, _, _, one, two, _] ->
158
        one <> two
×
159

160
      _other ->
161
        host
311✔
162
    end
163
  end
164

165
  @mapping_realtime_region_to_tenant_region_aws %{
166
    "ap-southeast-1" => [
167
      "ap-east-1",
168
      "ap-northeast-1",
169
      "ap-northeast-2",
170
      "ap-south-1",
171
      "ap-southeast-1"
172
    ],
173
    "ap-southeast-2" => ["ap-southeast-2"],
174
    "eu-west-2" => [
175
      "eu-central-1",
176
      "eu-central-2",
177
      "eu-north-1",
178
      "eu-west-1",
179
      "eu-west-2",
180
      "eu-west-3"
181
    ],
182
    "us-east-1" => [
183
      "ca-central-1",
184
      "sa-east-1",
185
      "us-east-1",
186
      "us-east-2"
187
    ],
188
    "us-west-1" => ["us-west-1", "us-west-2"]
189
  }
190
  @mapping_realtime_region_to_tenant_region_fly %{
191
    "iad" => ["ca-central-1", "sa-east-1", "us-east-1"],
192
    "lhr" => ["eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3"],
193
    "sea" => ["us-west-1"],
194
    "syd" => [
195
      "ap-east-1",
196
      "ap-northeast-1",
197
      "ap-northeast-2",
198
      "ap-south-1",
199
      "ap-southeast-1",
200
      "ap-southeast-2"
201
    ]
202
  }
203

204
  @doc """
205
  Fetches the tenant regions for a given realtime reagion
206
  """
207
  @spec region_to_tenant_regions(String.t()) :: list() | nil
208
  def region_to_tenant_regions(region) do
209
    platform = Application.get_env(:realtime, :platform)
×
210

211
    mappings =
×
212
      case platform do
213
        :aws -> @mapping_realtime_region_to_tenant_region_aws
×
214
        :fly -> @mapping_realtime_region_to_tenant_region_fly
×
215
        _ -> %{}
×
216
      end
217

218
    Map.get(mappings, region)
×
219
  end
220
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

© 2025 Coveralls, Inc