commit 72763f947b94a99ccc8bb5e0fff2cce943b38497 from: Vladimir Davydov via: Vladimir Davydov date: Thu Jun 13 07:25:34 2024 UTC vinyl: fix run iterator skipping tuples following non-terminal statement If a run iterator is positioned at a non-terminal statement (UPSERT or UPDATE), `vy_run_iterator_next()` will iterate over older statements with the same key using `vy_run_iterator_next_lsn()` to build the key history. While doing so, it may reach the end of the run file (if the current key is the last in the run). This would stop iteration permanently, which is apparently wrong for reverse iterators (LE or LT): if this happens the run iterator won't return any keys preceding the last one in the run file. Fix this by removing `vy_run_iterator_stop()` from `vy_run_iterator_next_lsn()`. Part of #10109 NO_DOC=bug fix NO_CHANGELOG=next commit commit - 9b63ced353eaf6c1224358c7d3655ca52f7d21fb commit + 72763f947b94a99ccc8bb5e0fff2cce943b38497 blob - 255034593d069fda795534c9af3816dae47b2a2e blob + 54b78be88d18b73cc7adcc00ab1ffc1942bdb49c --- src/box/vy_run.c +++ src/box/vy_run.c @@ -1557,10 +1557,8 @@ vy_run_iterator_next_lsn(struct vy_run_iterator *itr, struct vy_run_iterator_pos next_pos; next: - if (vy_run_iterator_next_pos(itr, ITER_GE, &next_pos) != 0) { - vy_run_iterator_stop(itr); + if (vy_run_iterator_next_pos(itr, ITER_GE, &next_pos) != 0) return 0; - } struct vy_entry next; if (vy_run_iterator_read(itr, next_pos, &next) != 0) blob - /dev/null blob + 6e0296e5c037cd8089ae59ad07bdafe9ae9c034a (mode 644) --- /dev/null +++ test/vinyl-luatest/gh_10109_read_iterator_test.lua @@ -0,0 +1,39 @@ +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() + cg.server:start() +end) + +g.after_all(function(cg) + cg.server:drop() +end) + +g.after_each(function(cg) + cg.server:exec(function() + if box.space.test ~= nil then + box.space.test:drop() + end + box.error.injection.set('ERRINJ_VY_COMPACTION_DELAY', false) + end) +end) + +g.test_read_upsert = function(cg) + cg.server:exec(function() + box.error.injection.set('ERRINJ_VY_COMPACTION_DELAY', true) + local s = box.schema.create_space('test', {engine = 'vinyl'}) + s:create_index('pk') + s:insert({100}) + box.snapshot() + s:upsert({200}, {}) + s:upsert({300}, {}) + s:upsert({400}, {}) + box.snapshot() + t.assert_equals(s:select({500}, {iterator = 'lt', fullscan = true}), + {{400}, {300}, {200}, {100}}) + end) +end