diff --git a/src/box/vinyl.c b/src/box/vinyl.c index 8807f75835852242491cabea356b48d99ffbc019..ea4839dea171c3f7524b3e3ccdb908104e5c8164 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -3948,6 +3948,16 @@ vy_build_insert_tuple(struct vy_env *env, struct vy_lsm *lsm, return -1; } } + /* + * Despite the fact that we protected mem from being + * dumped, its generation still may bump due to rotation + * in vy_tx_write_prepare() (insertions during index build + * are still handled by on_replace_trigger). This may happen + * if dump process is triggered (vy_scheduler_trigger_dump()). + * Hence, to get right mem (during mem rotation it becomes + * sealed i.e. read-only) we should fetch it from lsm again. + */ + mem = lsm->mem; /* Insert the new tuple into the in-memory index. */ size_t mem_used_before = lsregion_used(&env->mem_env.allocator); diff --git a/test/vinyl/gh-4810-dump-during-index-build.result b/test/vinyl/gh-4810-dump-during-index-build.result new file mode 100644 index 0000000000000000000000000000000000000000..f55c44a1fb1811314257d69fb711c9acbf404289 --- /dev/null +++ b/test/vinyl/gh-4810-dump-during-index-build.result @@ -0,0 +1,150 @@ +-- test-run result file version 2 +test_run = require('test_run').new() + | --- + | ... + +test_run:cmd("create server test with script='vinyl/low_quota.lua'") + | --- + | - true + | ... +test_run:cmd("start server test with args='13421772'") + | --- + | - true + | ... +test_run:cmd('switch test') + | --- + | - true + | ... + +fiber = require 'fiber' + | --- + | ... + +math.randomseed(os.time()) + | --- + | ... + +s = box.schema.space.create('test', {engine = 'vinyl'}) + | --- + | ... +_ = s:create_index('pk', {parts = {1, 'unsigned'}}) + | --- + | ... + +-- +-- Purpose of this test is to trigger dump during secondary index build. +-- It is worth noting that dump must be triggered by exceeding memory +-- quota and not by invoking box.snapshot() since checkpoint process +-- is locked by DDL latch. +-- + +MAX_KEY = 1000000 + | --- + | ... +MAX_VAL = 1000000 + | --- + | ... +PADDING = string.rep('x', 100) + | --- + | ... + +test_run:cmd("setopt delimiter ';'") + | --- + | - true + | ... + +function gen_insert() + pcall(s.insert, s, {math.random(MAX_KEY), math.random(MAX_VAL), + math.random(MAX_VAL), math.random(MAX_VAL), PADDING}) +end; + | --- + | ... + +function fill_L0_without_dump() + local dump_watermark = box.cfg.vinyl_memory / 2 + while box.stat.vinyl().memory.level0 < dump_watermark do + gen_insert() + end +end; + | --- + | ... + +fill_L0_without_dump(); + | --- + | ... +assert(box.stat.vinyl().scheduler.dump_count == 0); + | --- + | - true + | ... + +function insert_loop() + while not stop do + gen_insert() + end + ch:put(true) +end; + | --- + | ... + +function idx_build() + _ = s:create_index('i1', {unique = true, parts = {2, 'unsigned', 3, 'unsigned'}}) + ch:put(true) +end; + | --- + | ... + +stop = false; + | --- + | ... +ch = fiber.channel(2); + | --- + | ... + +_ = fiber.create(idx_build); + | --- + | ... +_ = fiber.create(insert_loop); + | --- + | ... + +fiber.sleep(3); + | --- + | ... + +stop = true; + | --- + | ... +for i = 1, ch:size() do + ch:get() +end; + | --- + | ... + +test_run:cmd("setopt delimiter ''"); + | --- + | - true + | ... +assert(box.stat.vinyl().scheduler.dump_count > 0) + | --- + | - true + | ... +assert(s.index.i1 ~= nil) + | --- + | - true + | ... +s:drop() + | --- + | ... + +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/gh-4810-dump-during-index-build.test.lua b/test/vinyl/gh-4810-dump-during-index-build.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..7b7026498b415976fbd11e8a65d4f9faa6d46055 --- /dev/null +++ b/test/vinyl/gh-4810-dump-during-index-build.test.lua @@ -0,0 +1,74 @@ +test_run = require('test_run').new() + +test_run:cmd("create server test with script='vinyl/low_quota.lua'") +test_run:cmd("start server test with args='13421772'") +test_run:cmd('switch test') + +fiber = require 'fiber' + +math.randomseed(os.time()) + +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('pk', {parts = {1, 'unsigned'}}) + +-- +-- Purpose of this test is to trigger dump during secondary index build. +-- It is worth noting that dump must be triggered by exceeding memory +-- quota and not by invoking box.snapshot() since checkpoint process +-- is locked by DDL latch. +-- + +MAX_KEY = 1000000 +MAX_VAL = 1000000 +PADDING = string.rep('x', 100) + +test_run:cmd("setopt delimiter ';'") + +function gen_insert() + pcall(s.insert, s, {math.random(MAX_KEY), math.random(MAX_VAL), + math.random(MAX_VAL), math.random(MAX_VAL), PADDING}) +end; + +function fill_L0_without_dump() + local dump_watermark = box.cfg.vinyl_memory / 2 + while box.stat.vinyl().memory.level0 < dump_watermark do + gen_insert() + end +end; + +fill_L0_without_dump(); +assert(box.stat.vinyl().scheduler.dump_count == 0); + +function insert_loop() + while not stop do + gen_insert() + end + ch:put(true) +end; + +function idx_build() + _ = s:create_index('i1', {unique = true, parts = {2, 'unsigned', 3, 'unsigned'}}) + ch:put(true) +end; + +stop = false; +ch = fiber.channel(2); + +_ = fiber.create(idx_build); +_ = fiber.create(insert_loop); + +fiber.sleep(3); + +stop = true; +for i = 1, ch:size() do + ch:get() +end; + +test_run:cmd("setopt delimiter ''"); +assert(box.stat.vinyl().scheduler.dump_count > 0) +assert(s.index.i1 ~= nil) +s:drop() + +test_run:cmd('switch default') +test_run:cmd("stop server test") +test_run:cmd("cleanup server test")