1
1
local log = require (' log' )
2
2
local fiber = require (' fiber' )
3
+ local uuid = require (' uuid' )
3
4
4
5
local state = require (' queue.abstract.state' )
5
6
@@ -75,6 +76,19 @@ local function tube_release_all_tasks(tube)
75
76
log .info (prefix .. (' released %d tasks' ):format (released ))
76
77
end
77
78
79
+ --- Get session UUID by box session ID.
80
+ local function get_quuid_by_sid (box_sid )
81
+ local session_ids = box .space ._queue_session_ids .index .box_sid :get (box_sid )
82
+ local quuid
83
+ if session_ids ~= nil then
84
+ quuid = session_ids [2 ]
85
+ else
86
+ quuid = queue .identificate ()
87
+ end
88
+
89
+ return quuid
90
+ end
91
+
78
92
-- tube methods
79
93
local tube = {}
80
94
@@ -140,7 +154,8 @@ function tube.touch(self, id, delta)
140
154
return
141
155
end
142
156
143
- local _taken = box .space ._queue_taken :get {session .id (), self .tube_id , id }
157
+ local _taken = box .space ._queue_taken :get {get_quuid_by_sid (session .id ()),
158
+ self .tube_id , id }
144
159
if _taken == nil then
145
160
error (" Task was not taken in the session" )
146
161
end
@@ -152,7 +167,8 @@ function tube.touch(self, id, delta)
152
167
end
153
168
154
169
function tube .ack (self , id )
155
- local _taken = box .space ._queue_taken :get {session .id (), self .tube_id , id }
170
+ local quuid = get_quuid_by_sid (session .id ())
171
+ local _taken = box .space ._queue_taken :get {quuid , self .tube_id , id }
156
172
if _taken == nil then
157
173
error (" Task was not taken in the session" )
158
174
end
@@ -161,7 +177,7 @@ function tube.ack(self, id)
161
177
162
178
self :peek (id )
163
179
-- delete task
164
- box .space ._queue_taken :delete {session . id () , self .tube_id , id }
180
+ box .space ._queue_taken :delete {quuid , self .tube_id , id }
165
181
local result = self .raw :normalize_task (
166
182
self .raw :delete (id ):transform (2 , 1 , state .DONE )
167
183
)
@@ -171,20 +187,20 @@ function tube.ack(self, id)
171
187
return result
172
188
end
173
189
174
- local function tube_release_internal (self , id , opts , session_id )
190
+ local function tube_release_internal (self , id , opts , queue_uuid )
175
191
opts = opts or {}
176
- local _taken = box .space ._queue_taken :get {session_id , self .tube_id , id }
192
+ local _taken = box .space ._queue_taken :get {queue_uuid , self .tube_id , id }
177
193
if _taken == nil then
178
194
error (" Task was not taken in the session" )
179
195
end
180
196
181
- box .space ._queue_taken :delete {session_id , self .tube_id , id }
197
+ box .space ._queue_taken :delete {queue_uuid , self .tube_id , id }
182
198
self :peek (id )
183
199
return self .raw :normalize_task (self .raw :release (id , opts ))
184
200
end
185
201
186
202
function tube .release (self , id , opts )
187
- return tube_release_internal (self , id , opts , session .id ())
203
+ return tube_release_internal (self , id , opts , get_quuid_by_sid ( session .id () ))
188
204
end
189
205
190
206
function tube .peek (self , id )
197
213
198
214
function tube .bury (self , id )
199
215
local task = self :peek (id )
200
- local _taken = box .space ._queue_taken :get {session .id (), self .tube_id , id }
216
+ local quuid = get_quuid_by_sid (session .id ())
217
+ local _taken = box .space ._queue_taken :get {quuid , self .tube_id , id }
201
218
if _taken ~= nil then
202
- box .space ._queue_taken :delete {session . id () , self .tube_id , id }
219
+ box .space ._queue_taken :delete {quuid , self .tube_id , id }
203
220
end
204
221
if task [2 ] == state .BURIED then
205
222
return task
@@ -281,9 +298,11 @@ function tube.grant(self, user, args)
281
298
tube_grant_space (user , ' _queue' , ' read' )
282
299
tube_grant_space (user , ' _queue_consumers' )
283
300
tube_grant_space (user , ' _queue_taken' )
301
+ tube_grant_space (user , ' _queue_session_ids' )
284
302
tube_grant_space (user , self .name )
285
303
286
304
if args .call then
305
+ tube_grant_func (user , ' queue.identificate' )
287
306
local prefix = (args .prefix or ' queue.tube' ) .. (' .%s:' ):format (self .name )
288
307
tube_grant_func (user , prefix .. ' take' )
289
308
tube_grant_func (user , prefix .. ' touch' )
@@ -362,7 +381,8 @@ local function make_self(driver, space, tube_name, tube_type, tube_id, opts)
362
381
end
363
382
-- task switched to taken - register in taken space
364
383
elseif task [2 ] == state .TAKEN then
365
- queue_taken :insert {session .id (), self .tube_id , task [1 ], fiber .time64 ()}
384
+ queue_taken :insert {get_quuid_by_sid (session .id ()), self .tube_id ,
385
+ task [1 ], fiber .time64 ()}
366
386
end
367
387
if stats_data ~= nil then
368
388
queue .stat [space .name ]:inc (stats_data )
@@ -393,17 +413,47 @@ local function make_self(driver, space, tube_name, tube_type, tube_id, opts)
393
413
return self
394
414
end
395
415
416
+ --- Release all session tasks if the session has no actual connections.
417
+ local function cleanup_session_tasks (queue_uuid )
418
+ local sesion_ids = box .space ._queue_session_ids .index .queue_uuid :select (
419
+ queue_uuid ,
420
+ {limit = 1 })[1 ]
421
+ if sesion_ids ~= nil then
422
+ return
423
+ end
424
+
425
+ -- Release all session tasks.
426
+ while true do
427
+ local task = box .space ._queue_taken .index .pk :min {queue_uuid }
428
+ if task == nil or task [1 ] ~= queue_uuid then
429
+ break
430
+ end
431
+
432
+ local tube_row = box .space ._queue .index .tube_id :get {task [2 ]}
433
+ if tube_row == nil then
434
+ log .error (" Inconsistent queue state: tube %d not found" , task [2 ])
435
+ box .space ._queue_taken :delete {queue_uuid , task [2 ], task [3 ]}
436
+ else
437
+ log .warn (" Session %s closed, release task %s" ,
438
+ uuid .frombin (queue_uuid ):str (), task [3 ])
439
+
440
+ tube_release_internal (queue .tube [tube_row [1 ]], task [3 ], nil ,
441
+ queue_uuid )
442
+ end
443
+ end
444
+ end
445
+
396
446
function method ._on_consumer_disconnect ()
397
- local waiter , fb , task , tube , id
398
- id = session . id ()
447
+ local sid = session . id ()
448
+
399
449
-- wakeup all waiters
400
450
while true do
401
- waiter = box .space ._queue_consumers .index .pk :min {id }
451
+ local waiter = box .space ._queue_consumers .index .pk :min {sid }
402
452
if waiter == nil then
403
453
break
404
454
end
405
455
-- Don't touch the other consumers
406
- if waiter [1 ] ~= id then
456
+ if waiter [1 ] ~= sid then
407
457
break
408
458
end
409
459
box .space ._queue_consumers :delete { waiter [1 ], waiter [2 ] }
@@ -414,24 +464,11 @@ function method._on_consumer_disconnect()
414
464
end
415
465
end
416
466
417
- -- release all session tasks
418
- while true do
419
- task = box .space ._queue_taken .index .pk :min {id }
420
- if task == nil or task [1 ] ~= id then
421
- break
422
- end
423
-
424
- tube = box .space ._queue .index .tube_id :get {task [2 ]}
425
- if tube == nil then
426
- log .error (" Inconsistent queue state: tube %d not found" , task [2 ])
427
- box .space ._queue_taken :delete {task [1 ], task [2 ], task [3 ] }
428
- else
429
- log .warn (" Consumer %s disconnected, release task %s(%s)" ,
430
- id , task [3 ], tube [1 ])
431
-
432
- tube_release_internal (queue .tube [tube [1 ]], task [3 ], nil , id )
433
- end
434
- end
467
+ -- Remove connection from the list of active connections and
468
+ -- release tasks if necessary.
469
+ local quuid = get_quuid_by_sid (sid )
470
+ box .space ._queue_session_ids :delete {sid }
471
+ cleanup_session_tasks (quuid )
435
472
end
436
473
437
474
-- function takes tuples and recreates tube
@@ -495,6 +532,31 @@ function method.create_tube(tube_name, tube_type, opts)
495
532
return self
496
533
end
497
534
535
+ --- Create everything that's needed to work with "shared" sessions.
536
+ local function identification_init ()
537
+ local queue_session_ids = box .space ._queue_session_ids
538
+ if queue_session_ids == nil then
539
+ queue_session_ids = box .schema .create_space (' _queue_session_ids' , {
540
+ temporary = true ,
541
+ format = {
542
+ {name = ' box_session_id' , type = num_type ()},
543
+ {name = ' queue_session_uuid' , type = str_type ()}
544
+ }
545
+ })
546
+
547
+ queue_session_ids :create_index (' box_sid' , {
548
+ type = ' tree' ,
549
+ parts = {1 , num_type ()},
550
+ unique = true
551
+ })
552
+ queue_session_ids :create_index (' queue_uuid' , {
553
+ type = ' tree' ,
554
+ parts = {2 , str_type ()},
555
+ unique = false
556
+ })
557
+ end
558
+ end
559
+
498
560
-- create or join infrastructure
499
561
function method .start ()
500
562
-- tube_name, tube_id, space_name, tube_type, opts
@@ -527,7 +589,7 @@ function method.start()
527
589
_cons = box .schema .create_space (' _queue_consumers' , {
528
590
temporary = true ,
529
591
format = {
530
- {name = ' session_id ' , type = num_type ()},
592
+ {name = ' box_session_id ' , type = num_type ()},
531
593
{name = ' fiber_id' , type = num_type ()},
532
594
{name = ' tube_id' , type = num_type ()},
533
595
{name = ' event_time' , type = num_type ()},
@@ -547,19 +609,27 @@ function method.start()
547
609
end
548
610
549
611
local _taken = box .space ._queue_taken
612
+
613
+ -- Format of the temporary space has been changed.
614
+ -- So recreate it if needed.
615
+ if _taken ~= nil and _taken :format ()[1 ][' type' ] == ' unsigned' then
616
+ _taken :drop ()
617
+ _taken = nil
618
+ end
619
+
550
620
if _taken == nil then
551
621
-- session_id, tube_id, task_id, time
552
622
_taken = box .schema .create_space (' _queue_taken' , {
553
623
temporary = true ,
554
624
format = {
555
- {name = ' session_id ' , type = num_type ()},
625
+ {name = ' queue_session_uuid ' , type = str_type ()},
556
626
{name = ' tube_id' , type = num_type ()},
557
627
{name = ' task_id' , type = num_type ()},
558
628
{name = ' taken_time' , type = num_type ()}
559
629
}})
560
630
_taken :create_index (' pk' , {
561
631
type = ' tree' ,
562
- parts = {1 , num_type (), 2 , num_type (), 3 , num_type ()},
632
+ parts = {1 , str_type (), 2 , num_type (), 3 , num_type ()},
563
633
unique = true })
564
634
565
635
_taken :create_index (' task' ,{
@@ -575,6 +645,8 @@ function method.start()
575
645
tube_release_all_tasks (tube )
576
646
end
577
647
648
+ identification_init ()
649
+
578
650
session .on_disconnect (queue ._on_consumer_disconnect )
579
651
return queue
580
652
end
@@ -613,6 +685,38 @@ local function build_stats(space)
613
685
return stats
614
686
end
615
687
688
+ --- Identificate the session.
689
+ -- If "queue_uuid" == nil - create a new UUID for the session
690
+ -- and return it, otherwise connect to the existing one.
691
+ function method .identificate (queue_uuid )
692
+ local queue_session_ids = box .space ._queue_session_ids
693
+ local sid = session .id ()
694
+ local session_ids = queue_session_ids .index .box_sid :get (sid )
695
+ local quuid = session_ids and session_ids [2 ]
696
+
697
+ if queue_uuid == nil and quuid == nil then
698
+ -- Generate new UUID for the session.
699
+ quuid = uuid .bin ()
700
+ queue_session_ids :insert {sid , quuid }
701
+ elseif queue_uuid ~= nil then
702
+ -- Identificate using a previously created session.
703
+
704
+ -- Validate UUID.
705
+ uuid .frombin (queue_uuid )
706
+
707
+ if quuid ~= queue_uuid then
708
+ queue_session_ids :upsert ({sid , queue_uuid }, {{' =' , 2 , queue_uuid }})
709
+ if quuid ~= nil then
710
+ -- Cleanup old session tasks.
711
+ cleanup_session_tasks (quuid )
712
+ end
713
+ quuid = queue_uuid
714
+ end
715
+ end
716
+
717
+ return quuid
718
+ end
719
+
616
720
queue .statistics = function (space )
617
721
if space ~= nil then
618
722
return build_stats (space )
0 commit comments