This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb.git
The following commit(s) were added to refs/heads/main by this push:
new 308817d0a Use BTree cache for views
308817d0a is described below
commit 308817d0a11db474116911d858514f6c5a1ef6cc
Author: Nick Vatamaniuc <[email protected]>
AuthorDate: Thu Jan 29 00:14:53 2026 -0500
Use BTree cache for views
In #5625 we introduced a BTree cache for dbs, and since view files
also use BTrees, let's use a cache for them as well. This should help
with concurrent view queries.
I test the improvments I used k6 for benchmarking with a 1M doc, q=16
db, and a view with 10M rows. With a 400 rps request arrival rate saw
more than a 50% improvement in average, p90, and p95 latencies:
* Average: 14 -> 7 msec
* Median: 12 -> 7 msec
* P90 21 -> 10 msec
* P95 27 -> 12 msec
* Max 120 -> 77 msec
Since view results could have large kp node values due to custom
reduce functions, add max term limit for cache entries. To avoid
adding yet another config value use a few multiple of BTree chunk
size. This should help avoid config values clashing if users raise the
chunk size but forget to raise their cache term limit (it would render
the cache unusuable suddenly).
More detailed test results along with a test at a higher depth of 4 to
show further improvements.
```
BENCH_RATE=400 BENCH_DURATION=5m BENCH_VIEW_LIMIT=10 k6 run
./k6_couchdb_constant_arrival_view_query.js
HTTP
http_req_duration..............: avg=14.65ms min=2.08ms med=12.7ms
max=120.08ms p(90)=20.53ms p(95)=26.78ms
{ expected_response:true }...: avg=14.65ms min=2.08ms med=12.7ms
max=120.08ms p(90)=20.53ms p(95)=26.78ms
{ name:view_get }............: avg=14.65ms min=2.93ms med=12.7ms
max=120.08ms p(90)=20.53ms p(95)=26.78ms
http_req_failed................: 0.00% 0 out of 120002
http_reqs......................: 120002 399.891866/s
```
```
$ BENCH_RATE=400 BENCH_DURATION=5m BENCH_VIEW_LIMIT=10 k6 run
./k6_couchdb_constant_arrival_view_query.js
HTTP
http_req_duration..............: avg=7.45ms min=2.38ms med=6.9ms
max=77.78ms p(90)=10.21ms p(95)=11.5ms
{ expected_response:true }...: avg=7.45ms min=2.38ms med=6.9ms
max=77.78ms p(90)=10.21ms p(95)=11.5ms
{ name:view_get }............: avg=7.45ms min=2.38ms med=6.9ms
max=77.78ms p(90)=10.21ms p(95)=11.49ms
http_req_failed................: 0.00% 0 out of 120004
http_reqs......................: 120004 399.799018/s
```
```
$ BENCH_RATE=400 BENCH_DURATION=5m BENCH_VIEW_LIMIT=10 k6 run
./k6_couchdb_constant_arrival_view_query.js
HTTP
http_req_duration..............: avg=6.21ms min=2.22ms med=5.78ms
max=77ms p(90)=8.4ms p(95)=9.5ms
{ expected_response:true }...: avg=6.21ms min=2.22ms med=5.78ms
max=77ms p(90)=8.4ms p(95)=9.5ms
{ name:view_get }............: avg=6.21ms min=2.22ms med=5.78ms
max=77ms p(90)=8.4ms p(95)=9.5ms
http_req_failed................: 0.00% 0 out of 120003
http_reqs......................: 120003 399.931891/s
```
---
rel/overlay/etc/default.ini | 4 ++++
src/couch/src/couch_bt_engine_cache.erl | 20 ++++++++++++++------
src/couch/src/couch_btree.erl | 1 +
src/couch_mrview/src/couch_mrview_util.erl | 11 +++++++++--
4 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini
index 69aae2ad8..d599656b1 100644
--- a/rel/overlay/etc/default.ini
+++ b/rel/overlay/etc/default.ini
@@ -178,6 +178,10 @@ view_index_dir = {{view_index_dir}}
; caching set the value to 0
;db_btree_cache_depth = 3
+; Cache view btree nodes up to this depth only. Works like db_btree_cache_depth
+; but for map-reduce (view) b-trees.
+;view_btree_cache_depth = 3
+
[purge]
; Allowed maximum number of accumulated revisions in one purge request
;max_revisions_number = infinity
diff --git a/src/couch/src/couch_bt_engine_cache.erl
b/src/couch/src/couch_bt_engine_cache.erl
index a39564957..0b3d02704 100644
--- a/src/couch/src/couch_bt_engine_cache.erl
+++ b/src/couch/src/couch_bt_engine_cache.erl
@@ -13,6 +13,7 @@
-module(couch_bt_engine_cache).
-include_lib("stdlib/include/ms_transform.hrl").
+-include_lib("couch/include/couch_db.hrl").
% Main API
%
@@ -52,7 +53,7 @@
-define(MISSES, misses).
-define(FULL, full).
--record(cache, {tid, max_size}).
+-record(cache, {tid, max_size, max_term}).
% Main API
@@ -60,11 +61,11 @@ insert(Key, Term) ->
insert(Key, Term, 1).
insert(Key, Term, Priority) when is_integer(Priority) ->
- Priority1 = min(?MAX_PRIORITY, max(0, Priority)),
case get_cache(Key) of
- #cache{tid = Tid, max_size = Max} ->
- case ets:info(Tid, memory) < Max of
+ #cache{tid = Tid, max_size = Max, max_term = MaxTerm} ->
+ case ets:info(Tid, memory) < Max andalso ?term_size(Term) =<
MaxTerm of
true ->
+ Priority1 = min(?MAX_PRIORITY, max(0, Priority)),
case ets:insert_new(Tid, {Key, Priority1, Term}) of
true ->
true;
@@ -107,11 +108,13 @@ info() ->
Caches when is_tuple(Caches) ->
SizeMem = [info(C) || C <- tuple_to_list(Caches)],
MaxMem = [Max || #cache{max_size = Max} <- tuple_to_list(Caches)],
+ #cache{max_term = MaxTerm} = element(1, Caches),
{Sizes, Mem} = lists:unzip(SizeMem),
#{
size => lists:sum(Sizes),
memory => lists:sum(Mem),
max_memory => lists:sum(MaxMem) * wordsize(),
+ max_term => MaxTerm,
full => sample_metric(?FULL),
hits => sample_metric(?HITS),
misses => sample_metric(?MISSES),
@@ -207,8 +210,13 @@ new() ->
Opts = [public, {write_concurrency, true}, {read_concurrency, true}],
Max0 = round(max_size() / wordsize() / shard_count()),
% Some per-table overhead for the table metadata
- Max = Max0 + round(250 * 1024 / wordsize()),
- #cache{tid = ets:new(?MODULE, Opts), max_size = Max}.
+ MaxSize = Max0 + round(250 * 1024 / wordsize()),
+ MaxTerm = couch_btree:get_chunk_size() * 4,
+ #cache{
+ tid = ets:new(?MODULE, Opts),
+ max_size = MaxSize,
+ max_term = MaxTerm
+ }.
get_cache(Term) ->
case persistent_term:get(?PTERM_KEY, undefined) of
diff --git a/src/couch/src/couch_btree.erl b/src/couch/src/couch_btree.erl
index 1519b1fbc..1c349d002 100644
--- a/src/couch/src/couch_btree.erl
+++ b/src/couch/src/couch_btree.erl
@@ -17,6 +17,7 @@
-export([fold_reduce/4, lookup/2, set_options/2]).
-export([is_btree/1, get_state/1, get_fd/1, get_reduce_fun/1]).
-export([extract/2, assemble/3, less/3]).
+-export([get_chunk_size/0]).
-include_lib("couch/include/couch_db.hrl").
diff --git a/src/couch_mrview/src/couch_mrview_util.erl
b/src/couch_mrview/src/couch_mrview_util.erl
index 5405e8db8..494f5b3ec 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -61,6 +61,8 @@
(C >= $A andalso C =< $F))
).
+-define(DEFAULT_BTREE_CACHE_DEPTH, 3).
+
-include_lib("couch/include/couch_db.hrl").
-include_lib("couch_mrview/include/couch_mrview.hrl").
@@ -369,7 +371,8 @@ init_state(Db, Fd, State, Header) ->
}} = maybe_update_header(Header),
IdBtOpts = [
- {compression, couch_compress:get_compression_method()}
+ {compression, couch_compress:get_compression_method()},
+ {cache_depth, btree_cache_depth()}
],
{ok, IdBtree} = couch_btree:open(IdBtreeState, Fd, IdBtOpts),
@@ -394,7 +397,8 @@ open_view(_Db, Fd, Lang, ViewState, View) ->
ViewBtOpts = [
{less, LessFun},
{reduce, ReduceFun},
- {compression, Compression}
+ {compression, Compression},
+ {cache_depth, btree_cache_depth()}
],
{ok, Btree} = couch_btree:open(BTState, Fd, ViewBtOpts),
@@ -1365,3 +1369,6 @@ compact_on_collator_upgrade() ->
commit_on_header_upgrade() ->
config:get_boolean("view_upgrade", "commit_on_header_upgrade", true).
+
+btree_cache_depth() ->
+ config:get_integer("bt_engine_cache", "view_btree_cache_depth",
?DEFAULT_BTREE_CACHE_DEPTH).