diff options
author | Robert Newson <rnewson@apache.org> | 2023-03-30 11:08:03 +0100 |
---|---|---|
committer | Robert Newson <rnewson@apache.org> | 2023-03-30 11:45:25 +0100 |
commit | 3fc0d1afa13ef5f09014912925a23f15fd5d1d3d (patch) | |
tree | 5925018abcf68209059bf71bd58fb5406bdb0ed2 | |
parent | da329fa72c67bb9ebac6f33ccb0bfc4ee5255ce3 (diff) | |
download | couchdb-3fc0d1afa13ef5f09014912925a23f15fd5d1d3d.tar.gz |
support _nouveau_info endpoint
-rw-r--r-- | nouveau/README.md | 2 | ||||
-rw-r--r-- | src/nouveau/src/nouveau_api.erl | 2 | ||||
-rw-r--r-- | src/nouveau/src/nouveau_fabric_info.erl | 99 | ||||
-rw-r--r-- | src/nouveau/src/nouveau_httpd.erl | 27 | ||||
-rw-r--r-- | src/nouveau/src/nouveau_httpd_handlers.erl | 2 | ||||
-rw-r--r-- | src/nouveau/src/nouveau_index_updater.erl | 4 | ||||
-rw-r--r-- | src/nouveau/src/nouveau_rpc.erl | 7 |
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 |