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

vinyl: fix cache iterator skipping tuples in read view

The tuple cache doesn't store older tuple versions so if a reader is
in a read view, it must skip tuples that are newer than the read view,
see `vy_cache_iterator_stmt_is_visible()`. A reader must also ignore
cached intervals if any of the tuples used as a boundary is invisible
from the read view, see `vy_cache_iterator_skip_to_read_view()`.
There's a bug in `vy_cache_iterator_restore()` because of which such
an interval may be returned to the reader: when we step backwards
from the last returned tuple we consider only one of the boundaries.
As a result, if the other boundary is invisible from the read view,
the reader will assume there's nothing in the index between the
boundaries and skip reading older sources (memory, disk). Fix this by
always checking if the other boundary is visible.

Closes #10109

NO_DOC=bug fix
parent 72763f94
No related branches found
No related tags found
No related merge requests found
## bugfix/vinyl
* Fixed a bug when a tuple was not returned by range `select`. The bug could
also trigger a crash in the read iterator (gh-10109).
......@@ -826,10 +826,13 @@ vy_cache_iterator_restore(struct vy_cache_iterator *itr, struct vy_entry last,
struct vy_cache_node *node =
*vy_cache_tree_iterator_get_elem(tree, &pos);
int cmp = dir * vy_entry_compare(node->entry, key, def);
bool is_visible = vy_cache_iterator_stmt_is_visible(
itr, node->entry.stmt);
if (!is_visible)
*stop = false;
if (cmp < 0 || (cmp == 0 && !key_belongs))
break;
if (vy_cache_iterator_stmt_is_visible(
itr, node->entry.stmt)) {
if (is_visible) {
itr->curr_pos = pos;
if (itr->curr.stmt != NULL)
tuple_unref(itr->curr.stmt);
......
......@@ -37,3 +37,44 @@ g.test_read_upsert = function(cg)
{{400}, {300}, {200}, {100}})
end)
end
g.test_read_cache = function(cg)
cg.server:exec(function()
local fiber = require('fiber')
local timeout = 30
box.error.injection.set('ERRINJ_VY_COMPACTION_DELAY', true)
local s = box.schema.create_space('test', {engine = 'vinyl'})
s:create_index('pk')
s:replace({500})
box.snapshot()
s:upsert({200}, {})
box.snapshot()
s:replace({100})
s:replace({300})
s:replace({400})
box.snapshot()
local c = fiber.channel()
fiber.new(function()
local _
local result = {}
local gen, param, state = s:pairs({400}, {iterator = 'lt'})
_, result[#result + 1] = gen(param, state) -- {300}
c:put(true)
c:get()
_, result[#result + 1] = gen(param, state) -- {200}
_, result[#result + 1] = gen(param, state) -- {100}
_, result[#result + 1] = gen(param, state) -- eof
c:put(result)
end)
t.assert(c:get(timeout))
s:replace({350, 'x'}) -- send the iterator to a read view
s:replace({150, 'x'}) -- must be invisible to the iterator
-- Add the interval connecting the tuple {100} visible from the read
-- view with the tuple {150, 'x'} invisible from the read view to
-- the cache.
t.assert_equals(s:select({100}, {iterator = 'ge', limit = 2}),
{{100}, {150, 'x'}})
c:put(true, timeout)
t.assert_equals(c:get(timeout), {{300}, {200}, {100}})
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