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()
        box.error.injection.set('ERRINJ_VY_COMPACTION_DELAY', false)
        if box.space.test ~= nil then
            box.space.test:drop()
        end
    end)
end)

g.test_deferred_delete_compaction = function(cg)
    cg.server:exec(function()
        local fiber = require('fiber')

        local s = box.schema.space.create('test', {
            engine = 'vinyl',
            defer_deletes = true,
        })
        s:create_index('primary')
        s:create_index('secondary', {
            unique = false,
            parts = {2, 'unsigned'},
        })

        -- Temporarily block compaction.
        box.error.injection.set('ERRINJ_VY_COMPACTION_DELAY', true)

        -- Create primary and secondary index run files with INSERT{1,10}.
        s:insert({1, 10})
        box.snapshot()

        -- Create the primary index run file with DELETE{1}.
        --
        -- Generation of DELETE{1,10} for the secondary index is deferred
        -- until the primary index compaction.
        s:delete({1})
        box.snapshot()

        -- Trigger primary index compaction (it's still blocked though).
        s.index.primary:compact()

        -- Create primary and secondary index run files with INSERT{1,10}.
        s:upsert({1, 10}, {})
        box.snapshot()

        -- Create a read view referring to the last INSERT{1,10}.
        local f = fiber.create(function()
            box.begin()
            s:select()
            fiber.sleep(9000)
        end)
        fiber.sleep(0.1)

        -- Unblock compaction and wait for it to complete.
        --
        -- Compaction of the primary index generates DELETE{1, 10} for
        -- the older INSERT{1, 10} stored in the secondary index.
        box.error.injection.set('ERRINJ_VY_COMPACTION_DELAY', false)
        while box.stat.vinyl().scheduler.tasks_inprogress > 0 do
            fiber.sleep(0.1)
        end

        -- Write DELETE{1,10} + INSERT{1,20} to the secondary index.
        --
        -- Now, the memory level of the secondary index contains two
        -- DELETE{1,10} statements: the first one is generated by the primary
        -- index compaction for the older INSERT; the second one is generated
        -- by the UPSERT for the newer INSERT. The newer DELETE should
        -- overwrite the older one, but due to gh-10895 the older DELETE
        -- overwrites the newer one, as a result the newer INSERT was never
        -- purged.
        s:upsert({1, 20}, {{'=', 2, 20}})
        box.snapshot()

        f:cancel()

        -- Delete INSERT{1,20}, trigger primary index compaction to generate
        -- DELETE for the secondary index, then dump and compaction of the
        -- secondary index.
        s:delete({1})
        box.snapshot()
        s.index.primary:compact()
        while box.stat.vinyl().scheduler.tasks_inprogress > 0 do
            fiber.sleep(0.1)
        end

        box.snapshot()
        s.index.secondary:compact()
        while box.stat.vinyl().scheduler.tasks_inprogress > 0 do
            fiber.sleep(0.1)
        end

        -- No statements should be left in either of the indexes, but if
        -- gh-10895 wasn't fixed, the secondary index run file would still
        -- contain the newer INSERT{1,10}.
        t.assert_equals(s.index.primary:len(), 0)
        t.assert_equals(s.index.secondary:len(), 0)
    end)
end
