From 7b72080ddf926d271a4a4cad6f6ffe6e6c00e332 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov <vdavydov@tarantool.org> Date: Mon, 10 Jun 2024 13:56:31 +0300 Subject: [PATCH] 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 --- .../gh-10109-vy-read-iterator-fix.md | 4 ++ src/box/vy_cache.c | 7 +++- .../gh_10109_read_iterator_test.lua | 41 +++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/gh-10109-vy-read-iterator-fix.md diff --git a/changelogs/unreleased/gh-10109-vy-read-iterator-fix.md b/changelogs/unreleased/gh-10109-vy-read-iterator-fix.md new file mode 100644 index 0000000000..c52eea7512 --- /dev/null +++ b/changelogs/unreleased/gh-10109-vy-read-iterator-fix.md @@ -0,0 +1,4 @@ +## 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). diff --git a/src/box/vy_cache.c b/src/box/vy_cache.c index 0604d6ab96..0ebb49229f 100644 --- a/src/box/vy_cache.c +++ b/src/box/vy_cache.c @@ -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); diff --git a/test/vinyl-luatest/gh_10109_read_iterator_test.lua b/test/vinyl-luatest/gh_10109_read_iterator_test.lua index 6e0296e5c0..3a99e69361 100644 --- a/test/vinyl-luatest/gh_10109_read_iterator_test.lua +++ b/test/vinyl-luatest/gh_10109_read_iterator_test.lua @@ -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 -- GitLab