diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c index 1f67bea52f9f2304faefaec4bffee4a33c829f10..66ed38bf71bcdc766058c1a0b1e1bb3b84e500ee 100644 --- a/src/box/vy_lsm.c +++ b/src/box/vy_lsm.c @@ -701,6 +701,15 @@ vy_lsm_compaction_priority(struct vy_lsm *lsm) struct vy_range *range = vy_range_heap_top(&lsm->range_heap); if (range == NULL) return 0; + /* + * There's no point in compacting dropped LSM trees. Moreover, since we + * don't commit a new run for a dropped LSM tree so as not to mess with + * garbage collection (see vy_task_compaction_complete()), enabling + * compaction in this case would result in rescheduling it over and + * over again, which is no good. + */ + if (lsm->is_dropped) + return 0; return range->compaction_priority; } diff --git a/src/box/vy_scheduler.c b/src/box/vy_scheduler.c index b3e4120cdecfecbafa5ba4f61d0d855aa5fe08c5..dd65729c6ba44352f382dcbaae746e7c1ce56042 100644 --- a/src/box/vy_scheduler.c +++ b/src/box/vy_scheduler.c @@ -1453,6 +1453,17 @@ vy_task_compaction_complete(struct vy_task *task) struct vy_slice *slice, *next_slice, *new_slice = NULL; struct vy_run *run; + /* + * The LSM tree could have been dropped while we were writing the new + * run. In this case we should discard the run without committing to + * vylog, because all the information about the LSM tree and its runs + * could have already been garbage collected from vylog. + */ + if (lsm->is_dropped) { + vy_run_unref(new_run); + goto out; + } + /* * Allocate a slice of the new run. * @@ -1580,7 +1591,7 @@ vy_task_compaction_complete(struct vy_task *task) vy_slice_wait_pinned(slice); vy_slice_delete(slice); } - +out: /* The iterator has been cleaned up in worker. */ task->wi->iface->close(task->wi); diff --git a/test/vinyl/gh-5141-invalid-vylog-file.result b/test/vinyl/gh-5141-invalid-vylog-file.result deleted file mode 100644 index 15caa75a91e0335bf2b35867f2fd1ae6f6c50c0f..0000000000000000000000000000000000000000 --- a/test/vinyl/gh-5141-invalid-vylog-file.result +++ /dev/null @@ -1,140 +0,0 @@ --- test-run result file version 2 -test_run = require('test_run').new() - | --- - | ... -test_run:cmd("push filter 'Invalid VYLOG file: Slice [0-9]+ deleted but not registered'" .. \ - "to 'Invalid VYLOG file: Slice <NUM> deleted but not registered'") - | --- - | - true - | ... - --- restart the current server to avoid of issues from previous runs -test_run:cmd("restart server default with cleanup=True") - | - --- Let's test number of upserts in one transaction that exceeds --- the limit of operations allowed in one update. --- -ups_cnt = 10000 - | --- - | ... - -s = box.schema.create_space('test', {engine = 'vinyl'}) - | --- - | ... -pk = s:create_index('pk') - | --- - | ... - -tuple = {} - | --- - | ... -for i = 1, ups_cnt do tuple[i] = i end - | --- - | ... -_ = s:insert(tuple) - | --- - | ... -box.snapshot() - | --- - | - ok - | ... - -box.begin() - | --- - | ... -for k = 1, ups_cnt do s:upsert({1}, {{'+', k, 1}}) end - | --- - | ... -box.commit() - | --- - | ... --- Upserts are not able to squash, so scheduler will get stuck. --- So let's not waste much time here, just check that no crash --- takes place. --- -box.snapshot() - | --- - | - ok - | ... -require('fiber').sleep(0.01) - | --- - | ... - -s:drop() - | --- - | ... - --- --- WARNING: do not split from previous subtest. --- gh-5141: vinyl: after test with number of upserts in one transaction --- that exceeded the limit of operations allowed in one update, then --- box.snapshot() call may fail with error: --- "Invalid VYLOG file: Slice <NUM> deleted but not registered". --- To avoid of the issue tarantool server restart is needed. --- - --- let's check the issue reproduced -s0 = box.schema.space.create('tweedledum', {engine = 'vinyl'}) - | --- - | ... -i0 = s0:create_index('primary', { type = 'tree', parts = {1, 'unsigned'}}) - | --- - | ... -cnt = 1 - | --- - | ... -s0:replace{cnt, 'tuple'} - | --- - | - [1, 'tuple'] - | ... -test_run:wait_cond(function() \ - cnt = cnt + 1 \ - s0:replace{cnt, 'tuple ' .. cnt} \ - local ok, err = pcall(box.snapshot) \ - if ok == false then \ - require('log').info( \ - "box.snapshot() returned on loop " .. cnt .. " 'err': " .. err) \ - end \ - return err:match "Invalid VYLOG file: Slice %d+ deleted but not registered" \ -end, 10) - | --- - | - 'Invalid VYLOG file: Slice <NUM> deleted but not registered' - | ... -s0:drop() - | --- - | ... - --- let's check the issue is stable -s1 = box.schema.space.create('tweedledum', {engine = 'vinyl'}) - | --- - | ... -i1 = s1:create_index('primary', { type = 'tree', parts = {1, 'unsigned'}}) - | --- - | ... -box.snapshot() - | --- - | - error: 'Invalid VYLOG file: Slice <NUM> deleted but not registered' - | ... -s1:drop() - | --- - | ... - --- restart the current server to resolve the issue -test_run:cmd("restart server default with cleanup=True") - | - --- let's check the issue resolved -s2 = box.schema.space.create('tweedledum', {engine = 'vinyl'}) - | --- - | ... -i2 = s2:create_index('primary', { type = 'tree', parts = {1, 'unsigned'}}) - | --- - | ... -box.snapshot() - | --- - | - ok - | ... -s2:drop() - | --- - | ... diff --git a/test/vinyl/gh-5141-invalid-vylog-file.skipcond b/test/vinyl/gh-5141-invalid-vylog-file.skipcond deleted file mode 100644 index 2d688cae17ed714de72fd35af4a741a991ef4193..0000000000000000000000000000000000000000 --- a/test/vinyl/gh-5141-invalid-vylog-file.skipcond +++ /dev/null @@ -1,6 +0,0 @@ -# vim: set ft=python : -import platform - -# Disabled on FreeBSD due to issue #5141 not reproduced. -if platform.system() == 'FreeBSD': - self.skip = 1 diff --git a/test/vinyl/gh-5141-invalid-vylog-file.test.lua b/test/vinyl/gh-5141-invalid-vylog-file.test.lua deleted file mode 100644 index 3df4a879e239500a2b8ee334ee0825d70fc581aa..0000000000000000000000000000000000000000 --- a/test/vinyl/gh-5141-invalid-vylog-file.test.lua +++ /dev/null @@ -1,72 +0,0 @@ -test_run = require('test_run').new() -test_run:cmd("push filter 'Invalid VYLOG file: Slice [0-9]+ deleted but not registered'" .. \ - "to 'Invalid VYLOG file: Slice <NUM> deleted but not registered'") - --- restart the current server to avoid of issues from previous runs -test_run:cmd("restart server default with cleanup=True") - --- Let's test number of upserts in one transaction that exceeds --- the limit of operations allowed in one update. --- -ups_cnt = 10000 - -s = box.schema.create_space('test', {engine = 'vinyl'}) -pk = s:create_index('pk') - -tuple = {} -for i = 1, ups_cnt do tuple[i] = i end -_ = s:insert(tuple) -box.snapshot() - -box.begin() -for k = 1, ups_cnt do s:upsert({1}, {{'+', k, 1}}) end -box.commit() --- Upserts are not able to squash, so scheduler will get stuck. --- So let's not waste much time here, just check that no crash --- takes place. --- -box.snapshot() -require('fiber').sleep(0.01) - -s:drop() - --- --- WARNING: do not split from previous subtest. --- gh-5141: vinyl: after test with number of upserts in one transaction --- that exceeded the limit of operations allowed in one update, then --- box.snapshot() call may fail with error: --- "Invalid VYLOG file: Slice <NUM> deleted but not registered". --- To avoid of the issue tarantool server restart is needed. --- - --- let's check the issue reproduced -s0 = box.schema.space.create('tweedledum', {engine = 'vinyl'}) -i0 = s0:create_index('primary', { type = 'tree', parts = {1, 'unsigned'}}) -cnt = 1 -s0:replace{cnt, 'tuple'} -test_run:wait_cond(function() \ - cnt = cnt + 1 \ - s0:replace{cnt, 'tuple ' .. cnt} \ - local ok, err = pcall(box.snapshot) \ - if ok == false then \ - require('log').info( \ - "box.snapshot() returned on loop " .. cnt .. " 'err': " .. err) \ - end \ - return err:match "Invalid VYLOG file: Slice %d+ deleted but not registered" \ -end, 10) -s0:drop() - --- let's check the issue is stable -s1 = box.schema.space.create('tweedledum', {engine = 'vinyl'}) -i1 = s1:create_index('primary', { type = 'tree', parts = {1, 'unsigned'}}) -box.snapshot() -s1:drop() - --- restart the current server to resolve the issue -test_run:cmd("restart server default with cleanup=True") - --- let's check the issue resolved -s2 = box.schema.space.create('tweedledum', {engine = 'vinyl'}) -i2 = s2:create_index('primary', { type = 'tree', parts = {1, 'unsigned'}}) -box.snapshot() -s2:drop() diff --git a/test/vinyl/gh-5436-vylog-gc-during-compaction.result b/test/vinyl/gh-5436-vylog-gc-during-compaction.result new file mode 100644 index 0000000000000000000000000000000000000000..c0c4953cc96710b3276d9a4ebd1c62409ae97aac --- /dev/null +++ b/test/vinyl/gh-5436-vylog-gc-during-compaction.result @@ -0,0 +1,84 @@ +-- test-run result file version 2 +test_run = require('test_run').new() + | --- + | ... + +errinj = box.error.injection + | --- + | ... + +-- Make each checkpoint trigger garbage collection. +default_checkpoint_count = box.cfg.checkpoint_count + | --- + | ... +box.cfg{checkpoint_count = 1} + | --- + | ... + +-- Temporarily block compaction execution. +errinj.set('ERRINJ_VY_COMPACTION_DELAY', true) + | --- + | - ok + | ... + +-- Trigger compaction of a space. +s = box.schema.create_space('test', {engine = 'vinyl'}) + | --- + | ... +_ = s:create_index('primary', {parts = {1, 'unsigned'}, run_count_per_level = 1}) + | --- + | ... +s:insert{1, 'some data'} + | --- + | - [1, 'some data'] + | ... +box.snapshot() + | --- + | - ok + | ... +s:replace{1, 'some other data'} + | --- + | - [1, 'some other data'] + | ... +box.snapshot() + | --- + | - ok + | ... + +-- Wait for compaction to start. +test_run:wait_cond(function() return box.stat.vinyl().scheduler.tasks_inprogress > 0 end) + | --- + | - true + | ... + +-- Drop the space and trigger garbage collection. +s:drop() + | --- + | ... +box.snapshot() + | --- + | - ok + | ... + +-- Resume compaction and wait for it to finish. +errinj.set('ERRINJ_VY_COMPACTION_DELAY', false) + | --- + | - ok + | ... +test_run:wait_cond(function() return box.stat.vinyl().scheduler.tasks_inprogress == 0 end) + | --- + | - true + | ... + +-- Bump lsn and rotate vylog - should work fine. +box.space._schema:delete('no_such_key') + | --- + | ... +box.snapshot() + | --- + | - ok + | ... + +box.cfg{checkpoint_count = default_checkpoint_count} + | --- + | ... diff --git a/test/vinyl/gh-5436-vylog-gc-during-compaction.test.lua b/test/vinyl/gh-5436-vylog-gc-during-compaction.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..9e1daf08365d5c706427981b2cc1c3e4510c3f20 --- /dev/null +++ b/test/vinyl/gh-5436-vylog-gc-during-compaction.test.lua @@ -0,0 +1,35 @@ +test_run = require('test_run').new() + +errinj = box.error.injection + +-- Make each checkpoint trigger garbage collection. +default_checkpoint_count = box.cfg.checkpoint_count +box.cfg{checkpoint_count = 1} + +-- Temporarily block compaction execution. +errinj.set('ERRINJ_VY_COMPACTION_DELAY', true) + +-- Trigger compaction of a space. +s = box.schema.create_space('test', {engine = 'vinyl'}) +_ = s:create_index('primary', {parts = {1, 'unsigned'}, run_count_per_level = 1}) +s:insert{1, 'some data'} +box.snapshot() +s:replace{1, 'some other data'} +box.snapshot() + +-- Wait for compaction to start. +test_run:wait_cond(function() return box.stat.vinyl().scheduler.tasks_inprogress > 0 end) + +-- Drop the space and trigger garbage collection. +s:drop() +box.snapshot() + +-- Resume compaction and wait for it to finish. +errinj.set('ERRINJ_VY_COMPACTION_DELAY', false) +test_run:wait_cond(function() return box.stat.vinyl().scheduler.tasks_inprogress == 0 end) + +-- Bump lsn and rotate vylog - should work fine. +box.space._schema:delete('no_such_key') +box.snapshot() + +box.cfg{checkpoint_count = default_checkpoint_count} diff --git a/test/vinyl/suite.ini b/test/vinyl/suite.ini index 1311b297019b34b747b936d4e5b55a0a0059f266..a7f09898d2b363318b74ea92ccbf8fccc27f6b92 100644 --- a/test/vinyl/suite.ini +++ b/test/vinyl/suite.ini @@ -2,7 +2,7 @@ core = tarantool description = vinyl integration tests script = vinyl.lua -release_disabled = errinj.test.lua errinj_ddl.test.lua errinj_gc.test.lua errinj_stat.test.lua errinj_tx.test.lua errinj_vylog.test.lua partial_dump.test.lua quota_timeout.test.lua recovery_quota.test.lua replica_rejoin.test.lua gh-4864-stmt-alloc-fail-compact.test.lua gh-4805-open-run-err-recovery.test.lua gh-4821-ddl-during-throttled-dump.test.lua gh-3395-read-prepared-uncommitted.test.lua gh-5823-skip-newer-than-snap-vylog.test.lua +release_disabled = errinj.test.lua errinj_ddl.test.lua errinj_gc.test.lua errinj_stat.test.lua errinj_tx.test.lua errinj_vylog.test.lua partial_dump.test.lua quota_timeout.test.lua recovery_quota.test.lua replica_rejoin.test.lua gh-4864-stmt-alloc-fail-compact.test.lua gh-4805-open-run-err-recovery.test.lua gh-4821-ddl-during-throttled-dump.test.lua gh-3395-read-prepared-uncommitted.test.lua gh-5823-skip-newer-than-snap-vylog.test.lua gh-5436-vylog-gc-during-compaction.test.lua config = suite.cfg lua_libs = suite.lua stress.lua large.lua ../box/lua/txn_proxy.lua ../box/lua/utils.lua use_unix_sockets = True