summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorILYA Khlopotov <iilyak@apache.org>2019-08-07 16:45:15 +0000
committerILYA Khlopotov <iilyak@apache.org>2019-08-15 10:54:08 -0700
commit69d15cd10652de1895b7750dd86b37ba04267f1d (patch)
tree0197160425134584e5f32257044286f136c9f913
parent5dcc162c9e2bd7c79a93397bf82a9744864206c5 (diff)
downloadcouchdb-69d15cd10652de1895b7750dd86b37ba04267f1d.tar.gz
Refactor fabric:cleanup_index_files
Previous implementation assembled a regexp by concatenating active signatures. The approach caused regexp to exceed system limit in the case of huge number of them.
-rw-r--r--src/couch/test/exunit/fabric_test.exs101
-rw-r--r--src/fabric/src/fabric.erl25
-rw-r--r--test/elixir/lib/setup/common.ex5
3 files changed, 121 insertions, 10 deletions
diff --git a/src/couch/test/exunit/fabric_test.exs b/src/couch/test/exunit/fabric_test.exs
new file mode 100644
index 000000000..bdb84e9a2
--- /dev/null
+++ b/src/couch/test/exunit/fabric_test.exs
@@ -0,0 +1,101 @@
+defmodule Couch.Test.Fabric do
+ use Couch.Test.ExUnit.Case
+ alias Couch.Test.Utils
+
+ alias Couch.Test.Setup
+
+ alias Couch.Test.Setup.Step
+
+ import Couch.DBTest
+
+ import Utils
+
+ @admin {:user_ctx, user_ctx(roles: ["_admin"])}
+
+ def with_db(context, setup) do
+ setup =
+ setup
+ |> Setup.Common.with_db()
+ |> Setup.run()
+
+ context =
+ Map.merge(context, %{
+ db_name: setup |> Setup.get(:db) |> Step.Create.DB.name()
+ })
+
+ {context, setup}
+ end
+
+ describe "Fabric miscellaneous API" do
+ @describetag setup: &__MODULE__.with_db/2
+ test "Get inactive_index_files", ctx do
+ {:ok, _rev} = update_doc(ctx.db_name, %{"_id" => "doc1"})
+
+ design_doc = %{
+ "_id" => "_design/test",
+ "language" => "javascript",
+ "views" => %{
+ "view" => %{
+ "map" => "function(doc){emit(doc._id, doc._rev)}"
+ }
+ }
+ }
+
+ {:ok, rev1} = update_doc(ctx.db_name, design_doc)
+ wait_sig_update(ctx.db_name, "test", "")
+ prev_active = get_active_sig(ctx.db_name, "test")
+
+ updated_design_doc =
+ put_in(design_doc, ["views", "view", "map"], "function(doc){emit(doc._id, null)}")
+
+ {:ok, rev2} =
+ update_doc(
+ ctx.db_name,
+ Map.put(updated_design_doc, "_rev", rev1)
+ )
+
+ assert rev1 != rev2
+ wait_sig_update(ctx.db_name, "test", prev_active)
+
+ {:ok, info} = :fabric.get_view_group_info(ctx.db_name, "_design/test")
+ active = info[:signature]
+
+ files = Enum.map(:fabric.inactive_index_files(ctx.db_name), &List.to_string/1)
+
+ assert [] != files, "We should have some inactive"
+
+ assert not Enum.any?(files, fn
+ file_path -> String.contains?(file_path, active)
+ end),
+ "We are not suppose to return active views"
+
+ assert Enum.all?(files, fn
+ file_path -> String.contains?(file_path, prev_active)
+ end),
+ "We expect all files to contain previous active signature"
+ end
+ end
+
+ defp update_doc(db_name, body) do
+ json_body = :jiffy.decode(:jiffy.encode(body))
+
+ case :fabric.update_doc(db_name, json_body, [@admin]) do
+ {:ok, rev} ->
+ {:ok, :couch_doc.rev_to_str(rev)}
+
+ error ->
+ error
+ end
+ end
+
+ defp get_active_sig(db_name, ddoc_id) do
+ {:ok, info} = :fabric.get_view_group_info(db_name, "_design/#{ddoc_id}")
+ info[:signature]
+ end
+
+ defp wait_sig_update(db_name, ddoc_id, prev_active) do
+ retry_until(fn ->
+ get_active_sig(db_name, ddoc_id) != prev_active
+ end)
+ end
+end
diff --git a/src/fabric/src/fabric.erl b/src/fabric/src/fabric.erl
index 6d04184e6..d98ffc978 100644
--- a/src/fabric/src/fabric.erl
+++ b/src/fabric/src/fabric.erl
@@ -36,7 +36,8 @@
% miscellany
-export([design_docs/1, reset_validation_funs/1, cleanup_index_files/0,
- cleanup_index_files/1, cleanup_index_files_all_nodes/1, dbname/1]).
+ cleanup_index_files/1, cleanup_index_files_all_nodes/1, dbname/1,
+ inactive_index_files/1]).
-include_lib("fabric/include/fabric.hrl").
@@ -503,26 +504,30 @@ cleanup_index_files() ->
%% @doc clean up index files for a specific db
-spec cleanup_index_files(dbname()) -> ok.
cleanup_index_files(DbName) ->
+ lists:foreach(fun(File) ->
+ file:delete(File)
+ end, inactive_index_files(DbName)).
+
+%% @doc inactive index files for a specific db
+-spec inactive_index_files(dbname()) -> ok.
+inactive_index_files(DbName) ->
{ok, DesignDocs} = fabric:design_docs(DbName),
- ActiveSigs = lists:map(fun(#doc{id = GroupId}) ->
+ ActiveSigs = maps:from_list(lists:map(fun(#doc{id = GroupId}) ->
{ok, Info} = fabric:get_view_group_info(DbName, GroupId),
- binary_to_list(couch_util:get_value(signature, Info))
- end, [couch_doc:from_json_obj(DD) || DD <- DesignDocs]),
+ {binary_to_list(couch_util:get_value(signature, Info)), nil}
+ end, [couch_doc:from_json_obj(DD) || DD <- DesignDocs])),
FileList = lists:flatmap(fun(#shard{name = ShardName}) ->
IndexDir = couch_index_util:index_dir(mrview, ShardName),
filelib:wildcard([IndexDir, "/*"])
end, mem3:local_shards(dbname(DbName))),
- DeleteFiles = if ActiveSigs =:= [] -> FileList; true ->
- {ok, RegExp} = re:compile([$(, string:join(ActiveSigs, "|"), $)]),
+ if ActiveSigs =:= [] -> FileList; true ->
lists:filter(fun(FilePath) ->
- re:run(FilePath, RegExp, [{capture, none}]) == nomatch
+ not maps:is_key(filename:basename(FilePath, ".view"), ActiveSigs)
end, FileList)
- end,
- [file:delete(File) || File <- DeleteFiles],
- ok.
+ end.
%% @doc clean up index files for a specific db on all nodes
-spec cleanup_index_files_all_nodes(dbname()) -> [reference()].
diff --git a/test/elixir/lib/setup/common.ex b/test/elixir/lib/setup/common.ex
index 3b59e9476..e81f109c9 100644
--- a/test/elixir/lib/setup/common.ex
+++ b/test/elixir/lib/setup/common.ex
@@ -19,4 +19,9 @@ defmodule Couch.Test.Setup.Common do
|> Step.Create.DB.new(:db)
end
+ def with_db(setup) do
+ setup
+ |> Step.Start.new(:start, extra_apps: [:fabric])
+ |> Step.Create.DB.new(:db)
+ end
end \ No newline at end of file