@@ -10,6 +10,8 @@ local checks = require("checks")
10
10
local fun = require (" fun" )
11
11
local log = require (" log" )
12
12
local fiber = require (" fiber" )
13
+ local is_metrics_package , metrics = pcall (require , " metrics" )
14
+ local is_hotreload_package , hotreload = pcall (require , " cartridge.hotreload" )
13
15
14
16
-- get fiber id function
15
17
local function get_fiber_id (fiber )
@@ -20,7 +22,30 @@ local function get_fiber_id(fiber)
20
22
return fid
21
23
end
22
24
25
+ local stash_names = {
26
+ cfg = ' __expirationd_cfg' ,
27
+ metrics_stats = ' __expirationd_metrics_stats' ,
28
+ }
29
+
30
+ if is_hotreload_package then
31
+ for _ , name in pairs (stash_names ) do
32
+ hotreload .whitelist_globals ({ name })
33
+ end
34
+ end
35
+
36
+ -- get a stash instance, initialize if needed
37
+ local function stash_get (name )
38
+ local instance = rawget (_G , name ) or {}
39
+ rawset (_G , name , instance )
40
+ return instance
41
+ end
42
+
23
43
local task_list = {}
44
+ local cfg = stash_get (stash_names .cfg )
45
+ if cfg .metrics == nil then
46
+ cfg .metrics = true
47
+ end
48
+ local metrics_stats = stash_get (stash_names .metrics_stats )
24
49
25
50
local constants = {
26
51
-- default value of number of tuples that will be checked by one iteration
@@ -49,6 +74,92 @@ local constants = {
49
74
atomic_iteration = false ,
50
75
}
51
76
77
+ local function is_metrics_v_0_11_installed ()
78
+ if not is_metrics_package or metrics .unregister_callback == nil then
79
+ return false
80
+ end
81
+ local counter = require (' metrics.collectors.counter' )
82
+ return counter .remove and true or false
83
+ end
84
+
85
+ local function metrics_enable ()
86
+ if not is_metrics_v_0_11_installed () then
87
+ error (" metrics >= 0.11.0 is required" , 3 )
88
+ end
89
+
90
+ -- Workaround for a cartridge role reload:
91
+ --
92
+ -- Metrics package does not lose observation after a cartridge reload since
93
+ -- 0.13.0. expirationd does not yet support the cartridge reload (it
94
+ -- requires saving and restarting tasks at least). So, we are acting here
95
+ -- as if all expirationd tasks have been killed: reset all collectors and
96
+ -- the callback.
97
+ if metrics_stats .callback then
98
+ metrics .unregister_callback (metrics_stats .callback )
99
+ end
100
+ metrics_stats .callback = nil
101
+ if metrics_stats .collectors then
102
+ for _ , c in pairs (metrics_stats .collectors ) do
103
+ metrics .registry :unregister (c .collector )
104
+ end
105
+ end
106
+ metrics_stats .collectors = nil
107
+
108
+ local create_collector = function (name , description )
109
+ return {
110
+ collector = metrics .counter (name , description ),
111
+ task_value = {},
112
+ }
113
+ end
114
+
115
+ metrics_stats .collectors = {
116
+ [" checked_count" ] = create_collector (
117
+ " expirationd_checked_count" ,
118
+ " expirationd task's a number of checked tuples"
119
+ ),
120
+ [" expired_count" ] = create_collector (
121
+ " expirationd_expired_count" ,
122
+ " expirationd task's a number of expired tuples"
123
+ ),
124
+ [" restarts" ] = create_collector (
125
+ " expirationd_restarts" ,
126
+ " expirationd task's a number of restarts"
127
+ ),
128
+ [" working_time" ] = create_collector (
129
+ " expirationd_working_time" ,
130
+ " expirationd task's operation time"
131
+ ),
132
+ }
133
+
134
+ local callback = function ()
135
+ for task_name , task in pairs (task_list ) do
136
+ local stats = task :statistics ()
137
+ for k , v in pairs (stats ) do
138
+ local prev_v = metrics_stats .collectors [k ].task_value [task_name ] or 0
139
+ local v_inc = v - prev_v
140
+ metrics_stats .collectors [k ].collector :inc (v_inc , {name = task_name })
141
+ metrics_stats .collectors [k ].task_value [task_name ] = v
142
+ end
143
+ end
144
+ end
145
+ metrics .register_callback (callback )
146
+ metrics_stats .callback = callback
147
+ end
148
+
149
+ local function metrics_disable ()
150
+ for _ , c in pairs (metrics_stats .collectors ) do
151
+ metrics .registry :unregister (c .collector )
152
+ end
153
+ metrics_stats .collectors = nil
154
+ metrics .unregister_callback (metrics_stats .callback )
155
+ metrics_stats .callback = nil
156
+ end
157
+
158
+ if cfg .metrics then
159
+ local enabled , _ = pcall (metrics_enable )
160
+ cfg .metrics = enabled
161
+ end
162
+
52
163
-- ========================================================================= --
53
164
-- Task local functions
54
165
-- ========================================================================= --
@@ -281,6 +392,12 @@ local Task_methods = {
281
392
-- @function task.kill
282
393
kill = function (self )
283
394
self :stop ()
395
+ if metrics_stats .collectors then
396
+ for _ , c in pairs (metrics_stats .collectors ) do
397
+ c .collector :remove ({name = self .name })
398
+ c .task_value [self .name ] = nil
399
+ end
400
+ end
284
401
task_list [self .name ] = nil
285
402
end ,
286
403
400
517
--
401
518
-- @section Functions
402
519
520
+ --- Configure expirationd.
521
+ --
522
+ -- Since version 1.2.0.
523
+ --
524
+ -- How to set up a configuration option:
525
+ --
526
+ -- ```
527
+ -- expirationd.cfg({metrics = true})
528
+ -- ```
529
+ --
530
+ -- How to get an option value:
531
+ --
532
+ -- ```
533
+ -- print(expirationd.cfg.metrics)
534
+ -- true
535
+ -- ```
536
+ --
537
+ -- @table options
538
+ --
539
+ -- @bool[opt] options.metrics
540
+ -- Enable or disable stats collection by [metrics][1]. metrics >= 0.11.0
541
+ -- is required. It is enabled by default.
542
+ --
543
+ -- If enabled it creates four counter collectors, see @{task.statistics}:
544
+ --
545
+ -- 1. `expirationd_checked_count`
546
+ --
547
+ -- 2. `expirationd_expired_count`
548
+ --
549
+ -- 3. `expirationd_restarts`
550
+ --
551
+ -- 4. `expirationd_working_time`
552
+ --
553
+ -- Labeled with `name = task_name`.
554
+ --
555
+ -- [1]: https://github.com/tarantool/metrics/
556
+ --
557
+ -- @return None
558
+ --
559
+ -- @function expirationd.cfg
560
+ local function expirationd_cfg (self , options )
561
+ checks (' table' , {
562
+ metrics = ' ?boolean' ,
563
+ })
564
+
565
+ if options .metrics == nil then
566
+ return
567
+ end
568
+
569
+ if cfg .metrics ~= options .metrics then
570
+ if options .metrics == true then
571
+ metrics_enable ()
572
+ else
573
+ metrics_disable ()
574
+ end
575
+ rawset (cfg , ' metrics' , options .metrics )
576
+ end
577
+ end
578
+
403
579
--- Run a scheduled task to check and process (expire) tuples in a given space.
404
580
--
405
581
-- How expirationd works in general:
@@ -949,6 +1125,12 @@ local function show_task_list_obsolete(...)
949
1125
end
950
1126
951
1127
return {
1128
+ cfg = setmetatable ({}, {
1129
+ __index = cfg ,
1130
+ __newindex = function () error (" Use expirationd.cfg{} instead" , 2 ) end ,
1131
+ __call = expirationd_cfg ,
1132
+ __serialize = function () return cfg end ,
1133
+ }),
952
1134
start = expirationd_run_task ,
953
1135
stats = expirationd_task_stats ,
954
1136
update = expirationd_update ,
0 commit comments