Skip to content

Commit 16b8042

Browse files
committed
api: add metrics support
The patch adds the ability to export statistics to metrics >= 0.10.0. expirationd does not require the metrics package itself and tries to use an installed one. It also adds a new API method expirationd.cfg({options}). Part of #100
1 parent 7013e8a commit 16b8042

File tree

4 files changed

+491
-0
lines changed

4 files changed

+491
-0
lines changed

expirationd.lua

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ local checks = require("checks")
1010
local fun = require("fun")
1111
local log = require("log")
1212
local fiber = require("fiber")
13+
local is_metrics_package, metrics = pcall(require, "metrics")
1314

1415
-- get fiber id function
1516
local function get_fiber_id(fiber)
@@ -21,6 +22,7 @@ local function get_fiber_id(fiber)
2122
end
2223

2324
local task_list = {}
25+
local cfg = {metrics = false}
2426

2527
local constants = {
2628
-- default value of number of tuples that will be checked by one iteration
@@ -49,6 +51,74 @@ local constants = {
4951
atomic_iteration = false,
5052
}
5153

54+
local metrics_callback = nil
55+
local metrics_collectors = nil
56+
57+
local function metrics_enable()
58+
if metrics_callback then
59+
return
60+
end
61+
62+
local supported_v0_11 = false
63+
if is_metrics_package and metrics.unregister_callback then
64+
local counter = require('metrics.collectors.counter')
65+
supported_v0_11 = counter.remove and true or false
66+
end
67+
if not supported_v0_11 then
68+
error("metrics >= 0.11.0 is required", 3)
69+
end
70+
71+
local create_collector = function(name, description)
72+
return {
73+
collector = metrics.counter(name, description),
74+
task_value = {},
75+
}
76+
end
77+
78+
metrics_collectors = {
79+
["checked_count"] = create_collector(
80+
"expirationd_checked_count",
81+
"expirationd task's a number of checked tuples"
82+
),
83+
["expired_count"] = create_collector(
84+
"expirationd_expired_count",
85+
"expirationd task's a number of expired tuples"
86+
),
87+
["restarts"] = create_collector(
88+
"expirationd_restarts",
89+
"expirationd task's a number of restarts"
90+
),
91+
["working_time"] = create_collector(
92+
"expirationd_working_time",
93+
"expirationd task's operation time"
94+
),
95+
}
96+
97+
metrics_callback = function()
98+
for task_name, task in pairs(task_list) do
99+
local stats = task:statistics()
100+
for k, v in pairs(stats) do
101+
local prev_v = metrics_collectors[k].task_value[task_name] or 0
102+
local v_inc = v - prev_v
103+
metrics_collectors[k].collector:inc(v_inc, {name = task_name})
104+
metrics_collectors[k].task_value[task_name] = v
105+
end
106+
end
107+
end
108+
metrics.register_callback(metrics_callback)
109+
end
110+
111+
local function metrics_disable()
112+
if metrics_callback then
113+
for _, c in pairs(metrics_collectors) do
114+
metrics.registry:unregister(c.collector)
115+
end
116+
metrics.unregister_callback(metrics_callback)
117+
metrics_callback = nil
118+
metrics_collectors = nil
119+
end
120+
end
121+
52122
-- ========================================================================= --
53123
-- Task local functions
54124
-- ========================================================================= --
@@ -281,6 +351,12 @@ local Task_methods = {
281351
-- @function task.kill
282352
kill = function (self)
283353
self:stop()
354+
if metrics_collectors then
355+
for _, c in pairs(metrics_collectors) do
356+
c.collector:remove({name = self.name})
357+
c.task_value[self.name] = nil
358+
end
359+
end
284360
task_list[self.name] = nil
285361
end,
286362

@@ -400,6 +476,63 @@ end
400476
--
401477
-- @section Functions
402478

479+
--- Configure expirationd.
480+
--
481+
-- How to set up a configuration option:
482+
--
483+
-- ```
484+
-- expirationd.cfg({metrics = true})
485+
-- ```
486+
--
487+
-- How to get an option value:
488+
--
489+
-- ```
490+
-- print(expirationd.cfg.metrics)
491+
-- true
492+
-- ```
493+
--
494+
-- @table options
495+
--
496+
-- @bool[opt] options.metrics
497+
-- Enable or disable stats collection by [metrics][1]. metrics >= 0.10.0
498+
-- is required. It is disabled by default.
499+
--
500+
-- If enabled it creates four counter collectors, see @{task.statistics}:
501+
--
502+
-- 1. `expirationd_checked_count`
503+
--
504+
-- 2. `expirationd_expired_count`
505+
--
506+
-- 3. `expirationd_restarts`
507+
--
508+
-- 4. `expirationd_working_time`
509+
--
510+
-- Labeled with `name = task_name`.
511+
--
512+
-- [1]: https://github.com/tarantool/metrics/
513+
--
514+
-- @return None
515+
--
516+
-- @function expirationd.cfg
517+
local function expirationd_cfg(self, options)
518+
checks('table', {
519+
metrics = '?boolean',
520+
})
521+
522+
if options.metrics == nil then
523+
return
524+
end
525+
526+
if cfg.metrics ~= options.metrics then
527+
if options.metrics == true then
528+
metrics_enable()
529+
else
530+
metrics_disable()
531+
end
532+
rawset(cfg, 'metrics', options.metrics)
533+
end
534+
end
535+
403536
--- Run a scheduled task to check and process (expire) tuples in a given space.
404537
--
405538
-- How expirationd works in general:
@@ -949,6 +1082,12 @@ local function show_task_list_obsolete(...)
9491082
end
9501083

9511084
return {
1085+
cfg = setmetatable({}, {
1086+
__index = cfg,
1087+
__newindex = function() error("Use expirationd.cfg{} instead", 2) end,
1088+
__call = expirationd_cfg,
1089+
__serialize = function() return cfg end,
1090+
}),
9521091
start = expirationd_run_task,
9531092
stats = expirationd_task_stats,
9541093
update = expirationd_update,

test/helper.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,16 @@ function helpers.is_expired_true()
223223
return true
224224
end
225225

226+
function helpers.is_metrics_supported()
227+
local is_package, metrics = pcall(require, "metrics")
228+
if not is_package then
229+
return false
230+
end
231+
-- metrics >= 0.11.0 is required
232+
local counter = require('metrics.collectors.counter')
233+
return metrics.unregister_callback and counter.remove
234+
end
235+
226236
function helpers.iterate_with_func(task)
227237
return task.index:pairs(task.start_key(), { iterator = task.iterator_type })
228238
:take_while(

test/unit/cfg_test.lua

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
local expirationd = require("expirationd")
2+
local t = require("luatest")
3+
local helpers = require("test.helper")
4+
local g = t.group('expirationd_cfg')
5+
6+
function g.test_cfg_default()
7+
t.assert_equals(expirationd.cfg.metrics, false)
8+
end
9+
10+
function g.test_cfg_newindex()
11+
t.assert_error_msg_content_equals("Use expirationd.cfg{} instead",
12+
function()
13+
expirationd.cfg.any_key = false
14+
end)
15+
end
16+
17+
function g.test_cfg_metrics_set_unset()
18+
t.skip_if(not helpers.is_metrics_supported(), "metrics is not installed")
19+
20+
expirationd.cfg({metrics = true})
21+
t.assert_equals(expirationd.cfg.metrics, true)
22+
expirationd.cfg({metrics = false})
23+
t.assert_equals(expirationd.cfg.metrics, false)
24+
end
25+
26+
function g.test_cfg_metrics_multiple_set_unset()
27+
t.skip_if(not helpers.is_metrics_supported(), "metrics is not installed")
28+
29+
expirationd.cfg({metrics = true})
30+
expirationd.cfg({metrics = true})
31+
t.assert_equals(expirationd.cfg.metrics, true)
32+
expirationd.cfg({metrics = false})
33+
expirationd.cfg({metrics = false})
34+
t.assert_equals(expirationd.cfg.metrics, false)
35+
end
36+
37+
function g.test_cfg_metrics_set_unsupported()
38+
t.skip_if(helpers.is_metrics_supported(), "metrics is installed")
39+
40+
t.assert_error_msg_content_equals("metrics >= 0.11.0 is required",
41+
function()
42+
expirationd.cfg({metrics = true})
43+
end)
44+
t.assert_equals(expirationd.cfg.metrics, false)
45+
end

0 commit comments

Comments
 (0)