summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Newson <rnewson@apache.org>2023-03-30 11:08:03 +0100
committerRobert Newson <rnewson@apache.org>2023-03-30 11:45:25 +0100
commit3fc0d1afa13ef5f09014912925a23f15fd5d1d3d (patch)
tree5925018abcf68209059bf71bd58fb5406bdb0ed2
parentda329fa72c67bb9ebac6f33ccb0bfc4ee5255ce3 (diff)
downloadcouchdb-3fc0d1afa13ef5f09014912925a23f15fd5d1d3d.tar.gz
support _nouveau_info endpoint
-rw-r--r--nouveau/README.md2
-rw-r--r--src/nouveau/src/nouveau_api.erl2
-rw-r--r--src/nouveau/src/nouveau_fabric_info.erl99
-rw-r--r--src/nouveau/src/nouveau_httpd.erl27
-rw-r--r--src/nouveau/src/nouveau_httpd_handlers.erl2
-rw-r--r--src/nouveau/src/nouveau_index_updater.erl4
-rw-r--r--src/nouveau/src/nouveau_rpc.erl7
7 files changed, 135 insertions, 8 deletions
diff --git a/nouveau/README.md b/nouveau/README.md
index a89c848e1..c275ab47e 100644
--- a/nouveau/README.md
+++ b/nouveau/README.md
@@ -23,6 +23,7 @@ This work is currently EXPERIMENTAL and may change in ways that invalidate any e
* integration with mango
* integration with resharding
* update=false
+* `_nouveau_info`
## What doesn't work yet?
@@ -30,7 +31,6 @@ This work is currently EXPERIMENTAL and may change in ways that invalidate any e
* configurable stop words for analyzers
* Makefile.win or Windows generally
* `_search_cleanup`
-* `_search_info`
I don't intend to add grouping support, it seems barely used. Would accept a tidy contribution, though.
diff --git a/src/nouveau/src/nouveau_api.erl b/src/nouveau/src/nouveau_api.erl
index 39fc24cea..8d385be54 100644
--- a/src/nouveau/src/nouveau_api.erl
+++ b/src/nouveau/src/nouveau_api.erl
@@ -57,7 +57,7 @@ index_info(#index{} = Index) ->
Resp = send_if_enabled(index_url(Index), [], get),
case Resp of
{ok, "200", _, RespBody} ->
- {ok, jiffy:decode(RespBody)};
+ {ok, jiffy:decode(RespBody, [return_maps])};
{ok, StatusCode, _, RespBody} ->
{error, jaxrs_error(StatusCode, RespBody)};
{error, Reason} ->
diff --git a/src/nouveau/src/nouveau_fabric_info.erl b/src/nouveau/src/nouveau_fabric_info.erl
new file mode 100644
index 000000000..59e47094f
--- /dev/null
+++ b/src/nouveau/src/nouveau_fabric_info.erl
@@ -0,0 +1,99 @@
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+
+-module(nouveau_fabric_info).
+
+-export([go/3]).
+
+-include_lib("mem3/include/mem3.hrl").
+
+go(DbName, DDocId, IndexName) when is_binary(DDocId) ->
+ {ok, DDoc} = fabric:open_doc(DbName, <<"_design/", DDocId/binary>>, [ejson_body]),
+ go(DbName, DDoc, IndexName);
+go(DbName, DDoc, IndexName) ->
+ {ok, Index} = nouveau_util:design_doc_to_index(DbName, DDoc, IndexName),
+ Shards = mem3:shards(DbName),
+ Counters0 = lists:map(
+ fun(#shard{} = Shard) ->
+ Ref = rexi:cast(
+ Shard#shard.node,
+ {nouveau_rpc, info, [Shard#shard.name, Index]}
+ ),
+ Shard#shard{ref = Ref}
+ end,
+ Shards
+ ),
+ Counters = fabric_dict:init(Counters0, nil),
+ Workers = fabric_dict:fetch_keys(Counters),
+ RexiMon = fabric_util:create_monitors(Workers),
+
+ Acc0 = {fabric_dict:init(Workers, nil), #{}},
+ try
+ fabric_util:recv(Workers, #shard.ref, fun handle_message/3, Acc0)
+ after
+ rexi_monitor:stop(RexiMon),
+ fabric_util:cleanup(Workers)
+ end.
+
+handle_message({rexi_DOWN, _, {_, NodeRef}, _}, _Worker, {Counters, Acc}) ->
+ case fabric_util:remove_down_workers(Counters, NodeRef) of
+ {ok, NewCounters} ->
+ {ok, {NewCounters, Acc}};
+ error ->
+ {error, {nodedown, <<"progress not possible">>}}
+ end;
+handle_message({rexi_EXIT, Reason}, Worker, {Counters, Acc}) ->
+ NewCounters = fabric_dict:erase(Worker, Counters),
+ case fabric_ring:is_progress_possible(NewCounters) of
+ true ->
+ {ok, {NewCounters, Acc}};
+ false ->
+ {error, Reason}
+ end;
+handle_message({ok, Info}, Worker, {Counters, Acc0}) ->
+ case fabric_dict:lookup_element(Worker, Counters) of
+ undefined ->
+ % already heard from someone else in this range
+ {ok, {Counters, Acc0}};
+ nil ->
+ C1 = fabric_dict:store(Worker, ok, Counters),
+ C2 = fabric_view:remove_overlapping_shards(Worker, C1),
+ Acc1 = maps:merge_with(fun merge_info/3, Info, Acc0),
+ case fabric_dict:any(nil, C2) of
+ true ->
+ {ok, {C2, Acc1}};
+ false ->
+ {stop, Acc1}
+ end
+ end;
+handle_message({error, Reason}, Worker, {Counters, Acc}) ->
+ NewCounters = fabric_dict:erase(Worker, Counters),
+ case fabric_ring:is_progress_possible(NewCounters) of
+ true ->
+ {ok, {NewCounters, Acc}};
+ false ->
+ {error, Reason}
+ end;
+handle_message({'EXIT', _}, Worker, {Counters, Acc}) ->
+ NewCounters = fabric_dict:erase(Worker, Counters),
+ case fabric_ring:is_progress_possible(NewCounters) of
+ true ->
+ {ok, {NewCounters, Acc}};
+ false ->
+ {error, {nodedown, <<"progress not possible">>}}
+ end.
+
+merge_info(_Key, Val1, Val2) ->
+ Val1 + Val2.
diff --git a/src/nouveau/src/nouveau_httpd.erl b/src/nouveau/src/nouveau_httpd.erl
index d8a309b7e..6ebce7394 100644
--- a/src/nouveau/src/nouveau_httpd.erl
+++ b/src/nouveau/src/nouveau_httpd.erl
@@ -98,9 +98,32 @@ handle_search_req(#httpd{} = Req, DbName, DDoc, IndexName, QueryArgs) ->
send_error(Req, Reason)
end.
-handle_info_req(_Req, _Db, _DDoc) ->
+handle_info_req(
+ #httpd{method = 'GET', path_parts = [_, _, _, _, IndexName]} = Req,
+ Db,
+ #doc{id = Id} = DDoc
+) ->
check_if_enabled(),
- ok.
+ DbName = couch_db:name(Db),
+ case nouveau_fabric_info:go(DbName, DDoc, IndexName) of
+ {ok, IndexInfo} ->
+ send_json(
+ Req,
+ 200,
+ {[
+ {name, <<Id/binary, "/", IndexName/binary>>},
+ {search_index, IndexInfo}
+ ]}
+ );
+ {error, Reason} ->
+ send_error(Req, Reason)
+ end;
+handle_info_req(#httpd{path_parts = [_, _, _, _, _]} = Req, _Db, _DDoc) ->
+ check_if_enabled(),
+ send_method_not_allowed(Req, "GET");
+handle_info_req(Req, _Db, _DDoc) ->
+ check_if_enabled(),
+ send_error(Req, {bad_request, "path not recognized"}).
include_docs(_DbName, Hits, false) ->
Hits;
diff --git a/src/nouveau/src/nouveau_httpd_handlers.erl b/src/nouveau/src/nouveau_httpd_handlers.erl
index d94e0d0c6..16999e103 100644
--- a/src/nouveau/src/nouveau_httpd_handlers.erl
+++ b/src/nouveau/src/nouveau_httpd_handlers.erl
@@ -28,6 +28,6 @@ db_handler(_) ->
design_handler(<<"_nouveau">>) ->
fun nouveau_httpd:handle_search_req/3;
design_handler(<<"_nouveau_info">>) ->
- nomatch;
+ fun nouveau_httpd:handle_info_req/3;
design_handler(_) ->
no_match.
diff --git a/src/nouveau/src/nouveau_index_updater.erl b/src/nouveau/src/nouveau_index_updater.erl
index 022e5cfd3..4bdbcf2b0 100644
--- a/src/nouveau/src/nouveau_index_updater.erl
+++ b/src/nouveau/src/nouveau_index_updater.erl
@@ -124,8 +124,8 @@ get_db_seq(#index{} = Index) ->
get_index_seq(#index{} = Index) ->
case nouveau_api:index_info(Index) of
- {ok, {Fields}} ->
- {ok, couch_util:get_value(<<"update_seq">>, Fields)};
+ {ok, #{<<"update_seq">> := Seq}} ->
+ {ok, Seq};
{error, Reason} ->
{error, Reason}
end.
diff --git a/src/nouveau/src/nouveau_rpc.erl b/src/nouveau/src/nouveau_rpc.erl
index 522471256..a17b1c2ef 100644
--- a/src/nouveau/src/nouveau_rpc.erl
+++ b/src/nouveau/src/nouveau_rpc.erl
@@ -15,7 +15,7 @@
-module(nouveau_rpc).
--export([search/3]).
+-export([search/3, info/2]).
-include("nouveau.hrl").
-import(nouveau_util, [index_path/1]).
@@ -42,3 +42,8 @@ search(DbName, #index{} = Index0, QueryArgs) ->
%% Run the search
rexi:reply(nouveau_api:search(Index1, QueryArgs)).
+
+info(DbName, #index{} = Index0) ->
+ %% Incorporate the shard name into the record.
+ Index1 = Index0#index{dbname = DbName},
+ rexi:reply(nouveau_api:index_info(Index1)). \ No newline at end of file