Skip to content

Commit 05faba5

Browse files
a1div0vakhov
authored andcommitted
fix: removed restrictions on role inclusion order
(cherry picked from commit dcbef05550f478761bdf6eb1227fdece8e814a4e)
1 parent d7581eb commit 05faba5

File tree

14 files changed

+559
-98
lines changed

14 files changed

+559
-98
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1313

1414
### Fixed
1515

16-
- Fix `is_master_only` option. Now it runs on master correctly.
16+
- Fix `is_master_only` option. Now it runs on master correctly.
17+
- Fixed restrictions on the order of role enable.
1718

1819
## 1.6.0 - 2024-03-25
1920

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ expirationd.start(job_name, space.id, is_expired, {
131131
## Testing
132132

133133
```
134+
$ tt install tarantool-ee 1.10.15-0-r648
134135
$ make deps-full
135136
$ make test
136137
```

cartridge/roles/expirationd.lua

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,35 @@ local expirationd = require("expirationd")
22
local role_name = "expirationd"
33
local started = require("cartridge.vars").new(role_name)
44

5-
local function dumb_fn() end
5+
local issue_map = {}
66

7-
local function load_function(func_name)
8-
if func_name == nil or type(func_name) ~= 'string' then
9-
return nil
10-
end
11-
12-
local func = rawget(_G, func_name)
13-
if func == nil then
14-
if type(box.cfg) == 'function' then
15-
-- After restart `validate_config` sometimes is run before box.cfg
16-
-- call and it leads to error like `Please call box.cfg first` and
17-
-- at this moment we are not able to check real availability of
18-
-- functions in box.func. If we return nil, we fail configration
19-
-- but it is not an error actually, because we cannot do any
20-
-- checks. Thus we decided to return dumb_fn to avoid
21-
-- misconfiguration at this stage and in hope that `apply_config`
22-
-- will do real check.
23-
-- P.S. Of course it is a bad solution, but...
24-
return dumb_fn
25-
end
26-
if not box.schema.func.exists(func_name) then
27-
return nil
28-
end
7+
function _G.expirationd_enable_issue(task_name, message, ...)
8+
issue_map[task_name] = {
9+
level = 'warning',
10+
topic = 'expirationd',
11+
message = ('EXPIRATIOND, task name "%s": ' .. message):format(task_name, ...),
12+
}
13+
end
2914

30-
return function(...)
31-
return box.func[func_name]:call({...})
32-
end
33-
end
15+
function _G.expirationd_disable_issue(task_name)
16+
issue_map[task_name] = nil
17+
end
3418

35-
if type(func) ~= 'function' then
36-
return nil
19+
local function get_issues()
20+
local res = {}
21+
for _, issue in pairs(issue_map) do
22+
table.insert(res, issue)
3723
end
3824

39-
return func
25+
return res
4026
end
4127

4228
local function get_param(param_name, value, types)
4329
local types_map = {
4430
b = {type = "boolean", err = "a boolean"},
4531
n = {type = "number", err = "a number"},
4632
s = {type = "string", err = "a string"},
47-
f = {type = "string", transform = load_function, err = "a function name in _G or in box.func"},
33+
f = {type = "string", err = "a string"},
4834
t = {type = "table", err = "a table"},
4935
any = {err = "any type"},
5036
}
@@ -273,4 +259,5 @@ return setmetatable({
273259
validate_config = validate_config,
274260
apply_config = apply_config,
275261
stop = stop,
262+
get_issues = get_issues,
276263
}, { __index = expirationd })

expirationd/init.lua

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,27 @@ local function get_fiber_id(fiber)
2222
return fid
2323
end
2424

25+
-- This method adds an issue to the cluster instance that is displayed in the admin panel
26+
-- and metrics. The method calls a global function that must be declared in the role module.
27+
-- If the function is not declared, then instead of an issue, a warning is written to the log.
28+
local function add_issue(task_name, message, ...)
29+
if rawget(_G, 'expirationd_enable_issue') ~= nil then
30+
_G.expirationd_enable_issue(task_name, message, ...)
31+
else
32+
local message_tpl = 'Expirationd warning: task "%s", ' .. message
33+
log.warn(message_tpl:format(task_name, ...))
34+
end
35+
end
36+
37+
-- This method removes a previously added issue from the cluster instance, which is displayed in
38+
-- the admin panel and metrics. The method calls a global function that must be declared in the
39+
-- role module. If the function is not declared, then nothing is called.
40+
local function remove_issue(task_name)
41+
if rawget(_G, 'expirationd_disable_issue') ~= nil then
42+
_G.expirationd_disable_issue(task_name)
43+
end
44+
end
45+
2546
local stash_names = {
2647
cfg = '__expirationd_cfg',
2748
metrics_stats = '__expirationd_metrics_stats',
@@ -186,6 +207,67 @@ local function check_space_and_index_exist(task)
186207
return true
187208
end
188209

210+
local function load_function(func_name)
211+
if func_name == nil or type(func_name) ~= 'string' then
212+
return nil
213+
end
214+
215+
local func = rawget(_G, func_name)
216+
if func ~= nil then
217+
if type(func) ~= 'function' then
218+
return nil
219+
end
220+
221+
return func
222+
elseif box.schema.func.exists(func_name) then
223+
return function(...)
224+
return box.func[func_name]:call({...})
225+
end
226+
else
227+
return nil
228+
end
229+
end
230+
231+
local function load_functions(task)
232+
local func_list = {
233+
'is_tuple_expired',
234+
'iterate_with',
235+
'on_full_scan_complete',
236+
'on_full_scan_error',
237+
'on_full_scan_start',
238+
'on_full_scan_success',
239+
'process_expired_tuple',
240+
'process_while',
241+
}
242+
243+
local result = {}
244+
for _, func_name in ipairs(func_list) do
245+
if task[func_name] and type(task[func_name]) == 'string' then
246+
result[func_name] = load_function(task[func_name])
247+
if result[func_name] == nil then
248+
add_issue(
249+
task.name,
250+
'Function "%s" (for option "%s") -- not loaded',
251+
task[func_name],
252+
func_name
253+
)
254+
return false
255+
end
256+
end
257+
end
258+
259+
for func_name, func_ptr in pairs(result) do
260+
task[func_name] = func_ptr
261+
end
262+
263+
if task.is_tuple_expired == nil then
264+
add_issue(task.name, 'Function "is_tuple_expired" must be declare')
265+
return false
266+
end
267+
268+
return true
269+
end
270+
189271
local function check_space_and_index(task)
190272
local ok, err = check_space_and_index_exist(task)
191273
if not ok then
@@ -374,11 +456,14 @@ local function worker_loop(task)
374456
fiber.self():cancel()
375457
end
376458
-- Full scan iteration is complete, yield
459+
remove_issue(task.name)
377460
fiber.sleep(task.full_scan_delay)
378461
end
379462
end
380463

381464
local function guardian_loop(task)
465+
add_issue(task.name, 'Task is not running')
466+
382467
-- detach the guardian from the creator and attach it to sched
383468
fiber.name(string.format("guardian of %q", task.name), { truncate = true })
384469

@@ -389,6 +474,13 @@ local function guardian_loop(task)
389474
fiber.sleep(constants.check_interval)
390475
end
391476

477+
while true do
478+
if load_functions(task) then
479+
break
480+
end
481+
fiber.sleep(constants.check_interval)
482+
end
483+
392484
while true do
393485
-- if fiber doesn't exist
394486
if get_fiber_id(task.worker_fiber) == 0 then
@@ -447,6 +539,8 @@ local Task_methods = {
447539
--
448540
-- @function task.stop
449541
stop = function (self)
542+
remove_issue(self.name)
543+
450544
if (get_fiber_id(self.guardian_fiber) ~= 0) then
451545
self.guardian_fiber:cancel()
452546
while self.guardian_fiber:status() ~= "dead" do
@@ -991,23 +1085,23 @@ end
9911085
--
9921086
-- @function expirationd.start
9931087
local function expirationd_run_task(name, space, is_tuple_expired, options)
994-
checks('string', 'number|string', 'function', {
1088+
checks('string', 'number|string', 'string|function', {
9951089
args = '?',
9961090
atomic_iteration = '?boolean',
9971091
force = '?boolean',
9981092
force_allow_functional_index = '?boolean',
9991093
full_scan_delay = '?number|cdata',
10001094
full_scan_time = '?number|cdata',
10011095
index = '?number|string',
1002-
iterate_with = '?function',
1096+
iterate_with = '?string|function',
10031097
iteration_delay = '?number|cdata',
10041098
iterator_type = '?number|string',
1005-
on_full_scan_complete = '?function',
1006-
on_full_scan_error = '?function',
1007-
on_full_scan_start = '?function',
1008-
on_full_scan_success = '?function',
1009-
process_expired_tuple = '?function',
1010-
process_while = '?function',
1099+
on_full_scan_complete = '?string|function',
1100+
on_full_scan_error = '?string|function',
1101+
on_full_scan_start = '?string|function',
1102+
on_full_scan_success = '?string|function',
1103+
process_expired_tuple = '?string|function',
1104+
process_while = '?string|function',
10111105
start_key = '?',
10121106
tuples_per_iteration = '?number|cdata',
10131107
vinyl_assumed_space_len_factor = '?number|cdata',

roles/expirationd.lua

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,32 @@
11
local expirationd = require("expirationd")
2+
local config = require('config')
23
local fiber = require("fiber")
34
local log = require("log")
45

56
local role_name = "roles.expirationd"
67
local started = {}
78

9+
function _G.expirationd_enable_issue(task_name, message, ...)
10+
config._aboard:set(
11+
{
12+
type = 'warn',
13+
message = ('EXPIRATIOND, task name "%s": ' .. message):format(task_name, ...),
14+
},
15+
{
16+
key = task_name,
17+
}
18+
)
19+
end
820

9-
local function load_function(func_name)
10-
if func_name == nil or type(func_name) ~= 'string' then
11-
return nil
12-
end
13-
14-
local func = rawget(_G, func_name)
15-
if func ~= nil then
16-
if type(func) ~= 'function' then
17-
return nil
18-
end
19-
20-
return func
21-
elseif box.schema.func.exists(func_name) then
22-
return function(...)
23-
return box.func[func_name]:call({...})
24-
end
25-
else
26-
return nil
27-
end
21+
function _G.expirationd_disable_issue(task_name)
22+
config._aboard:drop(task_name)
2823
end
2924

3025
local types_map = {
3126
b = {type = "boolean", err = "a boolean"},
3227
n = {type = "number", err = "a number"},
3328
s = {type = "string", err = "a string"},
34-
f = {type = "string", transform = load_function, err = "a function name in _G or in box.func"},
29+
f = {type = "string", err = "a string"},
3530
t = {type = "table", err = "a table"},
3631
any = {err = "any type"},
3732
}

test/entrypoint/empty.lua

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env tarantool
2+
3+
require('strict').on()
4+
_G.is_initialized = function() return false end
5+
6+
local log = require('log')
7+
local errors = require('errors')
8+
local cartridge = require('cartridge')
9+
10+
local ok, err = errors.pcall('CartridgeCfgError', cartridge.cfg, {
11+
advertise_uri = 'localhost:3301',
12+
http_port = 8081,
13+
bucket_count = 3000,
14+
roles = {
15+
'cartridge.roles.vshard-router',
16+
'cartridge.roles.vshard-storage',
17+
'cartridge.roles.expirationd'
18+
},
19+
roles_reload_allowed = true,
20+
})
21+
22+
if not ok then
23+
log.error('%s', err)
24+
os.exit(1)
25+
end
26+
27+
_G.is_initialized = cartridge.is_healthy

test/helper.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,21 @@ function helpers.create_persistent_function(name, body)
322322
})
323323
end
324324

325+
function helpers.create_persistent_function_on_server(server, name, body)
326+
if _TARANTOOL >= "2" then
327+
server:exec(function(name_on_server, body_on_server)
328+
box.schema.func.create(name_on_server, {
329+
language = 'LUA',
330+
if_not_exists = true,
331+
body = body_on_server
332+
})
333+
end, {name, body})
334+
else
335+
local expr = ([[rawset(_G, '%s', %s)]]):format(name, body)
336+
server:eval(expr)
337+
end
338+
end
339+
325340
local root = fio.dirname(fio.dirname(fio.abspath(package.search('test.helper'))))
326341

327342
helpers.lua_path = root .. '/?.lua;' ..

0 commit comments

Comments
 (0)