From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Vladimir Davydov Subject: [PATCH v2] Allow to increase box.cfg.vinyl_memory and memtx_memory at runtime Date: Wed, 30 May 2018 19:43:25 +0300 Message-Id: <30492862a8caf8c8c80410d9dea284478417eb2a.1527697921.git.vdavydov.dev@gmail.com> In-Reply-To: <20180530160825.say4ia6morjcme6k@esperanza> References: <20180530160825.say4ia6morjcme6k@esperanza> To: kostja@tarantool.org Cc: tarantool-patches@freelists.org List-ID: Slab arena can grow dynamically so all we need to do is increase the quota limit. Decreasing the limits is still explicitly prohibited, because slab arena never unmaps slabs. Closes #2634 --- https://github.com/tarantool/tarantool/issues/2634 https://github.com/tarantool/tarantool/commits/gh-2634-allow-to-increase-memory-size Changes in v2: - Tune the tests to make them less cpu intensive. - Use wal_mode=none in memtx test to speed it up. - Add a test case checking that setting the limit to the same value works. src/box/box.cc | 18 +++++++++ src/box/box.h | 2 + src/box/lua/cfg.cc | 24 +++++++++++ src/box/lua/load_cfg.lua | 4 ++ src/box/memtx_engine.c | 12 ++++++ src/box/memtx_engine.h | 10 +++++ src/box/vinyl.c | 12 ++++++ src/box/vinyl.h | 13 ++++++ src/box/vy_quota.h | 1 + test/box/cfg.result | 6 +++ test/box/cfg.test.lua | 3 ++ test/box/lua/cfg_memory.lua | 10 +++++ test/box/reconfigure.result | 83 ++++++++++++++++++++++++++++++++++++++- test/box/reconfigure.test.lua | 32 +++++++++++++++ test/vinyl/quota.result | 77 ++++++++++++++++++++++++++++++++++++ test/vinyl/quota.test.lua | 33 ++++++++++++++++ test/vinyl/quota_timeout.result | 30 +++++++++++++- test/vinyl/quota_timeout.test.lua | 12 ++++++ 18 files changed, 379 insertions(+), 3 deletions(-) create mode 100644 test/box/lua/cfg_memory.lua diff --git a/src/box/box.cc b/src/box/box.cc index c728a4c5..9edf8224 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -701,6 +701,15 @@ box_set_snap_io_rate_limit(void) } void +box_set_memtx_memory(void) +{ + struct memtx_engine *memtx; + memtx = (struct memtx_engine *)engine_by_name("memtx"); + assert(memtx != NULL); + memtx_engine_set_memory_xc(memtx, cfg_geti("memtx_memory")); +} + +void box_set_memtx_max_tuple_size(void) { struct memtx_engine *memtx; @@ -738,6 +747,15 @@ box_set_checkpoint_count(void) } void +box_set_vinyl_memory(void) +{ + struct vinyl_engine *vinyl; + vinyl = (struct vinyl_engine *)engine_by_name("vinyl"); + assert(vinyl != NULL); + vinyl_engine_set_memory_xc(vinyl, cfg_geti("vinyl_memory")); +} + +void box_set_vinyl_max_tuple_size(void) { struct vinyl_engine *vinyl; diff --git a/src/box/box.h b/src/box/box.h index d1a227e3..e94c48cc 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -175,7 +175,9 @@ void box_set_snap_io_rate_limit(void); void box_set_too_long_threshold(void); void box_set_readahead(void); void box_set_checkpoint_count(void); +void box_set_memtx_memory(void); void box_set_memtx_max_tuple_size(void); +void box_set_vinyl_memory(void); void box_set_vinyl_max_tuple_size(void); void box_set_vinyl_cache(void); void box_set_vinyl_timeout(void); diff --git a/src/box/lua/cfg.cc b/src/box/lua/cfg.cc index a20df6b8..5afebc94 100644 --- a/src/box/lua/cfg.cc +++ b/src/box/lua/cfg.cc @@ -177,6 +177,17 @@ lbox_cfg_set_read_only(struct lua_State *L) } static int +lbox_cfg_set_memtx_memory(struct lua_State *L) +{ + try { + box_set_memtx_memory(); + } catch (Exception *) { + luaT_error(L); + } + return 0; +} + +static int lbox_cfg_set_memtx_max_tuple_size(struct lua_State *L) { try { @@ -188,6 +199,17 @@ lbox_cfg_set_memtx_max_tuple_size(struct lua_State *L) } static int +lbox_cfg_set_vinyl_memory(struct lua_State *L) +{ + try { + box_set_vinyl_memory(); + } catch (Exception *) { + luaT_error(L); + } + return 0; +} + +static int lbox_cfg_set_vinyl_max_tuple_size(struct lua_State *L) { try { @@ -298,7 +320,9 @@ box_lua_cfg_init(struct lua_State *L) {"cfg_set_snap_io_rate_limit", lbox_cfg_set_snap_io_rate_limit}, {"cfg_set_checkpoint_count", lbox_cfg_set_checkpoint_count}, {"cfg_set_read_only", lbox_cfg_set_read_only}, + {"cfg_set_memtx_memory", lbox_cfg_set_memtx_memory}, {"cfg_set_memtx_max_tuple_size", lbox_cfg_set_memtx_max_tuple_size}, + {"cfg_set_vinyl_memory", lbox_cfg_set_vinyl_memory}, {"cfg_set_vinyl_max_tuple_size", lbox_cfg_set_vinyl_max_tuple_size}, {"cfg_set_vinyl_cache", lbox_cfg_set_vinyl_cache}, {"cfg_set_vinyl_timeout", lbox_cfg_set_vinyl_timeout}, diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua index cebc42fe..0b668cdc 100644 --- a/src/box/lua/load_cfg.lua +++ b/src/box/lua/load_cfg.lua @@ -191,7 +191,9 @@ local dynamic_cfg = { too_long_threshold = private.cfg_set_too_long_threshold, snap_io_rate_limit = private.cfg_set_snap_io_rate_limit, read_only = private.cfg_set_read_only, + memtx_memory = private.cfg_set_memtx_memory, memtx_max_tuple_size = private.cfg_set_memtx_max_tuple_size, + vinyl_memory = private.cfg_set_vinyl_memory, vinyl_max_tuple_size = private.cfg_set_vinyl_max_tuple_size, vinyl_cache = private.cfg_set_vinyl_cache, vinyl_timeout = private.cfg_set_vinyl_timeout, @@ -219,6 +221,8 @@ local dynamic_cfg = { local dynamic_cfg_skip_at_load = { wal_mode = true, listen = true, + memtx_memory = true, + vinyl_memory = true, replication = true, replication_timeout = true, replication_connect_timeout = true, diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c index 1d3d4943..89863e0f 100644 --- a/src/box/memtx_engine.c +++ b/src/box/memtx_engine.c @@ -1044,6 +1044,18 @@ memtx_engine_set_snap_io_rate_limit(struct memtx_engine *memtx, double limit) memtx->snap_io_rate_limit = limit * 1024 * 1024; } +int +memtx_engine_set_memory(struct memtx_engine *memtx, size_t size) +{ + if (size < quota_total(&memtx->quota)) { + diag_set(ClientError, ER_CFG, "memtx_memory", + "cannot decrease memory size at runtime"); + return -1; + } + quota_set(&memtx->quota, size); + return 0; +} + void memtx_engine_set_max_tuple_size(struct memtx_engine *memtx, size_t max_size) { diff --git a/src/box/memtx_engine.h b/src/box/memtx_engine.h index 97dba177..0f8e92ee 100644 --- a/src/box/memtx_engine.h +++ b/src/box/memtx_engine.h @@ -190,6 +190,9 @@ memtx_engine_recover_snapshot(struct memtx_engine *memtx, void memtx_engine_set_snap_io_rate_limit(struct memtx_engine *memtx, double limit); +int +memtx_engine_set_memory(struct memtx_engine *memtx, size_t size); + void memtx_engine_set_max_tuple_size(struct memtx_engine *memtx, size_t max_size); @@ -258,6 +261,13 @@ memtx_engine_new_xc(const char *snap_dirname, bool force_recovery, } static inline void +memtx_engine_set_memory_xc(struct memtx_engine *memtx, size_t size) +{ + if (memtx_engine_set_memory(memtx, size) != 0) + diag_raise(); +} + +static inline void memtx_engine_recover_snapshot_xc(struct memtx_engine *memtx, const struct vclock *vclock) { diff --git a/src/box/vinyl.c b/src/box/vinyl.c index f0d26874..4734670b 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -2766,6 +2766,18 @@ vinyl_engine_set_cache(struct vinyl_engine *vinyl, size_t quota) vy_cache_env_set_quota(&vinyl->env->cache_env, quota); } +int +vinyl_engine_set_memory(struct vinyl_engine *vinyl, size_t size) +{ + if (size < vinyl->env->quota.limit) { + diag_set(ClientError, ER_CFG, "vinyl_memory", + "cannot decrease memory size at runtime"); + return -1; + } + vy_quota_set_limit(&vinyl->env->quota, size); + return 0; +} + void vinyl_engine_set_max_tuple_size(struct vinyl_engine *vinyl, size_t max_size) { diff --git a/src/box/vinyl.h b/src/box/vinyl.h index ac7afefb..4e9f91f0 100644 --- a/src/box/vinyl.h +++ b/src/box/vinyl.h @@ -58,6 +58,12 @@ void vinyl_engine_set_cache(struct vinyl_engine *vinyl, size_t quota); /** + * Update vinyl memory size. + */ +int +vinyl_engine_set_memory(struct vinyl_engine *vinyl, size_t size); + +/** * Update max tuple size. */ void @@ -93,6 +99,13 @@ vinyl_engine_new_xc(const char *dir, size_t memory, return vinyl; } +static inline void +vinyl_engine_set_memory_xc(struct vinyl_engine *vinyl, size_t size) +{ + if (vinyl_engine_set_memory(vinyl, size) != 0) + diag_raise(); +} + #endif /* defined(__plusplus) */ #endif /* INCLUDES_TARANTOOL_BOX_VINYL_H */ diff --git a/src/box/vy_quota.h b/src/box/vy_quota.h index 89f88bdc..4788b0c9 100644 --- a/src/box/vy_quota.h +++ b/src/box/vy_quota.h @@ -110,6 +110,7 @@ vy_quota_set_limit(struct vy_quota *q, size_t limit) q->limit = q->watermark = limit; if (q->used >= limit) q->quota_exceeded_cb(q); + fiber_cond_broadcast(&q->cond); } /** diff --git a/test/box/cfg.result b/test/box/cfg.result index 66080795..ff208f9c 100644 --- a/test/box/cfg.result +++ b/test/box/cfg.result @@ -260,6 +260,12 @@ box.cfg{replicaset_uuid = '12345678-0123-5678-1234-abcdefabcdef'} --- - error: Can't set option 'replicaset_uuid' dynamically ... +box.cfg{memtx_memory = box.cfg.memtx_memory} +--- +... +box.cfg{vinyl_memory = box.cfg.vinyl_memory} +--- +... -------------------------------------------------------------------------------- -- Test of default cfg options -------------------------------------------------------------------------------- diff --git a/test/box/cfg.test.lua b/test/box/cfg.test.lua index 52808960..8bed7083 100644 --- a/test/box/cfg.test.lua +++ b/test/box/cfg.test.lua @@ -33,6 +33,9 @@ box.cfg{instance_uuid = '12345678-0123-5678-1234-abcdefabcdef'} box.cfg{replicaset_uuid = box.info.cluster.uuid} box.cfg{replicaset_uuid = '12345678-0123-5678-1234-abcdefabcdef'} +box.cfg{memtx_memory = box.cfg.memtx_memory} +box.cfg{vinyl_memory = box.cfg.vinyl_memory} + -------------------------------------------------------------------------------- -- Test of default cfg options -------------------------------------------------------------------------------- diff --git a/test/box/lua/cfg_memory.lua b/test/box/lua/cfg_memory.lua new file mode 100644 index 00000000..816b8342 --- /dev/null +++ b/test/box/lua/cfg_memory.lua @@ -0,0 +1,10 @@ +#!/usr/bin/env tarantool + +local LIMIT = tonumber(arg[1]) + +box.cfg{ + wal_mode = 'none', + memtx_memory = LIMIT, +} + +require('console').listen(os.getenv('ADMIN')) diff --git a/test/box/reconfigure.result b/test/box/reconfigure.result index ad054db7..9ed1864e 100644 --- a/test/box/reconfigure.result +++ b/test/box/reconfigure.result @@ -1,3 +1,6 @@ +test_run = require('test_run').new() +--- +... too_long_threshold_default = box.cfg.too_long_threshold --- ... @@ -95,7 +98,8 @@ box.cfg{log="new logger"} -- bad1 box.cfg{memtx_memory=53687091} --- -- error: Can't set option 'memtx_memory' dynamically +- error: 'Incorrect value for option ''memtx_memory'': cannot decrease memory size + at runtime' ... box.cfg.memtx_memory --- @@ -125,3 +129,80 @@ box.cfg { too_long_threshold = too_long_threshold_default } box.cfg { io_collect_interval = io_collect_interval_default } --- ... +-- +-- gh-2634: check that box.cfg.memtx_memory can be increased +-- +test_run:cmd("create server test with script='box/lua/cfg_memory.lua'") +--- +- true +... +test_run:cmd(string.format("start server test with args='%d'", 48 * 1024 * 1024)) +--- +- true +... +test_run:cmd("switch test") +--- +- true +... +box.slab.info().quota_size +--- +- 50331648 +... +s = box.schema.space.create('test') +--- +... +_ = s:create_index('pk') +--- +... +count = 200 +--- +... +pad = string.rep('x', 100 * 1024) +--- +... +for i = 1, count do s:replace{i, pad} end -- error: not enough memory +--- +- error: Failed to allocate 102424 bytes in slab allocator for memtx_tuple +... +s:count() < count +--- +- true +... +box.cfg{memtx_memory = 64 * 1024 * 1024} +--- +... +box.slab.info().quota_size +--- +- 67108864 +... +for i = s:count() + 1, count do s:replace{i, pad} end -- ok +--- +... +s:count() == count +--- +- true +... +s:drop() +--- +... +box.cfg{memtx_memory = 48 * 1024 * 1024} -- error: decreasing memtx_memory is not allowed +--- +- error: 'Incorrect value for option ''memtx_memory'': cannot decrease memory size + at runtime' +... +box.slab.info().quota_size +--- +- 67108864 +... +test_run:cmd("switch default") +--- +- true +... +test_run:cmd("stop server test") +--- +- true +... +test_run:cmd("cleanup server test") +--- +- true +... diff --git a/test/box/reconfigure.test.lua b/test/box/reconfigure.test.lua index 1af99f6b..4db29eef 100644 --- a/test/box/reconfigure.test.lua +++ b/test/box/reconfigure.test.lua @@ -1,3 +1,5 @@ +test_run = require('test_run').new() + too_long_threshold_default = box.cfg.too_long_threshold io_collect_interval_default = box.cfg.io_collect_interval @@ -49,3 +51,33 @@ box.cfg.io_collect_interval = nil box.cfg { too_long_threshold = too_long_threshold_default } box.cfg { io_collect_interval = io_collect_interval_default } + +-- +-- gh-2634: check that box.cfg.memtx_memory can be increased +-- +test_run:cmd("create server test with script='box/lua/cfg_memory.lua'") +test_run:cmd(string.format("start server test with args='%d'", 48 * 1024 * 1024)) +test_run:cmd("switch test") + +box.slab.info().quota_size + +s = box.schema.space.create('test') +_ = s:create_index('pk') +count = 200 +pad = string.rep('x', 100 * 1024) +for i = 1, count do s:replace{i, pad} end -- error: not enough memory +s:count() < count + +box.cfg{memtx_memory = 64 * 1024 * 1024} +box.slab.info().quota_size + +for i = s:count() + 1, count do s:replace{i, pad} end -- ok +s:count() == count +s:drop() + +box.cfg{memtx_memory = 48 * 1024 * 1024} -- error: decreasing memtx_memory is not allowed +box.slab.info().quota_size + +test_run:cmd("switch default") +test_run:cmd("stop server test") +test_run:cmd("cleanup server test") diff --git a/test/vinyl/quota.result b/test/vinyl/quota.result index 1fa773dd..359efe8b 100644 --- a/test/vinyl/quota.result +++ b/test/vinyl/quota.result @@ -94,3 +94,80 @@ box.info.vinyl().quota.used space:drop() --- ... +-- +-- gh-2634: check that box.cfg.vinyl_memory can be increased +-- +test_run:cmd("create server test with script='vinyl/low_quota.lua'") +--- +- true +... +test_run:cmd(string.format("start server test with args='%d'", 1024 * 1024)) +--- +- true +... +test_run:cmd('switch test') +--- +- true +... +s = box.schema.space.create('test', {engine = 'vinyl'}) +--- +... +_ = s:create_index('pk') +--- +... +count = 20 +--- +... +pad = string.rep('x', 100 * 1024) +--- +... +box.info.vinyl().quota.limit +--- +- 1048576 +... +for i = 1, count do s:replace{i, pad} end -- triggers dump +--- +... +box.info.vinyl().quota.used < count * pad:len() +--- +- true +... +box.snapshot() +--- +- ok +... +box.cfg{vinyl_memory = 8 * 1024 * 1024} +--- +... +box.info.vinyl().quota.limit +--- +- 8388608 +... +for i = 1, count do s:replace{i, pad} end -- does not trigger dump +--- +... +box.info.vinyl().quota.used > count * pad:len() +--- +- true +... +box.cfg{vinyl_memory = 4 * 1024 * 1024} -- error: decreasing vinyl_memory is not allowed +--- +- error: 'Incorrect value for option ''vinyl_memory'': cannot decrease memory size + at runtime' +... +box.info.vinyl().quota.limit +--- +- 8388608 +... +test_run:cmd('switch default') +--- +- true +... +test_run:cmd("stop server test") +--- +- true +... +test_run:cmd("cleanup server test") +--- +- true +... diff --git a/test/vinyl/quota.test.lua b/test/vinyl/quota.test.lua index c4cea8fb..7f7b4ec3 100644 --- a/test/vinyl/quota.test.lua +++ b/test/vinyl/quota.test.lua @@ -49,3 +49,36 @@ _ = space:replace{1, 1, string.rep('a', 1024 * 1024 * 5)} box.info.vinyl().quota.used space:drop() + +-- +-- gh-2634: check that box.cfg.vinyl_memory can be increased +-- +test_run:cmd("create server test with script='vinyl/low_quota.lua'") +test_run:cmd(string.format("start server test with args='%d'", 1024 * 1024)) +test_run:cmd('switch test') + +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('pk') + +count = 20 +pad = string.rep('x', 100 * 1024) + +box.info.vinyl().quota.limit + +for i = 1, count do s:replace{i, pad} end -- triggers dump +box.info.vinyl().quota.used < count * pad:len() + +box.snapshot() + +box.cfg{vinyl_memory = 8 * 1024 * 1024} +box.info.vinyl().quota.limit + +for i = 1, count do s:replace{i, pad} end -- does not trigger dump +box.info.vinyl().quota.used > count * pad:len() + +box.cfg{vinyl_memory = 4 * 1024 * 1024} -- error: decreasing vinyl_memory is not allowed +box.info.vinyl().quota.limit + +test_run:cmd('switch default') +test_run:cmd("stop server test") +test_run:cmd("cleanup server test") diff --git a/test/vinyl/quota_timeout.result b/test/vinyl/quota_timeout.result index e4bced02..e977fb52 100644 --- a/test/vinyl/quota_timeout.result +++ b/test/vinyl/quota_timeout.result @@ -65,6 +65,29 @@ box.info.vinyl().quota.used --- - 748241 ... +-- +-- Check that increasing box.cfg.vinyl_memory wakes up fibers +-- waiting for memory. +-- +box.cfg{vinyl_timeout=5} +--- +... +c = fiber.channel(1) +--- +... +_ = fiber.create(function() local ok = pcall(s.auto_increment, s, {pad}) c:put(ok) end) +--- +... +fiber.sleep(0.01) +--- +... +box.cfg{vinyl_memory = 3 * box.cfg.vinyl_memory / 2} +--- +... +c:get(1) +--- +- true +... box.error.injection.set('ERRINJ_VY_RUN_WRITE', false) --- - ok @@ -86,6 +109,9 @@ box.cfg{vinyl_timeout=60} box.cfg{too_long_threshold=0.01} --- ... +pad = string.rep('x', 2 * box.cfg.vinyl_memory / 3) +--- +... _ = s:auto_increment{pad} --- ... @@ -98,7 +124,7 @@ test_run:cmd("push filter '[0-9.]+ sec' to ' sec'") ... test_run:grep_log('test', 'waited for .* quota for too long.*') --- -- 'waited for 699089 bytes of vinyl memory quota for too long: sec' +- 'waited for 1048615 bytes of vinyl memory quota for too long: sec' ... test_run:cmd("clear filter") --- @@ -134,7 +160,7 @@ pad = string.rep('x', box.cfg.vinyl_memory) ... _ = s:auto_increment{pad} --- -- error: Failed to allocate 1048615 bytes in lsregion for vinyl transaction +- error: Failed to allocate 1572903 bytes in lsregion for vinyl transaction ... s:drop() --- diff --git a/test/vinyl/quota_timeout.test.lua b/test/vinyl/quota_timeout.test.lua index c3d17b44..6ea6d677 100644 --- a/test/vinyl/quota_timeout.test.lua +++ b/test/vinyl/quota_timeout.test.lua @@ -29,6 +29,17 @@ _ = s:auto_increment{pad} s:count() box.info.vinyl().quota.used +-- +-- Check that increasing box.cfg.vinyl_memory wakes up fibers +-- waiting for memory. +-- +box.cfg{vinyl_timeout=5} +c = fiber.channel(1) +_ = fiber.create(function() local ok = pcall(s.auto_increment, s, {pad}) c:put(ok) end) +fiber.sleep(0.01) +box.cfg{vinyl_memory = 3 * box.cfg.vinyl_memory / 2} +c:get(1) + box.error.injection.set('ERRINJ_VY_RUN_WRITE', false) fiber.sleep(0.01) -- wait for scheduler to unthrottle @@ -41,6 +52,7 @@ box.error.injection.set('ERRINJ_VY_RUN_WRITE_TIMEOUT', 0.01) box.cfg{vinyl_timeout=60} box.cfg{too_long_threshold=0.01} +pad = string.rep('x', 2 * box.cfg.vinyl_memory / 3) _ = s:auto_increment{pad} _ = s:auto_increment{pad} -- 2.11.0