[PATCH v2] Allow to increase box.cfg.vinyl_memory and memtx_memory at runtime

Vladimir Davydov vdavydov.dev at gmail.com
Wed May 30 19:43:25 MSK 2018


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> sec'")
 ...
 test_run:grep_log('test', 'waited for .* quota for too long.*')
 ---
-- 'waited for 699089 bytes of vinyl memory quota for too long: <sec> sec'
+- 'waited for 1048615 bytes of vinyl memory quota for too long: <sec> 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




More information about the Tarantool-patches mailing list