Skip to content

Commit 3754deb

Browse files
committed
Cache and store mem3 shard properties in one place only
Previously, shard properties were duplicated across all Q*N shards in the cache. If we needed to access them we loaded all the shards (from ets or disk), and then immediately threw them all away except the first one. To optimise and clean up properties put them in their own ?OPTS ets table. Item lookup, updating, and cleanup mirrors the behavior of ?SHARDS. There are a few other related optimisations and cleanups: * In the `for_docid` function we calculated the hash twice: once, when we calculated the `HashKey` for the ets selector, then again, in the `load_shards_from_disk(DbName, DocId)` if we loaded shards from disk. To optimise it, calculate the `HashKey` once and pass it on as `load_shards_from_disk(DbName, HashKey)`. * Previously, we didn't cache the properties for the shards dbs itself, so add a way to do that. If shards db changes the changes feed will restart ,and then the shards dbs properties will update again. These properties may be used used in the `_all_docs` call for instance, so having it cached would help not having to load it from disk. * Remove functions which were not used anywhere, and stop exporting functions which are used locally only: `mem3:engine/1`, `find_dirty_shards/0`, `gen_engine_opt/1`, `get_props_opt/1`, `get_shard_props/1`. * For mem3 shards and opts ets tables, since there could be multiple pending writers in different processes trying to update different entries, it makes sense to also enable `{write_concurrency, auto}` for those public tables. See: https://www.erlang.org/doc/apps/stdlib/ets#new_2_write_concurrency
1 parent 7bcf1a5 commit 3754deb

File tree

9 files changed

+130
-146
lines changed

9 files changed

+130
-146
lines changed

