Skip to content
Snippets Groups Projects
Commit ae6a02eb authored by Vladimir Davydov's avatar Vladimir Davydov Committed by Vladimir Davydov
Browse files

vinyl: do not log dump if index was dropped

An index can be dropped while a memory dump is in progress. If the vinyl
garbage collector happens to delete the index from the vylog by the time
the memory dump completes, the dump will log an entry for a deleted
index, resulting in an error next time we try to recover the vylog,
like:

```
ER_INVALID_VYLOG_FILE: Invalid VYLOG file: Run 2 committed after deletion
```

or

```
ER_INVALID_VYLOG_FILE: Invalid VYLOG file: Deleted range 9 has run slices
```

We already fixed a similar issue with compaction in commit 29e2931c
("vinyl: fix race between compaction and gc of dropped LSM"). Let's fix
this one in exactly the same way: discard the new run without logging it
to the vylog on a memory dump completion if the index was dropped while
the dump was in progress.

Closes #10277

NO_DOC=bug fix
parent 019bacbe
No related branches found
No related tags found
No related merge requests found
## bugfix/vinyl
* Fixed a bug when recovery could fail with the error "Invalid VYLOG file:
Deleted range XXXX has run slices" or "Invalid VYLOG file: Run XXXX committed
after deletion" after an index drop (gh-10277).
......@@ -1187,6 +1187,17 @@ vy_task_dump_complete(struct vy_task *task)
assert(lsm->is_dumping);
/*
* 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 delete_mems;
}
if (vy_run_is_empty(new_run)) {
/*
* In case the run is empty, we can discard the run
......
local server = require('luatest.server')
local t = require('luatest')
local g = t.group()
g.before_all(function(cg)
t.tarantool.skip_if_not_debug()
cg.server = server:new{
box_cfg = {
checkpoint_count = 1,
},
}
cg.server:start()
end)
g.after_all(function(cg)
cg.server:drop()
end)
g.after_each(function(cg)
cg.server:exec(function()
box.backup.stop()
if box.space.test ~= nil then
box.space.test:drop()
end
box.error.injection.set('ERRINJ_VY_DUMP_DELAY', false)
end)
end)
g.test_dump_after_gc = function(cg)
cg.server:exec(function()
local fiber = require('fiber')
-- Pause garbage collection.
box.backup.start()
-- Create a space.
local s = box.schema.space.create('test', {engine = 'vinyl'})
s:create_index('pk')
-- Create a checkpoint.
s:insert({1})
box.snapshot()
-- Start checkpointing in background.
box.error.injection.set('ERRINJ_VY_DUMP_DELAY', true)
s:insert({2})
local f = fiber.new(box.snapshot)
f:set_joinable(true)
fiber.sleep(0.1)
t.assert_equals(box.info.gc().checkpoint_is_in_progress, true)
-- Drop the space.
s:drop()
-- Resume garbage collection and wait for it to complete.
box.backup.stop()
t.helpers.retrying({}, function()
t.assert_equals(#box.info.gc().checkpoints, 1)
end)
-- Complete checkpointing.
box.error.injection.set('ERRINJ_VY_DUMP_DELAY', false)
t.assert_equals({f:join()}, {true, 'ok'})
end)
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment