Skip to content

Commit 7b72080

Browse files
committed
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 tarantool#10109 NO_DOC=bug fix
1 parent 72763f9 commit 7b72080

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## bugfix/vinyl
2+
3+
* Fixed a bug when a tuple was not returned by range `select`. The bug could
4+
also trigger a crash in the read iterator (gh-10109).

src/box/vy_cache.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -826,10 +826,13 @@ vy_cache_iterator_restore(struct vy_cache_iterator *itr, struct vy_entry last,
826826
struct vy_cache_node *node =
827827
*vy_cache_tree_iterator_get_elem(tree, &pos);
828828
int cmp = dir * vy_entry_compare(node->entry, key, def);
829+
bool is_visible = vy_cache_iterator_stmt_is_visible(
830+
itr, node->entry.stmt);
831+
if (!is_visible)
832+
*stop = false;
829833
if (cmp < 0 || (cmp == 0 && !key_belongs))
830834
break;
831-
if (vy_cache_iterator_stmt_is_visible(
832-
itr, node->entry.stmt)) {
835+
if (is_visible) {
833836
itr->curr_pos = pos;
834837
if (itr->curr.stmt != NULL)
835838
tuple_unref(itr->curr.stmt);

test/vinyl-luatest/gh_10109_read_iterator_test.lua

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,44 @@ g.test_read_upsert = function(cg)
3737
{{400}, {300}, {200}, {100}})
3838
end)
3939
end
40+
41+
g.test_read_cache = function(cg)
42+
cg.server:exec(function()
43+
local fiber = require('fiber')
44+
local timeout = 30
45+
box.error.injection.set('ERRINJ_VY_COMPACTION_DELAY', true)
46+
local s = box.schema.create_space('test', {engine = 'vinyl'})
47+
s:create_index('pk')
48+
s:replace({500})
49+
box.snapshot()
50+
s:upsert({200}, {})
51+
box.snapshot()
52+
s:replace({100})
53+
s:replace({300})
54+
s:replace({400})
55+
box.snapshot()
56+
local c = fiber.channel()
57+
fiber.new(function()
58+
local _
59+
local result = {}
60+
local gen, param, state = s:pairs({400}, {iterator = 'lt'})
61+
_, result[#result + 1] = gen(param, state) -- {300}
62+
c:put(true)
63+
c:get()
64+
_, result[#result + 1] = gen(param, state) -- {200}
65+
_, result[#result + 1] = gen(param, state) -- {100}
66+
_, result[#result + 1] = gen(param, state) -- eof
67+
c:put(result)
68+
end)
69+
t.assert(c:get(timeout))
70+
s:replace({350, 'x'}) -- send the iterator to a read view
71+
s:replace({150, 'x'}) -- must be invisible to the iterator
72+
-- Add the interval connecting the tuple {100} visible from the read
73+
-- view with the tuple {150, 'x'} invisible from the read view to
74+
-- the cache.
75+
t.assert_equals(s:select({100}, {iterator = 'ge', limit = 2}),
76+
{{100}, {150, 'x'}})
77+
c:put(true, timeout)
78+
t.assert_equals(c:get(timeout), {{300}, {200}, {100}})
79+
end)
80+
end

0 commit comments

Comments
 (0)