diff --git a/test/vinyl-luatest/select_consistency_test.lua b/test/vinyl-luatest/select_consistency_test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..de48a254b79963cb6a4acac5c60f91815c047a5c
--- /dev/null
+++ b/test/vinyl-luatest/select_consistency_test.lua
@@ -0,0 +1,241 @@
+local server = require('test.luatest_helpers.server')
+local t = require('luatest')
+
+local g = t.group('vinyl.select_consistency', {
+    {defer_deletes = true}, {defer_deletes = false}
+})
+
+g.before_all(function(cg)
+    cg.server = server:new({
+        alias = 'master',
+        box_cfg = {
+            vinyl_memory = 2 * 1024 * 1024,
+            vinyl_cache = 2 * 1024 * 1024,
+            vinyl_run_count_per_level = 1,
+            vinyl_run_size_ratio = 4,
+            vinyl_page_size = 1024,
+            vinyl_range_size = 128 * 1024,
+            vinyl_bloom_fpr = 0.1,
+            vinyl_read_threads = 2,
+            vinyl_write_threads = 4,
+            vinyl_defer_deletes = cg.params.defer_deletes,
+        },
+    })
+    cg.server:start()
+end)
+
+g.after_all(function(cg)
+    cg.server:drop()
+end)
+
+g.before_each(function(cg)
+    cg.server:exec(function()
+        local s = box.schema.create_space('test', {engine = 'vinyl'})
+        s:create_index('i1')
+        s:create_index('i2', {
+            unique = false,
+            parts = {{2, 'unsigned'}, {3, 'unsigned'}},
+        })
+        s:create_index('i3', {
+            unique = false,
+            parts = {{2, 'unsigned'}, {4, 'unsigned'}},
+        })
+        s:create_index('i4', {
+            unique = false,
+            parts = {{'[5][*]', 'unsigned'}, {3, 'unsigned'}},
+        })
+        s:create_index('i5', {
+            unique = false,
+            parts = {{'[5][*]', 'unsigned'}, {4, 'unsigned'}},
+        })
+    end)
+end)
+
+g.after_each(function(cg)
+    cg.server:exec(function()
+        if box.space.test then
+            box.space.test:drop()
+        end
+    end)
+end)
+
+g.test_select_consistency = function(cg)
+    -- Checks that SELECT over different indexes called in the same transaction
+    -- yield the same results under heavy load.
+    local function run_test()
+        cg.server:exec(function()
+            local digest = require('digest')
+            local fiber = require('fiber')
+            local t = require('luatest')
+
+            math.randomseed(os.time())
+            box.stat.reset()
+
+            local WRITE_FIBERS = 20
+            local READ_FIBERS = 5
+            local MAX_TX_STMTS = 10
+            local MAX_KEY = 100 * 1000
+            local MAX_VAL = 1000
+            local MAX_MULTIKEY_COUNT = 10
+            local PADDING_SIZE = 100
+            local DUMP_COUNT = 10
+
+            local s = box.space.test
+
+            local function make_tuple()
+                local multikey = {}
+                for _ = 1, math.random(MAX_MULTIKEY_COUNT) do
+                    table.insert(multikey, math.random(MAX_VAL))
+                end
+                return {
+                    math.random(MAX_KEY), math.random(MAX_VAL),
+                    math.random(MAX_VAL), math.random(MAX_VAL),
+                    multikey, digest.urandom(PADDING_SIZE),
+                }
+            end
+
+            local function do_write()
+                box.atomic(function()
+                    for _ = 1, math.random(MAX_TX_STMTS) do
+                        local op = math.random(3)
+                        if op == 1 then
+                            s:replace(make_tuple())
+                        elseif op == 2 then
+                            s:delete({math.random(MAX_KEY)})
+                        else
+                            local tuple = make_tuple()
+                            s:upsert(tuple, {
+                                {'=', 2, tuple[2]},
+                                {'=', 3, tuple[3]},
+                                {'=', 4, tuple[4]},
+                                {'=', 5, tuple[5]},
+                            })
+                        end
+                    end
+                end)
+            end
+
+            local failed = {}
+            local function check_select(idx1, idx2, iterator)
+                local key = {math.random(MAX_VAL)}
+                local opts = {iterator = iterator}
+                local res1 = {}
+                local res2 = {}
+                box.atomic(function()
+                    for _, tuple in s.index[idx1]:pairs(key, opts) do
+                        table.insert(res1, tuple:totable())
+                    end
+                    for _, tuple in s.index[idx2]:pairs(key, opts) do
+                        table.insert(res2, tuple:totable())
+                    end
+                end)
+                local ok = true
+                for _, t1 in ipairs(res1) do
+                    local found = false
+                    for _, t2 in ipairs(res2) do
+                        if t1[1] == t2[1] then
+                            found = true
+                            break
+                        end
+                    end
+                    if not found then
+                        ok = false
+                        break
+                    end
+                end
+                if not ok then
+                    table.insert(failed, {
+                        iterator = iterator,
+                        idx1 = idx1,
+                        idx2 = idx2,
+                        res1 = res1,
+                        res2 = res2,
+                    })
+                    error('consistency check failed')
+                end
+            end
+
+            local function do_read()
+                check_select('i2', 'i3', 'eq')
+                check_select('i2', 'i3', 'req')
+                check_select('i4', 'i5', 'eq')
+                check_select('i4', 'i5', 'req')
+            end
+
+            local stop = false
+
+            local read_fiber_ch = fiber.channel(1)
+            local function read_fiber_f()
+                while not stop do
+                    local status, err = pcall(do_read)
+                    if not status then
+                        read_fiber_ch:put(err)
+                        return
+                    end
+                    fiber.yield()
+                end
+                read_fiber_ch:put(true)
+            end
+
+            local write_fiber_ch = fiber.channel(1)
+            local function write_fiber_f()
+                while not stop do
+                    local status, err = pcall(do_write)
+                    if not status and
+                       err.code ~= box.error.TRANSACTION_CONFLICT then
+                        write_fiber_ch:put(err)
+                        return
+                    end
+                    fiber.yield()
+                end
+                write_fiber_ch:put(true)
+            end
+
+            for _ = 1, READ_FIBERS do
+                fiber.create(read_fiber_f)
+            end
+            for _ = 1, WRITE_FIBERS do
+                fiber.create(write_fiber_f)
+            end
+            t.helpers.retrying({timeout = 60}, function()
+                t.assert_ge(box.stat.vinyl().scheduler.dump_count, DUMP_COUNT)
+            end)
+            stop = true
+            for _ = 1, READ_FIBERS do
+                t.assert_equals(read_fiber_ch:get(5), true)
+            end
+            for _ = 1, WRITE_FIBERS do
+                t.assert_equals(write_fiber_ch:get(5), true)
+            end
+            t.assert_equals(failed, {})
+        end)
+    end
+    run_test()
+    -- Restart and try again.
+    cg.server:restart()
+    run_test()
+    -- Peform major compaction and check that there is no garbage statements.
+    cg.server:exec(function()
+        local t = require('luatest')
+        local s = box.space.test
+        box.stat.reset()
+        box.snapshot()
+        s.index.i1:compact()
+        t.helpers.retrying({timeout = 60}, function()
+            t.assert_equals(box.stat.vinyl().scheduler.compaction_queue, 0)
+            t.assert_equals(box.stat.vinyl().scheduler.tasks_inprogress, 0)
+        end)
+        box.snapshot()
+        s.index.i2:compact()
+        s.index.i3:compact()
+        s.index.i4:compact()
+        s.index.i5:compact()
+        t.helpers.retrying({timeout = 60}, function()
+            t.assert_equals(box.stat.vinyl().scheduler.compaction_queue, 0)
+            t.assert_equals(box.stat.vinyl().scheduler.tasks_inprogress, 0)
+        end)
+        t.assert_equals(s.index.i1:len(), s.index.i1:count())
+        t.assert_equals(s.index.i2:len(), s.index.i3:len())
+        t.assert_equals(s.index.i4:len(), s.index.i5:len())
+    end)
+end
diff --git a/test/vinyl-luatest/suite.ini b/test/vinyl-luatest/suite.ini
index dfcc717ee380a84c8a9d29f44f8025fa27f23ee5..c8cd8e7ec430b0edbbafc86a1c830ccf51562aa4 100644
--- a/test/vinyl-luatest/suite.ini
+++ b/test/vinyl-luatest/suite.ini
@@ -2,3 +2,4 @@
 core = luatest
 description = vinyl space engine luatests
 is_parallel = True
+long_run = select_consistency_test.lua