summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarren Smith <garren.smith@gmail.com>2018-09-20 14:37:08 +0200
committerGarren Smith <garren.smith@gmail.com>2018-10-02 14:33:48 +0200
commit2f3aa8be8b3689c409cda0e340030e9270d2deb1 (patch)
tree95921e5c1037a091b6accf9e3d86436dea196818
parentba78cec39dc957b2c1b80b9576f0a6c37b8a2924 (diff)
downloadcouchdb-add-partition-info-endpoint.tar.gz
Add /_partition/:partition/ endpointadd-partition-info-endpoint
Add an endpoint that returns the partition size and doc count
-rw-r--r--src/chttpd/src/chttpd_db.erl4
-rw-r--r--src/couch/src/couch_bt_engine.erl8
-rw-r--r--src/couch/src/couch_db.erl11
-rw-r--r--src/couch/src/couch_db_engine.erl24
-rw-r--r--src/fabric/src/fabric.erl15
-rw-r--r--src/fabric/src/fabric_db_partition_info.erl98
-rw-r--r--src/fabric/src/fabric_rpc.erl5
7 files changed, 160 insertions, 5 deletions
diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl
index 9dab4b5bd..b9d7d8f51 100644
--- a/src/chttpd/src/chttpd_db.erl
+++ b/src/chttpd/src/chttpd_db.erl
@@ -252,6 +252,10 @@ handle_view_cleanup_req(Req, Db) ->
send_json(Req, 202, {[{ok, true}]}).
+handle_partition_req(#httpd{method='GET',path_parts=[DbName, <<"_partition">>, Partition]}=Req, _Db) ->
+ {ok, PartitionInfo} = fabric:get_partition_info(DbName, Partition),
+ send_json(Req, {PartitionInfo});
+
handle_partition_req(#httpd{
path_parts=[DbName, <<"_partition">>, Partition, _Design, Name, <<"_",_/binary>> = Action | _Rest]
}=Req, Db) ->
diff --git a/src/couch/src/couch_bt_engine.erl b/src/couch/src/couch_bt_engine.erl
index e24d55e54..699385b8f 100644
--- a/src/couch/src/couch_bt_engine.erl
+++ b/src/couch/src/couch_bt_engine.erl
@@ -299,8 +299,12 @@ get_partition_info(#st{} = St, Partition) ->
{partition, Partition},
{doc_count, DocCount},
{doc_del_count, DocDelCount},
- {active, SizeInfo#size_info.active},
- {external, SizeInfo#size_info.external}
+ {sizes,
+ [
+ {active, SizeInfo#size_info.active},
+ {external, SizeInfo#size_info.external}
+ ]
+ }
].
diff --git a/src/couch/src/couch_db.erl b/src/couch/src/couch_db.erl
index a5c71d75d..5a13e8acc 100644
--- a/src/couch/src/couch_db.erl
+++ b/src/couch/src/couch_db.erl
@@ -113,7 +113,9 @@
validate_dbname/1,
make_doc/5,
- new_revid/1
+ new_revid/1,
+
+ get_partition_info/2
]).
@@ -475,6 +477,13 @@ get_db_info(Db) ->
],
{ok, InfoList}.
+get_partition_info(#db{} = Db, Partition) when is_binary(Partition) ->
+ Sizes = couch_db_engine:get_partition_info(Db, Partition),
+ {ok, Sizes};
+get_partition_info(_Db, _Partition) ->
+ throw({bad_request, <<"`partition` is not valid">>}).
+
+
get_design_docs(#db{name = <<"shards/", _:18/binary, DbName/binary>>}) ->
{_, Ref} = spawn_monitor(fun() -> exit(fabric:design_docs(DbName)) end),
receive {'DOWN', Ref, _, _, Response} ->
diff --git a/src/couch/src/couch_db_engine.erl b/src/couch/src/couch_db_engine.erl
index 9b288127a..9d804b1ab 100644
--- a/src/couch/src/couch_db_engine.erl
+++ b/src/couch/src/couch_db_engine.erl
@@ -42,6 +42,12 @@
-type purge_info() :: [{docid(), revs()}].
-type epochs() :: [{Node::atom(), UpdateSeq::non_neg_integer()}].
-type size_info() :: [{Name::atom(), Size::non_neg_integer()}].
+-type partition_info() :: [
+ {partition, Partition::binary()} |
+ {doc_count, DocCount::non_neg_integer()} |
+ {doc_del_count, DocDelCount::non_neg_integer()} |
+ {sizes, size_info()}
+].
-type write_stream_options() :: [
{buffer_size, Size::pos_integer()} |
@@ -254,6 +260,18 @@
-callback get_size_info(DbHandle::db_handle()) -> SizeInfo::size_info().
+% This returns the information for the given partition.
+% It should just be a list of {Name::atom(), Size::non_neg_integer()}
+% It returns the partition name, doc count, deleted doc count and two sizes:
+%
+% active - Theoretical minimum number of bytes to store this partition on disk
+%
+% external - Number of bytes that would be required to represent the
+% contents of this partition outside of the database
+-callback get_partition_info(DbHandle::db_handle(), Partition::binary()) ->
+ partition_info().
+
+
% The current update sequence of the database. The update
% sequence should be incrememnted for every revision added to
% the database.
@@ -627,6 +645,7 @@
get_prop/2,
get_prop/3,
get_size_info/1,
+ get_partition_info/2,
get_update_seq/1,
get_uuid/1,
@@ -803,6 +822,11 @@ get_size_info(#db{} = Db) ->
Engine:get_size_info(EngineState).
+get_partition_info(#db{} = Db, Partition) ->
+ #db{engine = {Engine, EngineState}} = Db,
+ Engine:get_partition_info(EngineState, Partition).
+
+
get_update_seq(#db{} = Db) ->
#db{engine = {Engine, EngineState}} = Db,
Engine:get_update_seq(EngineState).
diff --git a/src/fabric/src/fabric.erl b/src/fabric/src/fabric.erl
index f5c793736..00cbf3bab 100644
--- a/src/fabric/src/fabric.erl
+++ b/src/fabric/src/fabric.erl
@@ -21,7 +21,7 @@
delete_db/2, get_db_info/1, get_doc_count/1, set_revs_limit/3,
set_security/2, set_security/3, get_revs_limit/1, get_security/1,
get_security/2, get_all_security/1, get_all_security/2,
- compact/1, compact/2]).
+ compact/1, compact/2, get_partition_info/2]).
% Documents
-export([open_doc/3, open_revs/4, get_doc_info/3, get_full_doc_info/3,
@@ -84,6 +84,19 @@ all_dbs(Prefix) when is_list(Prefix) ->
get_db_info(DbName) ->
fabric_db_info:go(dbname(DbName)).
+%% @doc returns the size of a given partition
+-spec get_partition_info(dbname(), Partition::binary()) ->
+ {ok, [
+ {db_name, binary()} |
+ {partition, binary()} |
+ {doc_count, non_neg_integer()} |
+ {doc_del_count, non_neg_integer()} |
+ {sizes, json_obj()}
+ ]}.
+get_partition_info(DbName, Partition) ->
+ fabric_db_partition_info:go(dbname(DbName), Partition).
+
+
%% @doc the number of docs in a database
-spec get_doc_count(dbname()) ->
{ok, non_neg_integer()} |
diff --git a/src/fabric/src/fabric_db_partition_info.erl b/src/fabric/src/fabric_db_partition_info.erl
new file mode 100644
index 000000000..908361872
--- /dev/null
+++ b/src/fabric/src/fabric_db_partition_info.erl
@@ -0,0 +1,98 @@
+% 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.
+
+-module(fabric_db_partition_info).
+
+-export([go/2]).
+
+-include_lib("fabric/include/fabric.hrl").
+-include_lib("mem3/include/mem3.hrl").
+
+go(DbName, Partition) ->
+ Shards = mem3:shards(DbName, <<Partition/binary, ":foo">>),
+ Workers = fabric_util:submit_jobs(Shards, get_partition_info, [Partition]),
+ RexiMon = fabric_util:create_monitors(Shards),
+ Fun = fun handle_message/3,
+ Acc0 = {fabric_dict:init(Workers, nil), []},
+ try
+ case fabric_util:recv(Workers, #shard.ref, Fun, Acc0) of
+ {ok, Acc} -> {ok, Acc};
+ {timeout, {WorkersDict, _}} ->
+ DefunctWorkers = fabric_util:remove_done_workers(
+ WorkersDict,
+ nil
+ ),
+ fabric_util:log_timeout(
+ DefunctWorkers,
+ "get_partition_info"
+ ),
+ {error, timeout};
+ {error, Error} -> throw(Error)
+ end
+ after
+ rexi_monitor:stop(RexiMon)
+ end.
+
+handle_message({rexi_DOWN, _, {_,NodeRef},_}, _Shard, {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}, Shard, {Counters, Acc}) ->
+ NewCounters = fabric_dict:erase(Shard, Counters),
+ case fabric_view:is_progress_possible(NewCounters) of
+ true ->
+ {ok, {NewCounters, Acc}};
+ false ->
+ {error, Reason}
+ end;
+
+handle_message({ok, Sizes}, #shard{dbname=Name} = Shard, {Counters, Acc}) ->
+ Acc2 = [Sizes | Acc],
+ Counters1 = fabric_dict:erase(Shard, Counters),
+ case fabric_dict:size(Counters1) =:= 0 of
+ true ->
+ [FirstInfo | RestInfos] = Acc2,
+ PartitionInfo = get_max_partition_size(FirstInfo, RestInfos),
+ {stop, [{db_name, Name} | format_partition(PartitionInfo)]};
+ false ->
+ {ok, {Counters1, Acc2}}
+ end;
+
+handle_message(_, _, Acc) ->
+ {ok, Acc}.
+
+get_max_partition_size(Max, []) ->
+ Max;
+get_max_partition_size(MaxInfo, [NextInfo | Rest]) ->
+ {sizes, MaxSize} = lists:keyfind(sizes, 1, MaxInfo),
+ {sizes, NextSize} = lists:keyfind(sizes, 1, NextInfo),
+
+ {external, MaxExtSize} = lists:keyfind(external, 1, MaxSize),
+ {external, NextExtSize} = lists:keyfind(external, 1, NextSize),
+ case NextExtSize > MaxExtSize of
+ true ->
+ get_max_partition_size(NextInfo, Rest);
+ false ->
+ get_max_partition_size(MaxInfo, Rest)
+ end.
+
+
+% for JS to work nicely we need to convert the size list
+% to a jiffy object
+format_partition(PartitionInfo) ->
+ {value, {sizes, Size}, PartitionInfo1} = lists:keytake(sizes, 1, PartitionInfo),
+ [{sizes, {Size}} | PartitionInfo1].
+
diff --git a/src/fabric/src/fabric_rpc.erl b/src/fabric/src/fabric_rpc.erl
index e538a9dbe..20bca0eb5 100644
--- a/src/fabric/src/fabric_rpc.erl
+++ b/src/fabric/src/fabric_rpc.erl
@@ -18,7 +18,7 @@
-export([all_docs/3, changes/3, map_view/4, reduce_view/4, group_info/2]).
-export([create_db/1, create_db/2, delete_db/1, reset_validation_funs/1,
set_security/3, set_revs_limit/3, create_shard_db_doc/2,
- delete_shard_db_doc/2]).
+ delete_shard_db_doc/2, get_partition_info/2]).
-export([get_all_security/2, open_shard/2]).
-export([compact/1, compact/2]).
@@ -167,6 +167,9 @@ get_db_info(DbName) ->
get_db_info(DbName, DbOptions) ->
with_db(DbName, DbOptions, {couch_db, get_db_info, []}).
+get_partition_info(DbName, Partition) ->
+ with_db(DbName, [], {couch_db, get_partition_info, [Partition]}).
+
%% equiv get_doc_count(DbName, [])
get_doc_count(DbName) ->
get_doc_count(DbName, []).