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

Fix index hotswapping with no index_namespace set #87

Merged
merged 1 commit into from
Jan 18, 2024
Merged
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
12 changes: 4 additions & 8 deletions lib/snap/indexes.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,14 @@ defmodule Snap.Indexes do
"""
@spec list(module(), Keyword.t()) :: {:ok, list(String.t())} | Snap.Cluster.error()
def list(cluster, opts \\ []) do
namespace = Namespace.index_namespace(cluster)

with {:ok, indexes} <- Snap.get(cluster, "/_cat/indices", [format: "json"], [], opts) do
indexes =
indexes
|> Enum.map(& &1["index"])
# Only return indexes inside this namespace
|> Enum.filter(&String.starts_with?(&1, "#{namespace}-"))
|> Enum.filter(&Namespace.index_in_namespace?(&1, cluster))
# Present them without the namespace prefix
|> Enum.map(&String.trim_leading(&1, "#{namespace}-"))
|> Enum.map(&Namespace.strip_namespace(&1, cluster))
|> Enum.sort()

{:ok, indexes}
Expand All @@ -118,8 +116,6 @@ defmodule Snap.Indexes do
@spec list_starting_with(module(), String.t(), Keyword.t()) ::
{:ok, list(String.t())} | Snap.Cluster.error()
def list_starting_with(cluster, prefix, opts \\ []) do
namespace = Namespace.index_namespace(cluster)

with {:ok, indexes} <- Snap.get(cluster, "/_cat/indices", [format: "json"], [], opts) do
prefix = prefix |> to_string() |> Regex.escape()
{:ok, regex} = Regex.compile("^#{prefix}-[0-9]+$")
Expand All @@ -128,9 +124,9 @@ defmodule Snap.Indexes do
indexes
|> Enum.map(& &1["index"])
# Only return indexes inside this namespace
|> Enum.filter(&String.starts_with?(&1, "#{namespace}-"))
|> Enum.filter(&Namespace.index_in_namespace?(&1, cluster))
# Present them without the namespace prefix
|> Enum.map(&String.trim_leading(&1, "#{namespace}-"))
|> Enum.map(&Namespace.strip_namespace(&1, cluster))
|> Enum.filter(&Regex.match?(regex, &1))
|> Enum.sort_by(&sort_index_by_timestamp/1)

Expand Down
30 changes: 28 additions & 2 deletions lib/snap/namespace.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ defmodule Snap.Cluster.Namespace do

alias Snap.Cluster.Supervisor

@separator "-"

@doc false
def start_link(name) do
GenServer.start_link(__MODULE__, [], name: name)
Expand Down Expand Up @@ -99,7 +101,7 @@ defmodule Snap.Cluster.Namespace do
def add_namespace_to_index(index, cluster) do
[config_namespace(cluster), get_process_namespace(cluster, self()), index]
|> Enum.reject(&is_nil/1)
|> Enum.join("-")
|> merge_elements()
end

@doc """
Expand All @@ -109,7 +111,28 @@ defmodule Snap.Cluster.Namespace do
def index_namespace(cluster) do
[config_namespace(cluster), get_process_namespace(cluster, self())]
|> Enum.reject(&is_nil/1)
|> Enum.join("-")
|> merge_elements()
end

@doc """
Returns a boolean indicating whether the namespaced index is inside the currently
defined namespace.
"""
def index_in_namespace?(namespaced_index, cluster) do
case index_namespace(cluster) do
nil -> true
namespace -> String.starts_with?(namespaced_index, "#{namespace}#{@separator}")
end
end

@doc """
Remove the namespace prefix from a namespaced index, returning the remainder.
"""
def strip_namespace(namespaced_index, cluster) do
case index_namespace(cluster) do
nil -> namespaced_index
namespace -> String.replace_leading(namespaced_index, "#{namespace}#{@separator}", "")
end
end

@doc false
Expand Down Expand Up @@ -147,4 +170,7 @@ defmodule Snap.Cluster.Namespace do
config = cluster.config()
Keyword.get(config, :index_namespace)
end

defp merge_elements([]), do: nil
defp merge_elements(list), do: Enum.join(list, @separator)
end
100 changes: 100 additions & 0 deletions test/namespace_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
defmodule Snap.Cluster.NamespaceTest do
use ExUnit.Case, async: false

alias Snap.Cluster.Namespace

defmodule NoNamespaceCluster do
@moduledoc false
use Snap.Cluster, otp_app: :snap

def init(config) do
config =
config
|> Keyword.delete(:index_namespace)
|> Keyword.put(:url, "http://foo")

{:ok, config}
end
end

defmodule NamespaceCluster do
@moduledoc false
use Snap.Cluster, otp_app: :snap

def init(config) do
config =
config
|> Keyword.put(:index_namespace, "cluster")
|> Keyword.put(:url, "http://foo")

{:ok, config}
end
end

setup_all do
{:ok, namespace_pid} = NamespaceCluster.start_link()
{:ok, no_namespace_pid} = NoNamespaceCluster.start_link()

%{namespace_cluster_pid: namespace_pid, no_namespace_cluster_pid: no_namespace_pid}
end

describe "add_namespace_to_index/2" do
test "without a cluster index_namespace" do
assert nil == Namespace.index_namespace(NoNamespaceCluster)
assert "foo" == Namespace.add_namespace_to_index(:foo, NoNamespaceCluster)
end

test "with a cluster index_namespace" do
assert "cluster" == Namespace.index_namespace(NamespaceCluster)
assert "cluster-foo" == Namespace.add_namespace_to_index(:foo, NamespaceCluster)
end
end

describe "set_process_namespace/3 and clear_process_namespace/2" do
test "without a cluster index_namespace" do
Namespace.set_process_namespace(NoNamespaceCluster, "process")
assert "process-index" == Namespace.add_namespace_to_index("index", NoNamespaceCluster)

Namespace.clear_process_namespace(NoNamespaceCluster, self())
assert "index" == Namespace.add_namespace_to_index("index", NoNamespaceCluster)
end

test "with a cluster index_namespace" do
Namespace.set_process_namespace(NamespaceCluster, "process")

assert "cluster-process-index" ==
Namespace.add_namespace_to_index("index", NamespaceCluster)

Namespace.clear_process_namespace(NamespaceCluster, self())
assert "cluster-index" == Namespace.add_namespace_to_index("index", NamespaceCluster)
end
end

describe "index_in_namespace?/2" do
test "with an index in the cluster namespace" do
assert true == Namespace.index_in_namespace?("cluster-baz", NamespaceCluster)
end

test "with an index not in the cluster namespace" do
assert false == Namespace.index_in_namespace?("different-baz", NamespaceCluster)
end

test "with an index and no cluster index_namespace" do
assert true == Namespace.index_in_namespace?("foo-baz", NoNamespaceCluster)
end
end

describe "strip_namespace/2" do
test "it strips the cluster namespace" do
assert "foo" == Namespace.strip_namespace("cluster-foo", NamespaceCluster)
end

test "it doesn't strip something that isn't the cluster namespace" do
assert "notcluster-foo" == Namespace.strip_namespace("notcluster-foo", NamespaceCluster)
end

test "it preserves the index without a cluster namespace" do
assert "cluster-foo" == Namespace.strip_namespace("cluster-foo", NoNamespaceCluster)
end
end
end