src/chttpd/src/chttpd_db.erl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,7 @@ delete_db_req(#httpd{} = Req, DbName) ->
454454
end.
455455

456456
do_db_req(#httpd{path_parts = [DbName | _], user_ctx = Ctx} = Req, Fun) ->
457-
Shard = hd(mem3:shards(DbName)),
458-
Props = couch_util:get_value(props, Shard#shard.opts, []),
457+
Props = mem3:props(DbName),
459458
Opts =
460459
case Ctx of
461460
undefined ->

src/fabric/src/fabric.erl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,7 @@ doc(Db0, {_} = Doc) ->
670670
true ->
671671
Db0;
672672
false ->
673-
Shard = hd(mem3:shards(Db0)),
674-
Props = couch_util:get_value(props, Shard#shard.opts, []),
673+
Props = mem3:props(Db0),
675674
{ok, Db1} = couch_db:clustered_db(Db0, [{props, Props}]),
676675
Db1
677676
end,

src/fabric/src/fabric_util.erl

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -296,15 +296,12 @@ is_users_db(DbName) ->
296296
path_ends_with(Path, Suffix) ->
297297
Suffix =:= couch_db:dbname_suffix(Path).
298298

299-
open_cluster_db(#shard{dbname = DbName, opts = Options}) ->
300-
case couch_util:get_value(props, Options) of
301-
Props when is_list(Props) ->
302-
{ok, Db} = couch_db:clustered_db(DbName, [{props, Props}]),
303-
Db;
304-
_ ->
305-
{ok, Db} = couch_db:clustered_db(DbName, []),
306-
Db
307-
end.
299+
open_cluster_db(#shard{dbname = DbName}) ->
300+
open_cluster_db(DbName);
301+
open_cluster_db(DbName) when is_binary(DbName) ->
302+
Props = mem3:props(DbName),
303+
{ok, Db} = couch_db:clustered_db(DbName, [{props, Props}]),
304+
Db.
308305

309306
open_cluster_db(DbName, Opts) ->
310307
% as admin
@@ -320,25 +317,22 @@ kv(Item, Count) ->
320317
doc_id_and_rev(#doc{id = DocId, revs = {RevNum, [RevHash | _]}}) ->
321318
{DocId, {RevNum, RevHash}}.
322319

323-
is_partitioned(DbName0) when is_binary(DbName0) ->
324-
Shards = mem3:shards(fabric:dbname(DbName0)),
325-
is_partitioned(open_cluster_db(hd(Shards)));
320+
is_partitioned(DbName) when is_binary(DbName) ->
321+
is_partitioned(open_cluster_db(DbName));
326322
is_partitioned(Db) ->
327323
couch_db:is_partitioned(Db).
328324

329325
validate_all_docs_args(DbName, Args) when is_list(DbName) ->
330326
validate_all_docs_args(list_to_binary(DbName), Args);
331327
validate_all_docs_args(DbName, Args) when is_binary(DbName) ->
332-
Shards = mem3:shards(fabric:dbname(DbName)),
333-
Db = open_cluster_db(hd(Shards)),
328+
Db = open_cluster_db(DbName),
334329
validate_all_docs_args(Db, Args);
335330
validate_all_docs_args(Db, Args) ->
336331
true = couch_db:is_clustered(Db),
337332
couch_mrview_util:validate_all_docs_args(Db, Args).
338333

339334
validate_args(DbName, DDoc, Args) when is_binary(DbName) ->
340-
Shards = mem3:shards(fabric:dbname(DbName)),
341-
Db = open_cluster_db(hd(Shards)),
335+
Db = open_cluster_db(DbName),
342336
validate_args(Db, DDoc, Args);
343337
validate_args(Db, DDoc, Args) ->
344338
true = couch_db:is_clustered(Db),

src/fabric/test/eunit/fabric_bench_test.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ t_old_db_deletion_works(_Ctx) ->
5959
% Quick db creation and deletion is racy so
6060
% we have to wait until the db is gone before proceeding.
6161
WaitFun = fun() ->
62-
try mem3_shards:opts_for_db(Db) of
62+
try mem3:props(Db) of
6363
_ -> wait
6464
catch
6565
error:database_does_not_exist ->

src/mem3/include/mem3.hrl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
dbname :: binary() | 'undefined',
2323
range :: [non_neg_integer() | '$1' | '$2'] | '_' | 'undefined',
2424
ref :: reference() | '_' | 'undefined',
25-
opts :: list() | 'undefined'
25+
opts = []:: list() | 'undefined'
2626
}).
2727

2828
%% Do not reference outside of mem3.
@@ -33,7 +33,7 @@
3333
range :: [non_neg_integer() | '$1' | '$2'] | '_',
3434
ref :: reference() | 'undefined' | '_',
3535
order :: non_neg_integer() | 'undefined' | '_',
36-
opts :: list()
36+
opts = []:: list()
3737
}).
3838

3939
%% types

src/mem3/src/mem3.erl

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
restart/0,
1919
nodes/0,
2020
node_info/2,
21+
props/1,
2122
shards/1, shards/2,
2223
choose_shards/2,
2324
n/1, n/2,
@@ -40,7 +41,7 @@
4041
-export([generate_shard_suffix/0]).
4142

4243
%% For mem3 use only.
43-
-export([name/1, node/1, range/1, engine/1]).
44+
-export([name/1, node/1, range/1]).
4445

4546
-include_lib("mem3/include/mem3.hrl").
4647

@@ -115,6 +116,11 @@ nodes() ->
115116
node_info(Node, Key) ->
116117
mem3_nodes:get_node_info(Node, Key).
117118

119+
-spec props(DbName :: iodata()) -> [].
120+
props(DbName) ->
121+
Opts = mem3_shards:opts_for_db(DbName),
122+
couch_util:get_value(props, Opts, []).
123+
118124
-spec shards(DbName :: iodata()) -> [#shard{}].
119125
shards(DbName) ->
120126
shards_int(DbName, []).
@@ -135,8 +141,7 @@ shards_int(DbName, Options) ->
135141
name = ShardDbName,
136142
dbname = ShardDbName,
137143
range = [0, (2 bsl 31) - 1],
138-
order = undefined,
139-
opts = []
144+
order = undefined
140145
}
141146
];
142147
ShardDbName ->
@@ -147,8 +152,7 @@ shards_int(DbName, Options) ->
147152
node = config:node_name(),
148153
name = ShardDbName,
149154
dbname = ShardDbName,
150-
range = [0, (2 bsl 31) - 1],
151-
opts = []
155+
range = [0, (2 bsl 31) - 1]
152156
}
153157
];
154158
_ ->
@@ -416,18 +420,6 @@ name(#ordered_shard{name = Name}) ->
416420
owner(DbName, DocId, Nodes) ->
417421
hd(mem3_util:rotate_list({DbName, DocId}, lists:usort(Nodes))).
418422

419-
engine(#shard{opts = Opts}) ->
420-
engine(Opts);
421-
engine(#ordered_shard{opts = Opts}) ->
422-
engine(Opts);
423-
engine(Opts) when is_list(Opts) ->
424-
case couch_util:get_value(engine, Opts) of
425-
Engine when is_binary(Engine) ->
426-
[{engine, Engine}];
427-
_ ->
428-
[]
429-
end.
430-
431423
%% Check whether a node is up or down
432424
%% side effect: set up a connection to Node if there not yet is one.
433425

src/mem3/src/mem3_hash.erl

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,35 @@
2323
-include_lib("mem3/include/mem3.hrl").
2424
-include_lib("couch/include/couch_db.hrl").
2525

26-
calculate(#shard{opts = Opts}, DocId) ->
27-
Props = couch_util:get_value(props, Opts, []),
28-
MFA = get_hash_fun_int(Props),
26+
calculate(#shard{dbname = DbName}, DocId) ->
27+
MFA = get_hash_fun(DbName),
2928
calculate(MFA, DocId);
30-
calculate(#ordered_shard{opts = Opts}, DocId) ->
31-
Props = couch_util:get_value(props, Opts, []),
32-
MFA = get_hash_fun_int(Props),
29+
calculate(#ordered_shard{dbname = DbName}, DocId) ->
30+
MFA = get_hash_fun(DbName),
3331
calculate(MFA, DocId);
3432
calculate(DbName, DocId) when is_binary(DbName) ->
3533
MFA = get_hash_fun(DbName),
3634
calculate(MFA, DocId);
35+
calculate(Props, DocId) when is_list(Props) ->
36+
MFA = get_hash_fun(Props),
37+
calculate(MFA, DocId);
3738
calculate({Mod, Fun, Args}, DocId) ->
3839
erlang:apply(Mod, Fun, [DocId | Args]).
3940

40-
get_hash_fun(#shard{opts = Opts}) ->
41-
get_hash_fun_int(Opts);
42-
get_hash_fun(#ordered_shard{opts = Opts}) ->
43-
get_hash_fun_int(Opts);
41+
get_hash_fun(#shard{dbname = DbName}) ->
42+
get_hash_fun(DbName);
43+
get_hash_fun(#ordered_shard{dbname = DbName}) ->
44+
get_hash_fun(DbName);
4445
get_hash_fun(DbName0) when is_binary(DbName0) ->
4546
DbName = mem3:dbname(DbName0),
4647
try
47-
[#shard{opts = Opts} | _] = mem3_shards:for_db(DbName),
48-
get_hash_fun_int(couch_util:get_value(props, Opts, []))
48+
get_hash_fun_int(mem3:props(DbName))
4949
catch
5050
error:database_does_not_exist ->
5151
{?MODULE, crc32, []}
52-
end.
52+
end;
53+
get_hash_fun(Props) when is_list(Props) ->
54+
get_hash_fun_int(Props).
5355

5456
crc32(Item) when is_binary(Item) ->
5557
erlang:crc32(Item);

0 commit comments

Comments
 (0)