Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flag to disable pruning the node list in Cluster.Strategy.DNSPoll #196

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Allow Epmd strategy to reconnect after connection failures
- Detect Self Signed Certificate Authority for Kubernetes Strategy
- Remove calls to deprecated `Logger.warn/2`
- Prune flag for DNSPoll strategy

### 3.3.0

Expand Down
55 changes: 36 additions & 19 deletions lib/strategy/dns_poll.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule Cluster.Strategy.DNSPoll do
* `poll_interval` - How often to poll in milliseconds (optional; default: 5_000)
* `query` - DNS query to use (required; e.g. "my-app.example.com")
* `node_basename` - The short name of the nodes you wish to connect to (required; e.g. "my-app")
* `prune` - Remove nodes not returned in DNS response (optional; default: true)

## Usage

Expand All @@ -19,7 +20,8 @@ defmodule Cluster.Strategy.DNSPoll do
config: [
polling_interval: 5_000,
query: "my-app.example.com",
node_basename: "my-app"]]]
node_basename: "my-app",
prune: true]]]
"""

use GenServer
Expand All @@ -29,6 +31,7 @@ defmodule Cluster.Strategy.DNSPoll do
alias Cluster.Strategy

@default_polling_interval 5_000
@default_prune true

def start_link(args), do: GenServer.start_link(__MODULE__, args)

Expand All @@ -50,29 +53,12 @@ defmodule Cluster.Strategy.DNSPoll do
%State{
topology: topology,
connect: connect,
disconnect: disconnect,
list_nodes: list_nodes
} = state
) do
new_nodelist = state |> get_nodes() |> MapSet.new()
removed = MapSet.difference(state.meta, new_nodelist)

new_nodelist =
case Strategy.disconnect_nodes(
topology,
disconnect,
list_nodes,
MapSet.to_list(removed)
) do
:ok ->
new_nodelist

{:error, bad_nodes} ->
# Add back the nodes which should have been removed, but which couldn't be for some reason
Enum.reduce(bad_nodes, new_nodelist, fn {n, _}, acc ->
MapSet.put(acc, n)
end)
end
new_nodelist = if prune?(state), do: prune_nodelist(state, new_nodelist), else: new_nodelist

new_nodelist =
case Strategy.connect_nodes(
Expand Down Expand Up @@ -100,6 +86,37 @@ defmodule Cluster.Strategy.DNSPoll do
Keyword.get(config, :polling_interval, @default_polling_interval)
end

defp prune?(%{config: config}) do
Keyword.get(config, :prune, @default_prune)
end

defp prune_nodelist(
%State{
topology: topology,
disconnect: disconnect,
list_nodes: list_nodes
} = state,
new_nodelist
) do
removed = MapSet.difference(state.meta, new_nodelist)

case Strategy.disconnect_nodes(
topology,
disconnect,
list_nodes,
MapSet.to_list(removed)
) do
:ok ->
new_nodelist

{:error, bad_nodes} ->
# Add back the nodes which should have been removed, but which couldn't be for some reason
Enum.reduce(bad_nodes, new_nodelist, fn {n, _}, acc ->
MapSet.put(acc, n)
end)
end
end

defp get_nodes(%State{config: config} = state) do
query = Keyword.fetch(config, :query)
node_basename = Keyword.fetch(config, :node_basename)
Expand Down
24 changes: 24 additions & 0 deletions test/dns_poll_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,30 @@ defmodule Cluster.Strategy.DNSPollTest do
end)
end

test "keep missing nodes when prune is false" do
capture_log(fn ->
[
%Cluster.Strategy.State{
topology: :dns_poll,
config: [
polling_interval: 100,
query: "app",
node_basename: "node",
prune: false,
resolver: fn _query -> [{10, 0, 0, 1}] end
],
connect: {Nodes, :connect, [self()]},
disconnect: {Nodes, :disconnect, [self()]},
list_nodes: {Nodes, :list_nodes, [[:"[email protected]", :"[email protected]"]]},
meta: MapSet.new([:"[email protected]", :"[email protected]"])
}
]
|> DNSPoll.start_link()

refute_receive {:disconnect, :"[email protected]"}, 100
end)
end

test "keeps state" do
capture_log(fn ->
[
Expand Down