diff --git a/src/box/box.cc b/src/box/box.cc index 32b08e3600686414b920430332714f18a87c63eb..4cf7dd13d0b12e078b2133aa69623bb59917f43b 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -741,6 +741,15 @@ box_set_vinyl_max_tuple_size(void) cfg_geti("vinyl_max_tuple_size")); } +void +box_set_vinyl_cache(void) +{ + struct vinyl_engine *vinyl; + vinyl = (struct vinyl_engine *)engine_by_name("vinyl"); + assert(vinyl != NULL); + vinyl_engine_set_cache(vinyl, cfg_geti64("vinyl_cache")); +} + void box_set_vinyl_timeout(void) { @@ -1539,12 +1548,12 @@ engine_init() struct vinyl_engine *vinyl; vinyl = vinyl_engine_new_xc(cfg_gets("vinyl_dir"), cfg_geti64("vinyl_memory"), - cfg_geti64("vinyl_cache"), cfg_geti("vinyl_read_threads"), cfg_geti("vinyl_write_threads"), cfg_geti("force_recovery")); engine_register((struct engine *)vinyl); box_set_vinyl_max_tuple_size(); + box_set_vinyl_cache(); box_set_vinyl_timeout(); } diff --git a/src/box/box.h b/src/box/box.h index 5c87da9d9ef7223514509153f989c0d3f0478658..baddcf73f00729ad0d9f2ffde7380b17c76ba033 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -171,6 +171,7 @@ void box_set_readahead(void); void box_set_checkpoint_count(void); void box_set_memtx_max_tuple_size(void); void box_set_vinyl_max_tuple_size(void); +void box_set_vinyl_cache(void); void box_set_vinyl_timeout(void); void box_set_replication_timeout(void); void box_set_replication_connect_quorum(void); diff --git a/src/box/lua/cfg.cc b/src/box/lua/cfg.cc index be7c46929784a968eb8eb2cb2d9eb62a0729fc50..5e88ca348910abca78b03c9e2a57eeeb331cc7f3 100644 --- a/src/box/lua/cfg.cc +++ b/src/box/lua/cfg.cc @@ -198,6 +198,17 @@ lbox_cfg_set_vinyl_max_tuple_size(struct lua_State *L) return 0; } +static int +lbox_cfg_set_vinyl_cache(struct lua_State *L) +{ + try { + box_set_vinyl_cache(); + } catch (Exception *) { + luaT_error(L); + } + return 0; +} + static int lbox_cfg_set_vinyl_timeout(struct lua_State *L) { @@ -259,6 +270,7 @@ box_lua_cfg_init(struct lua_State *L) {"cfg_set_read_only", lbox_cfg_set_read_only}, {"cfg_set_memtx_max_tuple_size", lbox_cfg_set_memtx_max_tuple_size}, {"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}, {"cfg_set_replication_timeout", lbox_cfg_set_replication_timeout}, {"cfg_set_replication_connect_quorum", diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua index 891d819d3c8c58f1f735c7406b79b835798451a0..d4f2128db5df6e931f26fa331e87ba8e0c22efcd 100644 --- a/src/box/lua/load_cfg.lua +++ b/src/box/lua/load_cfg.lua @@ -170,6 +170,7 @@ local dynamic_cfg = { read_only = private.cfg_set_read_only, memtx_max_tuple_size = private.cfg_set_memtx_max_tuple_size, 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, checkpoint_count = private.cfg_set_checkpoint_count, checkpoint_interval = private.checkpoint_daemon.set_checkpoint_interval, diff --git a/src/box/vinyl.c b/src/box/vinyl.c index df518fcc151d624a2132999d6051762e4c6b605d..03a54670d3f6413babd8ff37ac554b4c9a3733e3 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -2625,7 +2625,7 @@ vy_squash_schedule(struct vy_index *index, struct tuple *stmt, void /* struct vy_env */ *arg); static struct vy_env * -vy_env_new(const char *path, size_t memory, size_t cache, +vy_env_new(const char *path, size_t memory, int read_threads, int write_threads, bool force_recovery) { enum { KB = 1000, MB = 1000 * 1000 }; @@ -2700,7 +2700,7 @@ vy_env_new(const char *path, size_t memory, size_t cache, VY_QUOTA_UPDATE_INTERVAL); e->quota_timer.data = e; ev_timer_start(loop(), &e->quota_timer); - vy_cache_env_create(&e->cache_env, slab_cache, cache); + vy_cache_env_create(&e->cache_env, slab_cache); vy_run_env_create(&e->run_env); vy_log_init(e->path); return e; @@ -2742,7 +2742,7 @@ vy_env_delete(struct vy_env *e) } struct vinyl_engine * -vinyl_engine_new(const char *dir, size_t memory, size_t cache, +vinyl_engine_new(const char *dir, size_t memory, int read_threads, int write_threads, bool force_recovery) { struct vinyl_engine *vinyl = calloc(1, sizeof(*vinyl)); @@ -2752,7 +2752,7 @@ vinyl_engine_new(const char *dir, size_t memory, size_t cache, return NULL; } - vinyl->env = vy_env_new(dir, memory, cache, read_threads, + vinyl->env = vy_env_new(dir, memory, read_threads, write_threads, force_recovery); if (vinyl->env == NULL) { free(vinyl); @@ -2772,6 +2772,12 @@ vinyl_engine_shutdown(struct engine *engine) free(vinyl); } +void +vinyl_engine_set_cache(struct vinyl_engine *vinyl, size_t quota) +{ + vy_cache_env_set_quota(&vinyl->env->cache_env, quota); +} + 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 63add146b60c8e73d448518b3aebf62b9927e429..ac7afefb6e17c930651c2bda9058d647245b6a87 100644 --- a/src/box/vinyl.h +++ b/src/box/vinyl.h @@ -42,7 +42,7 @@ struct info_handler; struct vinyl_engine; struct vinyl_engine * -vinyl_engine_new(const char *dir, size_t memory, size_t cache, +vinyl_engine_new(const char *dir, size_t memory, int read_threads, int write_threads, bool force_recovery); /** @@ -51,6 +51,12 @@ vinyl_engine_new(const char *dir, size_t memory, size_t cache, void vinyl_engine_info(struct vinyl_engine *vinyl, struct info_handler *handler); +/** + * Update vinyl cache size. + */ +void +vinyl_engine_set_cache(struct vinyl_engine *vinyl, size_t quota); + /** * Update max tuple size. */ @@ -76,11 +82,11 @@ vinyl_engine_set_too_long_threshold(struct vinyl_engine *vinyl, #include "diag.h" static inline struct vinyl_engine * -vinyl_engine_new_xc(const char *dir, size_t memory, size_t cache, +vinyl_engine_new_xc(const char *dir, size_t memory, int read_threads, int write_threads, bool force_recovery) { struct vinyl_engine *vinyl; - vinyl = vinyl_engine_new(dir, memory, cache, read_threads, + vinyl = vinyl_engine_new(dir, memory, read_threads, write_threads, force_recovery); if (vinyl == NULL) diag_raise(); diff --git a/src/box/vy_cache.c b/src/box/vy_cache.c index 6bededc130e624a55ffb62753ad94e3058fbfd38..1c3e3692a4d3fe11402cc723bdd156d9f3e506cd 100644 --- a/src/box/vy_cache.c +++ b/src/box/vy_cache.c @@ -30,6 +30,7 @@ */ #include "vy_cache.h" #include "diag.h" +#include "fiber.h" #include "schema_def.h" #ifndef CT_ASSERT_G @@ -51,12 +52,11 @@ enum { }; void -vy_cache_env_create(struct vy_cache_env *e, struct slab_cache *slab_cache, - size_t mem_quota) +vy_cache_env_create(struct vy_cache_env *e, struct slab_cache *slab_cache) { rlist_create(&e->cache_lru); e->mem_used = 0; - e->mem_quota = mem_quota; + e->mem_quota = 0; mempool_create(&e->cache_entry_mempool, slab_cache, sizeof(struct vy_cache_entry)); } @@ -202,11 +202,30 @@ vy_cache_gc(struct vy_cache_env *env) } } +void +vy_cache_env_set_quota(struct vy_cache_env *env, size_t quota) +{ + env->mem_quota = quota; + while (env->mem_used > env->mem_quota) { + vy_cache_gc(env); + /* + * Make sure we don't block other tx fibers + * for too long. + */ + fiber_sleep(0); + } +} + void vy_cache_add(struct vy_cache *cache, struct tuple *stmt, struct tuple *prev_stmt, const struct tuple *key, enum iterator_type order) { + if (cache->env->mem_quota == 0) { + /* Cache is disabled. */ + return; + } + /* Delete some entries if quota overused */ vy_cache_gc(cache->env); @@ -646,12 +665,6 @@ vy_cache_iterator_next(struct vy_cache_iterator *itr, *ret = NULL; *stop = false; - /* disable cache for errinj test - let it try to read from disk */ - ERROR_INJECT(ERRINJ_VY_READ_PAGE, - { itr->search_started = true; return; }); - ERROR_INJECT(ERRINJ_VY_READ_PAGE_TIMEOUT, - { itr->search_started = true; return; }); - if (!itr->search_started) { assert(itr->curr_stmt == NULL); itr->search_started = true; @@ -688,12 +701,6 @@ vy_cache_iterator_skip(struct vy_cache_iterator *itr, *ret = NULL; *stop = false; - /* disable cache for errinj test - let it try to read from disk */ - ERROR_INJECT(ERRINJ_VY_READ_PAGE, - { itr->search_started = true; return; }); - ERROR_INJECT(ERRINJ_VY_READ_PAGE_TIMEOUT, - { itr->search_started = true; return; }); - assert(!itr->search_started || itr->version == itr->cache->version); /* @@ -756,16 +763,6 @@ vy_cache_iterator_restore(struct vy_cache_iterator *itr, const struct tuple *last_stmt, struct tuple **ret, bool *stop) { - /* disable cache for errinj test - let it try to read from disk */ - if ((errinj(ERRINJ_VY_READ_PAGE, ERRINJ_BOOL) != NULL && - errinj(ERRINJ_VY_READ_PAGE, ERRINJ_BOOL)->bparam) || - (errinj(ERRINJ_VY_READ_PAGE_TIMEOUT, ERRINJ_BOOL) != NULL && - errinj(ERRINJ_VY_READ_PAGE_TIMEOUT, ERRINJ_BOOL)->bparam)) { - *ret = NULL; - *stop = false; - return 0; - } - struct key_def *def = itr->cache->cmp_def; int dir = iterator_direction(itr->iterator_type); diff --git a/src/box/vy_cache.h b/src/box/vy_cache.h index d4e213db972db3a885e8aed808c170d810f1461f..fceda5a216fa9a6374d2c323ee92db054647563a 100644 --- a/src/box/vy_cache.h +++ b/src/box/vy_cache.h @@ -127,11 +127,9 @@ struct vy_cache_env { * Initialize common cache environment. * @param e - the environment. * @param slab_cache - source of memory. - * @param mem_quota - memory limit for the cache. */ void -vy_cache_env_create(struct vy_cache_env *env, struct slab_cache *slab_cache, - size_t mem_quota); +vy_cache_env_create(struct vy_cache_env *env, struct slab_cache *slab_cache); /** * Destroy and free resources of cache environment. @@ -140,6 +138,17 @@ vy_cache_env_create(struct vy_cache_env *env, struct slab_cache *slab_cache, void vy_cache_env_destroy(struct vy_cache_env *e); +/** + * Set memory limit for the cache. + * @param e - the environment. + * @param quota - memory limit for the cache. + * + * This function blocks until it manages to free enough memory + * to fit in the new limit. + */ +void +vy_cache_env_set_quota(struct vy_cache_env *e, size_t quota); + /** * Tuple cache (of one particular index) */ diff --git a/test/unit/vy_iterators_helper.c b/test/unit/vy_iterators_helper.c index a35f4ef9a89e352fc1fb06e19f9f3e5ac676a3cd..0e41de4b1dab85e212e0ac356d8bcceca6145c56 100644 --- a/test/unit/vy_iterators_helper.c +++ b/test/unit/vy_iterators_helper.c @@ -19,7 +19,8 @@ vy_iterator_C_test_init(size_t cache_size) memory_init(); fiber_init(fiber_c_invoke); tuple_init(NULL); - vy_cache_env_create(&cache_env, cord_slab_cache(), cache_size); + vy_cache_env_create(&cache_env, cord_slab_cache()); + vy_cache_env_set_quota(&cache_env, cache_size); vy_key_format = tuple_format_new(&vy_tuple_format_vtab, NULL, 0, 0, NULL, 0, NULL); tuple_format_ref(vy_key_format); diff --git a/test/unit/vy_point_lookup.c b/test/unit/vy_point_lookup.c index 0cc8dc223f8631c113be9e4fcc33c69dbb3caf41..52f4427e97b5eed0eff9f56db5ec1a0e766c714f 100644 --- a/test/unit/vy_point_lookup.c +++ b/test/unit/vy_point_lookup.c @@ -71,7 +71,8 @@ test_basic() vy_run_env_create(&run_env); struct vy_cache_env cache_env; - vy_cache_env_create(&cache_env, slab_cache, QUOTA); + vy_cache_env_create(&cache_env, slab_cache); + vy_cache_env_set_quota(&cache_env, QUOTA); struct vy_cache cache; uint32_t fields[] = { 0 }; diff --git a/test/vinyl/cache.result b/test/vinyl/cache.result index 117eff052f7c29db3afd53103a315c22a6df28cc..b4812297360fa89bf4d2b692828891174bc2b407 100644 --- a/test/vinyl/cache.result +++ b/test/vinyl/cache.result @@ -1012,3 +1012,70 @@ pk:info().disk.iterator.lookup s:drop() --- ... +-- +-- Cache resize +-- +vinyl_cache = box.cfg.vinyl_cache +--- +... +box.cfg{vinyl_cache = 1000 * 1000} +--- +... +s = box.schema.space.create('test', {engine = 'vinyl'}) +--- +... +_ = s:create_index('pk') +--- +... +for i = 1, 100 do s:replace{i, string.rep('x', 1000)} end +--- +... +for i = 1, 100 do s:get{i} end +--- +... +box.info.vinyl().cache.used +--- +- 107700 +... +box.cfg{vinyl_cache = 50 * 1000} +--- +... +box.info.vinyl().cache.used +--- +- 49542 +... +box.cfg{vinyl_cache = 0} +--- +... +box.info.vinyl().cache.used +--- +- 0 +... +-- Make sure cache is not populated if box.cfg.vinyl_cache is set to 0 +st1 = s.index.pk:info().cache +--- +... +#s:select() +--- +- 100 +... +for i = 1, 100 do s:get{i} end +--- +... +st2 = s.index.pk:info().cache +--- +... +st2.put.rows - st1.put.rows +--- +- 0 +... +box.info.vinyl().cache.used +--- +- 0 +... +s:drop() +--- +... +box.cfg{vinyl_cache = vinyl_cache} +--- +... diff --git a/test/vinyl/cache.test.lua b/test/vinyl/cache.test.lua index 9478a442e8e0d26bfdca8f643b88fbb24a1e56f0..9ff77cbb91fe996188b7c9e682ff3e5cb0e8b4b3 100644 --- a/test/vinyl/cache.test.lua +++ b/test/vinyl/cache.test.lua @@ -358,3 +358,27 @@ info.lookup info.get.rows pk:info().disk.iterator.lookup s:drop() + +-- +-- Cache resize +-- +vinyl_cache = box.cfg.vinyl_cache +box.cfg{vinyl_cache = 1000 * 1000} +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('pk') +for i = 1, 100 do s:replace{i, string.rep('x', 1000)} end +for i = 1, 100 do s:get{i} end +box.info.vinyl().cache.used +box.cfg{vinyl_cache = 50 * 1000} +box.info.vinyl().cache.used +box.cfg{vinyl_cache = 0} +box.info.vinyl().cache.used +-- Make sure cache is not populated if box.cfg.vinyl_cache is set to 0 +st1 = s.index.pk:info().cache +#s:select() +for i = 1, 100 do s:get{i} end +st2 = s.index.pk:info().cache +st2.put.rows - st1.put.rows +box.info.vinyl().cache.used +s:drop() +box.cfg{vinyl_cache = vinyl_cache}