Skip to content

Commit

Permalink
Prevent iteration by functional index for Tarantool < 2.8.4
Browse files Browse the repository at this point in the history
expirationd.start fails with an error for a functional index if
a Tarantool instance version is less than 2.8.4. It can be disabled
using option.force_broken.

Closes #101
  • Loading branch information
oleg-jukovec committed May 12, 2022
1 parent ec9b72e commit 63fb8be
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 2 deletions.
30 changes: 29 additions & 1 deletion expirationd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,12 @@ end
-- configured upstream, it's an option `box.cfg.replication_source`
-- (`box.cfg.replication` for Tarantool 1.7.6+). The option `force` let a
-- user control where to start task processing and where don't.
--
-- @boolean[opt] options.force_broken
-- By default expirationd returns an error on broken cases:
-- - Iteration by a functional index may lead to a crash for Tarantool
-- < 2.8.4, see https://github.com/tarantool/expirationd/issues/101
-- You can skip the error using the option if you know what you are doing
-- (implement your own `iterate_with` as example).
-- @number[opt] options.full_scan_delay
-- Sleep time between full scans (in seconds). It is allowed to pass an FFI
-- number: `1LL`, `1ULL` etc. Default value is 1 sec.
Expand Down Expand Up @@ -603,6 +608,7 @@ local function expirationd_run_task(name, space_id, is_tuple_expired, options)
args = '?',
atomic_iteration = '?boolean',
force = '?boolean',
force_broken = '?boolean',
full_scan_delay = '?number|cdata',
full_scan_time = '?number|cdata',
index = '?number|string',
Expand Down Expand Up @@ -650,6 +656,28 @@ local function expirationd_run_task(name, space_id, is_tuple_expired, options)
if expire_index.type ~= "TREE" and expire_index.type ~= "HASH" then
error("Not supported index type, expected TREE or HASH")
end
local engine = box.space[space_id].engine
if engine == "memtx" and expire_index.func ~= nil then
local supported = false
local sdk = rawget(_G, "_TARANTOOL")
local major_minor_patch = sdk:split('-', 1)[1]
local major_minor_patch_parts = major_minor_patch:split('.', 2)

local major = tonumber(major_minor_patch_parts[1])
local minor = tonumber(major_minor_patch_parts[2])
local patch = tonumber(major_minor_patch_parts[3])
-- https://github.com/tarantool/expirationd/issues/101
-- fixed since 2.8.4 and 2.10
if (major == 2 and minor == 8 and patch >= 4)
or (major == 2 and minor >= 10) or (major > 2) then
supported = true
end
local force_broken = options.force_broken or false
if not supported and not force_broken then
error("Functional indices are not supported for" ..
" Tarantool < 2.8.4, see options.force_broken")
end
end
end
task.index = expire_index

Expand Down
8 changes: 8 additions & 0 deletions test/helper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,12 @@ function helpers.vinyl_is_broken()
return broken_v1_10 or broken_v2
end

function helpers.memtx_func_index_is_broken()
local major, minor, patch = helpers.tarantool_version()
if major > 2 or (major == 2 and minor == 8 and patch >= 4) or (major == 2 and minor >= 10) then
return false
end
return true
end

return helpers
44 changes: 43 additions & 1 deletion test/unit/custom_index_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,50 @@ function g.test_tree_index_multikey(cg)
task:kill()
end

function g.test_memtx_tree_functional_index_broken(cg)
t.skip_if(_TARANTOOL < "2" or not helpers.memtx_func_index_is_broken(),
"Unsupported Tarantool version")
t.skip_if(cg.params.index_type ~= 'TREE', 'Unsupported index type')
t.skip_if(cg.params.engine == 'vinyl', 'Unsupported engine')
local expected = "Functional indices are not supported for Tarantool < 2.8.4," ..
" see options.force_broken"

t.assert_error_msg_contains(expected, expirationd.start, "clean_all", cg.space.id,
helpers.is_expired_debug, {index = "functional_index"})
end

function g.test_memtx_tree_functional_index_force_broken(cg)
t.skip_if(_TARANTOOL < "2" or not helpers.memtx_func_index_is_broken(),
"Unsupported Tarantool version")
t.skip_if(cg.params.index_type ~= 'TREE', 'Unsupported index type')
t.skip_if(cg.params.engine == 'vinyl', 'Unsupported engine')

helpers.iteration_result = {}

cg.space:insert({1, "1", nil, nil, nil, nil, nil, "12"})
cg.space:insert({2, "2", nil, nil, nil, nil, nil, "21"})

local select_with = function(task)
return pairs(task.index:select({}, {iterator = "ALL"}))
end

local task = expirationd.start("clean_all", cg.space.id, helpers.is_expired_debug,
{index = "functional_index", force_broken = true, iterate_with = select_with})

-- wait for tuples expired
helpers.retrying({}, function()
-- sort by second character to eighth field
t.assert_equals(helpers.iteration_result, {
{2, "2", nil, nil, nil, nil, nil, "21"},
{1, "1", nil, nil, nil, nil, nil, "12"}
})
end)
task:kill()
end

function g.test_memtx_tree_functional_index(cg)
t.skip_if(_TARANTOOL < "2", "Unsupported Tarantool version")
t.skip_if(_TARANTOOL < "2" or helpers.memtx_func_index_is_broken(),
"Unsupported Tarantool version")
t.skip_if(cg.params.index_type ~= 'TREE', 'Unsupported index type')
t.skip_if(cg.params.engine == 'vinyl', 'Unsupported engine')

Expand Down

0 comments on commit 63fb8be

Please sign in to comment.