diff --git a/Makefile b/Makefile index 59c8ff78..74375878 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ all: @echo "Only tests are available: make test" +.PHONY: test test: rm -rf *.xlog* *.snap 51{2,3,4,5,6,7} INDEX_TYPE='TREE' SPACE_TYPE='vinyl' ./test.lua diff --git a/expirationd.lua b/expirationd.lua index 625331a7..a470b934 100644 --- a/expirationd.lua +++ b/expirationd.lua @@ -56,6 +56,7 @@ local constants = { -- Task fibers -- ------------------------------------------------------------------------- -- +-- get all fields in primary key(composite possible) from tuple local function construct_key(space_id, tuple) return fun.map( function(x) return tuple[x.fieldno] end, @@ -63,6 +64,7 @@ local function construct_key(space_id, tuple) ):totable() end +-- do expiration process on tuple local function expiration_process(task, tuple) task.checked_tuples_count = task.checked_tuples_count + 1 if task.is_tuple_expired(task.args, tuple) then @@ -71,6 +73,7 @@ local function expiration_process(task, tuple) end end +-- stop for some time local function suspend_basic(scan_space, task, len) local delay = (task.tuples_per_iteration * task.full_scan_time) delay = math.min(delay / len, task.iteration_delay) @@ -78,12 +81,14 @@ local function suspend_basic(scan_space, task, len) end local function suspend(scan_space, task) + -- Return the number of tuples in the space local space_len = scan_space:len() if space_len > 0 then suspend_basic(scan_space, task, space_len) end end +-- delete with some suspend and some tuples limit local function tree_index_iter(scan_space, task) -- iteration with GT iterator local params = {iterator = 'GT', limit = task.tuples_per_iteration} @@ -96,6 +101,7 @@ local function tree_index_iter(scan_space, task) end suspend(scan_space, task) local key = construct_key(scan_space.id, last_id) + -- select all greater then last key tuples = scan_space.index[0]:select(key, params) end @@ -186,6 +192,7 @@ local function guardian_loop(task) fiber.name(string.format("guardian of %q", task.name), { truncate = true }) while true do + -- if fiber doesn't exist if get_fiber_id(task.worker_fiber) == 0 then -- create worker fiber task.worker_fiber = fiber.create(worker_loop, task) @@ -205,7 +212,7 @@ end -- * task:start() -- start task -- * task:stop() -- stop task -- * task:restart() -- restart task --- * task:kill() -- delete task and restart +-- * task:kill() -- stop task and delete -- * task:statistics() -- return table with statistics local Task_methods = { start = function (self) @@ -502,10 +509,7 @@ local function expirationd_task_stats(name) return retval end --- kill task -local function expirationd_kill_task(name) - return get_task(name):kill() -end + -- get task by name local function expirationd_get_task(name) diff --git a/test/entrypoint/srv_basic.lua b/test/entrypoint/srv_basic.lua new file mode 100755 index 00000000..167e985e --- /dev/null +++ b/test/entrypoint/srv_basic.lua @@ -0,0 +1,43 @@ +#!/usr/bin/env tarantool + +require('strict').on() + +local log = require('log') +local errors = require('errors') +local cartridge = require('cartridge') +errors.set_deprecation_handler(function(err) + log.error('%s', err) + os.exit(1) +end) + +package.preload['mymodule'] = function() + + return { + role_name = 'myrole', + validate_config = function() + return true + end, + init = function(opts) end, + apply_config = function(_, opts) end, + stop = function() end, + } +end + +local ok, err = errors.pcall('CartridgeCfgError', cartridge.cfg, { + advertise_uri = 'localhost:3301', + http_port = 8081, + bucket_count = 3000, + roles = { + 'mymodule', + }, + roles_reload_allowed = true, + -- Compatibility tests run on cartridge 1.2.0 + -- which doesn't support it yet. + upload_prefix = package.loaded['cartridge.upload'] and '../upload', +}) +if not ok then + log.error('%s', err) + os.exit(1) +end + +_G.is_initialized = cartridge.is_healthy diff --git a/test/helper.lua b/test/helper.lua new file mode 100644 index 00000000..222970da --- /dev/null +++ b/test/helper.lua @@ -0,0 +1,30 @@ +require('strict').on() + +local fio = require('fio') +local digest = require('digest') +local helpers = table.copy(require('cartridge.test-helpers')) + +helpers.project_root = fio.dirname(debug.sourcedir()) + +fio.tempdir = function(base) + base = base or os.getenv('TMPDIR') or '/tmp' + local random = digest.urandom(9) + local suffix = digest.base64_encode(random, {urlsafe = true}) + local path = fio.pathjoin(base, 'tmp.cartridge.' .. suffix) + fio.mktree(path) + return path +end + +function helpers.entrypoint(name) + local path = fio.pathjoin( + helpers.project_root, + 'test', 'entrypoint', + string.format('%s.lua', name) + ) + if not fio.path.exists(path) then + error(path .. ': no such entrypoint', 2) + end + return path +end + +return helpers diff --git a/test/integration/hotreload_test.lua b/test/integration/hotreload_test.lua new file mode 100644 index 00000000..d77c3f30 --- /dev/null +++ b/test/integration/hotreload_test.lua @@ -0,0 +1,98 @@ +local fio = require('fio') +local utils = require('cartridge.utils') +local t = require('luatest') +local g = t.group("hot_reload") + +local helpers = require('test.helper') + +local function reload_myrole(fn) + -- For the sake of string.dump() function must have no upvalues. + -- https://www.lua.org/manual/5.1/manual.html#pdf-string.dump + utils.assert_upvalues(fn, {}) + + local ok, err = g.srv.net_box:eval([[ + package.preload["mymodule"] = loadstring(...) + return require("cartridge.roles").reload() + ]], {string.dump(fn)}) + + t.assert_equals({ok, err}, {true, nil}) +end + +g.before_all(function() + local tempdir = fio.tempdir() + g.cluster = helpers.Cluster:new({ + datadir = tempdir, + server_command = helpers.entrypoint('srv_basic'), + cookie = "secret-cluster-cookie", + replicasets = {{ + alias = 'A', + roles = {'myrole'}, + servers = 1, + }}, + }) + g.srv = g.cluster:server('A-1') + g.cluster:start() + + local ok, err = g.srv.net_box:eval([[ + local a = box.schema.create_space('origin', { + if_not_exists = true + }) + a:create_index('pri') + return true, nil + ]]) + t.assert_equals({ok, err}, {true, nil}) +end) + +g.after_all(function() + g.cluster:stop() + fio.rmtree(g.cluster.datadir) +end) + +function g.test_reload_config() + reload_myrole(function() + return { + role_name = 'myrole', + init = function() + rawset(_G, "expirationd", require('expirationd')) + box.space.origin:insert({1}) + local function is_expired(args, tuple) + return true + end + expirationd.start("clean_all", box.space.origin.id, is_expired, {force = true}) + return true, nil + end, + stop = function() + for _, task in pairs(expirationd.tasks()) do + expirationd.kill(task) + end + box.space.origin:insert({2}) + end + } + end) + t.assert_equals( + g.srv.net_box:eval('return box.space.origin:select()'), + {} + ) + reload_myrole(function() + return { + role_name = 'myrole', + init = function() + rawset(_G, "expirationd", require('expirationd')) + local function is_expired(args, tuple) + return true + end + expirationd.start("clean_all", box.space.origin.id, is_expired) + return true, nil + end, + stop = function() + for _, task in pairs(expirationd.tasks()) do + expirationd.kill(task) + end + end + } + end) + t.assert_equals( + g.srv.net_box:eval('return box.space.origin:select()'), + {{ 2 }} + ) +end