1
1
local log = require (' log' )
2
2
local fiber = require (' fiber' )
3
+ local uuid = require (' uuid' )
3
4
5
+ local session = require (' queue.abstract.queue_session' )
4
6
local state = require (' queue.abstract.state' )
5
7
6
8
local util = require (' queue.util' )
7
9
local qc = require (' queue.compat' )
8
10
local num_type = qc .num_type
9
11
local str_type = qc .str_type
10
12
13
+ -- The term "queue session" has been added to the queue. One "queue session"
14
+ -- can include many connections (box.session). For clarity, the box.session
15
+ -- will be referred to as connection below.
11
16
local connection = box .session
12
17
13
18
local queue = {
@@ -52,13 +57,15 @@ local function tube_release_all_tasks(tube)
52
57
log .info (prefix .. (' released %d tasks' ):format (released ))
53
58
end
54
59
55
- --- Check whether the task has been taken in a session with
56
- -- connection id == "conn_id" .
60
+ --- Check whether the task has been taken in a current session or in a session
61
+ -- with session uuid = session_uuid .
57
62
-- Throw an error if task is not take in the session.
58
- local function check_task_is_taken (tube_id , task_id , conn_id )
59
- local _taken = box .space ._queue_taken .index .task :get {tube_id , task_id }
60
- if _taken == nil or _taken [1 ] ~= conn_id then
61
- error (" Task was not taken in the session" )
63
+ local function check_task_is_taken (tube_id , task_id , session_uuid )
64
+ local _taken = box .space ._queue_taken_2 .index .task :get {tube_id , task_id }
65
+
66
+ session_uuid = session_uuid or session .identify (connection .id ())
67
+ if _taken == nil or _taken [4 ] ~= session_uuid then
68
+ error (" Task was not taken" )
62
69
end
63
70
end
64
71
@@ -127,7 +134,7 @@ function tube.touch(self, id, delta)
127
134
return
128
135
end
129
136
130
- check_task_is_taken (self .tube_id , id , connection . id () )
137
+ check_task_is_taken (self .tube_id , id )
131
138
132
139
local space_name = box .space ._queue :get {self .name }[3 ]
133
140
queue .stat [space_name ]:inc (' touch' )
@@ -136,14 +143,13 @@ function tube.touch(self, id, delta)
136
143
end
137
144
138
145
function tube .ack (self , id )
139
- local conn_id = connection .id ()
140
- check_task_is_taken (self .tube_id , id , conn_id )
146
+ check_task_is_taken (self .tube_id , id )
141
147
local tube = box .space ._queue :get {self .name }
142
148
local space_name = tube [3 ]
143
149
144
150
self :peek (id )
145
151
-- delete task
146
- box .space ._queue_taken .index .task :delete {self .tube_id , id }
152
+ box .space ._queue_taken_2 .index .task :delete {self .tube_id , id }
147
153
local result = self .raw :normalize_task (
148
154
self .raw :delete (id ):transform (2 , 1 , state .DONE )
149
155
)
@@ -153,17 +159,17 @@ function tube.ack(self, id)
153
159
return result
154
160
end
155
161
156
- local function tube_release_internal (self , id , opts , connection_id )
162
+ local function tube_release_internal (self , id , opts , session_uuid )
157
163
opts = opts or {}
158
- check_task_is_taken (self .tube_id , id , connection_id )
164
+ check_task_is_taken (self .tube_id , id , session_uuid )
159
165
160
- box .space ._queue_taken .index .task :delete {self .tube_id , id }
166
+ box .space ._queue_taken_2 .index .task :delete {self .tube_id , id }
161
167
self :peek (id )
162
168
return self .raw :normalize_task (self .raw :release (id , opts ))
163
169
end
164
170
165
171
function tube .release (self , id , opts )
166
- return tube_release_internal (self , id , opts , connection . id () )
172
+ return tube_release_internal (self , id , opts )
167
173
end
168
174
169
175
function tube .peek (self , id )
176
182
177
183
function tube .bury (self , id )
178
184
local task = self :peek (id )
179
- local conn_id = connection .id ()
180
- local is_taken , _ = pcall (check_task_is_taken , self .tube_id , id , conn_id )
185
+ local is_taken , _ = pcall (check_task_is_taken , self .tube_id , id )
181
186
if is_taken then
182
- box .space ._queue_taken .index .task :delete {self .tube_id , id }
187
+ box .space ._queue_taken_2 .index .task :delete {self .tube_id , id }
183
188
end
184
189
if task [2 ] == state .BURIED then
185
190
return task
@@ -214,8 +219,8 @@ function tube.drop(self)
214
219
error (" There are consumers connected the tube" )
215
220
end
216
221
217
- local taken = box .space ._queue_taken .index .task :min {tube_id }
218
- if taken ~= nil and taken [2 ] == tube_id then
222
+ local taken = box .space ._queue_taken_2 .index .task :min {tube_id }
223
+ if taken ~= nil and taken [1 ] == tube_id then
219
224
error (" There are taken tasks in the tube" )
220
225
end
221
226
@@ -260,10 +265,12 @@ function tube.grant(self, user, args)
260
265
261
266
tube_grant_space (user , ' _queue' , ' read' )
262
267
tube_grant_space (user , ' _queue_consumers' )
263
- tube_grant_space (user , ' _queue_taken ' )
268
+ tube_grant_space (user , ' _queue_taken_2 ' )
264
269
tube_grant_space (user , self .name )
270
+ session .grant (user )
265
271
266
272
if args .call then
273
+ tube_grant_func (user , ' queue.identify' )
267
274
local prefix = (args .prefix or ' queue.tube' ) .. (' .%s:' ):format (self .name )
268
275
tube_grant_func (user , prefix .. ' take' )
269
276
tube_grant_func (user , prefix .. ' touch' )
@@ -319,12 +326,12 @@ local function make_self(driver, space, tube_name, tube_type, tube_id, opts)
319
326
if task == nil then return end
320
327
321
328
local queue_consumers = box .space ._queue_consumers
322
- local queue_taken = box .space ._queue_taken
329
+ local queue_taken = box .space ._queue_taken_2
323
330
324
331
-- if task was taken and become other state
325
332
local taken = queue_taken .index .task :get {tube_id , task [1 ]}
326
333
if taken ~= nil then
327
- queue_taken :delete {taken [1 ], taken [2 ], taken [ 3 ] }
334
+ queue_taken :delete {taken [1 ], taken [2 ]}
328
335
end
329
336
-- task switched to ready (or new task)
330
337
if task [2 ] == state .READY then
@@ -342,8 +349,15 @@ local function make_self(driver, space, tube_name, tube_type, tube_id, opts)
342
349
end
343
350
-- task switched to taken - register in taken space
344
351
elseif task [2 ] == state .TAKEN then
345
- queue_taken :insert {connection .id (), self .tube_id , task [1 ],
346
- fiber .time64 ()}
352
+ local conn_id = connection .id ()
353
+ local session_uuid = session .identify (conn_id )
354
+ queue_taken :insert {
355
+ self .tube_id ,
356
+ task [1 ],
357
+ conn_id ,
358
+ session_uuid ,
359
+ fiber .time64 ()
360
+ }
347
361
end
348
362
if stats_data ~= nil then
349
363
queue .stat [space .name ]:inc (stats_data )
@@ -375,49 +389,45 @@ local function make_self(driver, space, tube_name, tube_type, tube_id, opts)
375
389
end
376
390
377
391
--- Release all session tasks.
378
- local function release_session_tasks (connection_id )
379
- while true do
380
- local task = box .space ._queue_taken .index .pk :min {connection_id }
381
- if task == nil or task [1 ] ~= connection_id then
382
- break
383
- end
392
+ local function release_session_tasks (session_uuid )
393
+ local taken_tasks = box .space ._queue_taken_2 .index .uuid :select {session_uuid }
384
394
385
- local tube = box .space ._queue .index .tube_id :get {task [2 ]}
395
+ for _ , task in pairs (taken_tasks ) do
396
+ local tube = box .space ._queue .index .tube_id :get {task [1 ]}
386
397
if tube == nil then
387
- log .error (" Inconsistent queue state: tube %d not found" , task [2 ])
388
- box .space ._queue_taken .index .task :delete {task [2 ], task [3 ]}
398
+ log .error (" Inconsistent queue state: tube %d not found" , task [1 ])
399
+ box .space ._queue_taken_2 .index .task :delete {task [1 ], task [2 ]}
389
400
else
390
- log .warn (" Consumer %s disconnected, release task %s(%s)" ,
391
- connection_id , task [3 ], tube [1 ])
392
-
393
- tube_release_internal (queue .tube [tube [1 ]], task [3 ], nil ,
394
- connection_id )
401
+ log .warn (" Session %s closed, release task %s(%s)" ,
402
+ uuid .frombin (session_uuid ):str (), task [2 ], tube [1 ])
403
+ tube_release_internal (queue .tube [tube [1 ]], task [2 ], nil ,
404
+ session_uuid )
395
405
end
396
406
end
397
407
end
398
408
399
409
function method ._on_consumer_disconnect ()
400
- local waiter , fb , task , tube , id
401
- id = connection . id ()
410
+ local conn_id = connection . id ()
411
+
402
412
-- wakeup all waiters
403
413
while true do
404
- waiter = box .space ._queue_consumers .index .pk :min {id }
414
+ local waiter = box .space ._queue_consumers .index .pk :min {conn_id }
405
415
if waiter == nil then
406
416
break
407
417
end
408
418
-- Don't touch the other consumers
409
- if waiter [1 ] ~= id then
419
+ if waiter [1 ] ~= conn_id then
410
420
break
411
421
end
412
- box .space ._queue_consumers :delete { waiter [1 ], waiter [2 ] }
422
+ box .space ._queue_consumers :delete {waiter [1 ], waiter [2 ]}
413
423
local cond = conds [waiter [2 ]]
414
424
if cond then
415
425
releasing_connections [waiter [2 ]] = true
416
426
cond :signal (waiter [2 ])
417
427
end
418
428
end
419
429
420
- release_session_tasks ( id )
430
+ session . disconnect ( conn_id )
421
431
end
422
432
423
433
-- function takes tuples and recreates tube
@@ -532,27 +542,34 @@ function method.start()
532
542
})
533
543
end
534
544
535
- local _taken = box .space ._queue_taken
545
+ -- Remove deprecated space
546
+ if box .space ._queue_taken ~= nil then
547
+ box .space ._queue_taken :drop ()
548
+ end
549
+
550
+ local _taken = box .space ._queue_taken_2
536
551
if _taken == nil then
537
- -- connection_id, tube_id, task_id, time
538
- _taken = box .schema .create_space (' _queue_taken ' , {
552
+ -- tube_id, task_id, connection_id, session_uuid , time
553
+ _taken = box .schema .create_space (' _queue_taken_2 ' , {
539
554
temporary = true ,
540
555
format = {
541
- {name = ' connection_id' , type = num_type ()},
542
556
{name = ' tube_id' , type = num_type ()},
543
557
{name = ' task_id' , type = num_type ()},
558
+ {name = ' connection_id' , type = num_type ()},
559
+ {name = ' session_uuid' , type = str_type ()},
544
560
{name = ' taken_time' , type = num_type ()}
545
561
}})
546
- _taken :create_index (' pk' , {
547
- type = ' tree' ,
548
- parts = {1 , num_type (), 2 , num_type (), 3 , num_type ()},
549
- unique = true })
550
562
551
- _taken :create_index (' task' ,{
563
+ _taken :create_index (' task' , {
552
564
type = ' tree' ,
553
- parts = {2 , num_type (), 3 , num_type ()},
565
+ parts = {1 , num_type (), 2 , num_type ()},
554
566
unique = true
555
567
})
568
+ _taken :create_index (' uuid' , {
569
+ type = ' tree' ,
570
+ parts = {4 , str_type ()},
571
+ unique = false
572
+ })
556
573
end
557
574
558
575
for _ , tube_tuple in _queue :pairs () do
@@ -565,6 +582,9 @@ function method.start()
565
582
end
566
583
end
567
584
585
+ session .on_session_remove (release_session_tasks )
586
+ session .start ()
587
+
568
588
connection .on_disconnect (queue ._on_consumer_disconnect )
569
589
return queue
570
590
end
@@ -628,20 +648,32 @@ local function build_stats(space)
628
648
return stats
629
649
end
630
650
651
+ --- Identifies the connection and return the UUID of the current session.
652
+ -- If session_uuid ~= nil: associate the connection with given session.
653
+ function method .identify (session_uuid )
654
+ return session .identify (connection .id (), session_uuid )
655
+ end
656
+
631
657
--- Configure of the queue module.
632
658
-- If an invalid value or an unknown option
633
659
-- is used, an error will be thrown.
634
660
local function cfg (self , opts )
635
661
opts = opts or {}
662
+ local session_opts = {}
636
663
637
664
-- Check all options before configuring so that
638
665
-- the configuration is done transactionally.
639
666
for key , val in pairs (opts ) do
640
- if key ~= ' ttr' then
667
+ if key == ' ttr' then
668
+ session_opts [key ] = val
669
+ else
641
670
error (' Unknown option ' .. tostring (key ))
642
671
end
643
672
end
644
673
674
+ -- Configuring the queue_session module.
675
+ session .cfg (session_opts )
676
+
645
677
for key , val in pairs (opts ) do
646
678
self [key ] = val
647
679
end
0 commit comments