@@ -45,7 +45,9 @@ local constants = {
45
45
-- factor for recalculation of vinyl space size
46
46
default_vinyl_assumed_space_len_factor = 2 ,
47
47
-- default function on full scan
48
- default_on_full_scan = function (...) end
48
+ default_on_full_scan = function (...) end ,
49
+ -- default iterating over the loop will go in ascending index
50
+ iterator = " GE"
49
51
}
50
52
51
53
-- ========================================================================= --
@@ -57,10 +59,10 @@ local constants = {
57
59
-- ------------------------------------------------------------------------- --
58
60
59
61
-- get all fields in primary key(composite possible) from tuple
60
- local function construct_key (space_id , tuple )
62
+ local function construct_key (expire_index , tuple )
61
63
return fun .map (
62
64
function (x ) return tuple [x .fieldno ] end ,
63
- box . space [ space_id ]. index [ 0 ] .parts
65
+ expire_index .parts
64
66
):totable ()
65
67
end
66
68
@@ -69,78 +71,77 @@ local function expiration_process(task, tuple)
69
71
task .checked_tuples_count = task .checked_tuples_count + 1
70
72
if task .is_tuple_expired (task .args , tuple ) then
71
73
task .expired_tuples_count = task .expired_tuples_count + 1
72
- task .process_expired_tuple (task .space_id , task .args , tuple )
74
+ task .process_expired_tuple (task .space_id , task .args , tuple , task )
73
75
end
74
76
end
75
77
76
78
-- stop for some time
77
- local function suspend_basic (scan_space , task , len )
79
+ local function suspend_basic (task , len )
78
80
local delay = (task .tuples_per_iteration * task .full_scan_time )
79
81
delay = math.min (delay / len , task .iteration_delay )
80
82
fiber .sleep (delay )
81
83
end
82
84
83
- local function suspend (scan_space , task )
85
+ local function suspend (task )
84
86
-- Return the number of tuples in the space
85
- local space_len = scan_space :len ()
87
+ local space_len = task . expire_index :len ()
86
88
if space_len > 0 then
87
- suspend_basic (scan_space , task , space_len )
89
+ suspend_basic (task , space_len )
88
90
end
89
91
end
90
92
91
93
-- delete with some suspend and some tuples limit
92
- local function tree_index_iter (scan_space , task )
94
+ local function tree_index_iter (task )
93
95
-- iteration with GT iterator
94
- local params = {iterator = ' GT ' , limit = task .tuples_per_iteration }
96
+ local params = {iterator = task . iterator , limit = task .tuples_per_iteration }
95
97
local last_id
96
- local tuples = scan_space . index [ 0 ] :select ({} , params )
98
+ local tuples = task . expire_index :select (task . start_element , params )
97
99
while # tuples > 0 do
98
100
last_id = tuples [# tuples ]
99
101
for _ , tuple in ipairs (tuples ) do
102
+ if task :stop_iteration () then goto done end
100
103
expiration_process (task , tuple )
101
104
end
102
- suspend (scan_space , task )
103
- local key = construct_key (scan_space . id , last_id )
105
+ suspend (task )
106
+ local key = construct_key (task . expire_index , last_id )
104
107
-- select all greater then last key
105
- tuples = scan_space . index [ 0 ] :select (key , params )
108
+ tuples = task . expire_index :select (key , params )
106
109
end
107
-
110
+ :: done ::
108
111
end
109
112
110
- local function hash_index_iter (scan_space , task )
113
+ local function hash_index_iter (task )
111
114
-- iteration for hash index
112
115
local checked_tuples_count = 0
113
- for _ , tuple in scan_space . index [ 0 ] :pairs (nil , {iterator = box .index .ALL }) do
116
+ for _ , tuple in task . expire_index :pairs (nil , {iterator = box .index .ALL }) do
114
117
checked_tuples_count = checked_tuples_count + 1
118
+ if task :stop_iteration () then break end
115
119
expiration_process (task , tuple )
116
120
-- find out if the worker can go to sleep
117
121
if checked_tuples_count >= task .tuples_per_iteration then
118
122
checked_tuples_count = 0
119
- suspend (scan_space , task )
123
+ suspend (task )
120
124
end
121
125
end
122
126
end
123
127
124
128
local function default_do_worker_iteration (task )
125
- local scan_space = box .space [task .space_id ]
126
- local index_type = scan_space .index [0 ].type
129
+ local index_type = task .expire_index .type
127
130
128
131
-- full index scan loop
129
132
if index_type == ' HASH' then
130
- hash_index_iter (scan_space , task )
133
+ hash_index_iter (task )
131
134
else
132
- tree_index_iter (scan_space , task )
135
+ tree_index_iter (task )
133
136
end
134
137
end
135
138
136
139
local function vinyl_do_worker_iteration (task )
137
- local scan_space = box .space [task .space_id ]
138
-
139
140
local checked_tuples_count = 0
140
141
local space_len = task .vinyl_assumed_space_len
141
142
142
- local params = {iterator = ' GT ' , limit = task .tuples_per_iteration }
143
- local tuples = scan_space . index [ 0 ] :select ({} , params )
143
+ local params = {iterator = task . iterator , limit = task .tuples_per_iteration }
144
+ local tuples = task . expire_index :select (task . start_element , params )
144
145
while true do
145
146
local tuple_cnt = # tuples
146
147
if tuple_cnt == 0 then
@@ -149,16 +150,24 @@ local function vinyl_do_worker_iteration(task)
149
150
local last_id = nil
150
151
for _ , tuple in ipairs (tuples ) do
151
152
last_id = tuple
153
+ if task :stop_iteration () then
154
+ checked_tuples_count = checked_tuples_count + tuple_cnt
155
+ if checked_tuples_count > space_len then
156
+ space_len = task .vinyl_assumed_space_len_factor * space_len
157
+ end
158
+ goto done
159
+ end
152
160
expiration_process (task , tuple )
153
161
end
154
162
checked_tuples_count = checked_tuples_count + tuple_cnt
155
163
if checked_tuples_count > space_len then
156
164
space_len = task .vinyl_assumed_space_len_factor * space_len
157
165
end
158
- local key = construct_key (scan_space . id , last_id )
159
- suspend_basic (scan_space , task , space_len )
160
- tuples = scan_space . index [ 0 ] :select (key , params )
166
+ local key = construct_key (task . expire_index , last_id )
167
+ suspend_basic (task , space_len )
168
+ tuples = task . expire_index :select (key , params )
161
169
end
170
+ :: done::
162
171
task .vinyl_assumed_space_len = checked_tuples_count
163
172
end
164
173
@@ -267,6 +276,8 @@ local function create_task(name)
267
276
is_tuple_expired = nil ,
268
277
process_expired_tuple = nil ,
269
278
args = nil ,
279
+ expire_index = nil ,
280
+ start_element = nil ,
270
281
iteration_delay = constants .max_delay ,
271
282
full_scan_delay = constants .max_delay ,
272
283
tuples_per_iteration = constants .default_tuples_per_iteration ,
@@ -276,7 +287,9 @@ local function create_task(name)
276
287
on_full_scan_error = constants .default_on_full_scan ,
277
288
on_full_scan_success = constants .default_on_full_scan ,
278
289
on_full_scan_start = constants .default_on_full_scan ,
279
- on_full_scan_complete = constants .default_on_full_scan
290
+ on_full_scan_complete = constants .default_on_full_scan ,
291
+ stop_iteration = constants .default_on_full_scan ,
292
+ iterator = constants .iterator
280
293
}, { __index = Task_methods })
281
294
return task
282
295
end
@@ -296,8 +309,9 @@ local function get_task(name)
296
309
end
297
310
298
311
-- default process_expired_tuple function
299
- local function default_tuple_drop (space_id , args , tuple )
300
- box .space [space_id ]:delete (construct_key (space_id , tuple ))
312
+ -- task as parameter was added to preserve backward compatibility
313
+ local function default_tuple_drop (space_id , args , tuple , task )
314
+ task .expire_index :delete (construct_key (task .expire_index , tuple ))
301
315
end
302
316
303
317
315
329
-- options = { -- (table with named options)
316
330
-- * process_expired_tuple -- applied to expired tuples, receives
317
331
-- (space_id, args, tuple) as arguments
332
+ -- * index -- name of index, need to have an index on this name (default primary index)
333
+ -- * ascending -- false if descending (default true)
334
+ -- * start_element -- must be the same data type as index field or fields (default nil)
335
+ -- * stop_iteration -- call function before check is tuple expired, if false the iteration will be stopped
318
336
-- * on_full_scan_start -- call function on starting full scan iteration
319
337
-- * on_full_scan_complete -- call function on complete full scan iteration
320
338
-- * on_full_scan_success -- call function on success full scan iteration
@@ -366,6 +384,88 @@ local function expirationd_run_task(name, space_id, is_tuple_expired, options)
366
384
end
367
385
task .process_expired_tuple = options .process_expired_tuple or default_tuple_drop
368
386
387
+ -- validate index field
388
+ -- local expire_index
389
+ -- if options.field then
390
+ -- if type(options.field) ~= "number" and type(options.field) ~= "string" then
391
+ -- error("field has a wrong type: " .. type(options.field))
392
+ -- end
393
+ -- local format_av = box.space[space_id]:format()
394
+ -- local format = {}
395
+ -- local have_format = false
396
+ -- -- add in format numbers of fields
397
+ -- -- it is needed to take field_no if field is string e.g. 'name'
398
+ -- for no, f in pairs(format_av) do
399
+ -- format[ f.name ] = {
400
+ -- name = f.name;
401
+ -- type = f.type;
402
+ -- no = no;
403
+ -- }
404
+ -- format[ no ] = format[ f.name ];
405
+ -- have_format = true
406
+ -- end
407
+ -- for _, idx in pairs(box.space[space_id].index) do
408
+ -- for _, part in pairs(idx.parts) do
409
+ -- format[ part.fieldno ] = format[ part.fieldno ] or { no = part.fieldno }
410
+ -- format[ part.fieldno ].type = part.type
411
+ -- end
412
+ -- end
413
+ --
414
+ -- local expire_field_no
415
+ -- if have_format and type(options.field) == "string" then
416
+ -- expire_field_no = format[ options.field ].no
417
+ -- else
418
+ -- expire_field_no = options.field
419
+ -- end
420
+ -- if type(expire_field_no) ~= 'number' then
421
+ -- error("Need correct field option")
422
+ -- end
423
+ --
424
+ -- for _, index in pairs(box.space[space_id].index) do
425
+ -- -- we use only first part of index,
426
+ -- -- because we will have problems with the starting element
427
+ -- -- perhaps we should first of all take an index consisting only of our field
428
+ -- if index.parts[1].fieldno == expire_field_no then
429
+ -- expire_index = index
430
+ -- end
431
+ -- end
432
+ -- expire_index = expire_index
433
+ -- else
434
+ -- -- take default primary index
435
+ -- expire_index = box.space[space_id].index[0]
436
+ -- end
437
+ -- task.expire_index = expire_index
438
+
439
+ -- validate index
440
+ local expire_index = box .space [space_id ].index [0 ]
441
+ if options .index then
442
+ for _ , index in pairs (box .space [space_id ].index ) do
443
+ if index .name == options .index then
444
+ expire_index = index
445
+ end
446
+ end
447
+ end
448
+ task .expire_index = expire_index
449
+
450
+ -- check ascending
451
+ if options .ascending ~= nil then
452
+ if type (options .ascending ) ~= ' boolean' then
453
+ error (" Invalid type of ascending" )
454
+ end
455
+ if not options .ascending then
456
+ task .iterator = " LE"
457
+ end
458
+ end
459
+
460
+ task .start_element = options .start_element
461
+
462
+ if options .stop_iteration ~= nil then
463
+ if type (options .stop_iteration ) ~= ' function' then
464
+ error (" invalid type of stop_iteration is not function" )
465
+ end
466
+ task .stop_iteration = options .stop_iteration
467
+ end
468
+
369
469
-- check expire and process after expiration handler's arguments
370
470
task .args = options .args
371
471
0 commit comments