@@ -14,11 +14,11 @@ import (
1414)
1515
1616var (
17- prepareCmd * valkey.Lua
18- // enqueueCmd *valkey.Lua
19- // completeCmd *valkey.Lua
20- // recoverCmd *valkey.Lua
21- // sizeCmd *valkey.Lua
17+ prepareCmd * valkey.Lua
18+ enqueueCmd * valkey.Lua
19+ completeCmd * valkey.Lua
20+ recoverCmd * valkey.Lua
21+ sizeCmd * valkey.Lua
2222)
2323
2424type taskQueue [T any ] struct {
@@ -65,7 +65,11 @@ func newTaskQueue[T any](keyPrefix, tasktype, workerName string) (*taskQueue[T],
6565
6666 // Load all Lua scripts
6767 cmdMapping := map [string ]* * valkey.Lua {
68- "queue/prepare.lua" : & prepareCmd ,
68+ "queue/prepare.lua" : & prepareCmd ,
69+ "queue/size.lua" : & sizeCmd ,
70+ "queue/recover.lua" : & recoverCmd ,
71+ "queue/enqueue.lua" : & enqueueCmd ,
72+ "queue/complete.lua" : & completeCmd ,
6973 }
7074
7175 if err := loadScripts (cmdMapping ); err != nil {
@@ -97,24 +101,27 @@ func (q *taskQueue[T]) Keys(queue workflow.Queue) KeyInfo {
97101}
98102
99103func (q * taskQueue [T ]) Size (ctx context.Context , client valkey.Client ) (map [workflow.Queue ]int64 , error ) {
100- members , err := client . Do (ctx , client . B (). Smembers (). Key ( q .queueSetKey ). Build ()). AsStrSlice ()
104+ sizeData , err := sizeCmd . Exec (ctx , client , [] string { q .queueSetKey }, [] string {}). ToArray ()
101105 if err != nil {
102106 return nil , fmt .Errorf ("getting queue size: %w" , err )
103107 }
104108
105109 res := map [workflow.Queue ]int64 {}
106- for _ , queueSetKey := range members {
107- size , err := client . Do ( ctx , client . B (). Scard (). Key ( queueSetKey ). Build ()). AsInt64 ()
110+ for i := 0 ; i < len ( sizeData ); i += 2 {
111+ queueName , err := sizeData [ i ]. ToString ()
108112 if err != nil {
109- return nil , fmt .Errorf ("getting queue size : %w" , err )
113+ return nil , fmt .Errorf ("parsing queue name : %w" , err )
110114 }
111115
112- trimmed := strings .TrimPrefix (queueSetKey , q .keyPrefix )
113- lastIdx := strings .LastIndex (trimmed , ":" )
114- if lastIdx == - 1 || lastIdx == len (trimmed )- 1 {
115- return nil , fmt .Errorf ("unexpected set key format: %s" , queueSetKey )
116+ queueName = strings .TrimPrefix (queueName , q .keyPrefix )
117+ queueName = strings .Split (queueName , ":" )[1 ] // queue name is the third part of the key (0-indexed)
118+
119+ queue := workflow .Queue (queueName )
120+ size , err := sizeData [i + 1 ].AsInt64 ()
121+ if err != nil {
122+ return nil , fmt .Errorf ("parsing queue size: %w" , err )
116123 }
117- queue := workflow . Queue ( trimmed [ lastIdx + 1 :])
124+
118125 res [queue ] = size
119126 }
120127
@@ -129,24 +136,8 @@ func (q *taskQueue[T]) Enqueue(ctx context.Context, client valkey.Client, queue
129136
130137 keys := q .Keys (queue )
131138
132- // Add to set to track uniqueness
133- err = client .Do (ctx , client .B ().Sadd ().Key (q .queueSetKey ).Member (keys .SetKey ).Build ()).Error ()
134- if err != nil {
135- return err
136- }
137-
138- // Add to set for this queue
139- added , err := client .Do (ctx , client .B ().Sadd ().Key (keys .SetKey ).Member (id ).Build ()).AsInt64 ()
140- if err != nil {
141- return err
142- }
143-
144- // Only add to stream if it's a new task
145- if added > 0 {
146- err = client .Do (ctx , client .B ().Xadd ().Key (keys .StreamKey ).Id ("*" ).FieldValue ().FieldValue ("id" , id ).FieldValue ("data" , string (ds )).Build ()).Error ()
147- if err != nil {
148- return err
149- }
139+ if err := enqueueCmd .Exec (ctx , client , []string {q .queueSetKey , keys .SetKey , keys .StreamKey }, []string {q .groupName , id , string (ds )}).Error (); err != nil {
140+ return fmt .Errorf ("enqueueing task: %w" , err )
150141 }
151142
152143 return nil
@@ -209,84 +200,67 @@ func (q *taskQueue[T]) Extend(ctx context.Context, client valkey.Client, queue w
209200}
210201
211202func (q * taskQueue [T ]) Complete (ctx context.Context , client valkey.Client , queue workflow.Queue , taskID string ) error {
212- keyInfo := q .Keys (queue )
213-
214- // Get the task to find the ID
215- msgs , err := client .Do (ctx , client .B ().Xrange ().Key (keyInfo .StreamKey ).Start (taskID ).End (taskID ).Build ()).AsXRange ()
216- if err != nil {
217- // Check if error is due to no data available (nil response)
218- if valkey .IsValkeyNil (err ) {
219- return nil
220- }
203+ err := completeCmd .Exec (ctx , client , []string {
204+ q .Keys (queue ).SetKey ,
205+ q .Keys (queue ).StreamKey ,
206+ }, []string {taskID , q .groupName }).Error ()
207+ if err != nil && ! valkey .IsValkeyNil (err ) {
221208 return fmt .Errorf ("completing task: %w" , err )
222209 }
223210
224- if len (msgs ) == 0 {
225- return nil
226- }
227-
228- msg := msgs [0 ]
229- id , ok := msg .FieldValues ["id" ]
230- if ! ok {
231- return fmt .Errorf ("completing task: missing id field" )
232- }
211+ return nil
212+ }
233213
234- // Remove from set
235- err = client . Do ( ctx , client . B (). Srem (). Key ( keyInfo . SetKey ). Member ( id ). Build ()). Error ()
236- if err != nil {
237- return fmt . Errorf ( "completing task: %w" , err )
214+ func ( q * taskQueue [ T ]) recover ( ctx context. Context , client valkey. Client , queues []workflow. Queue , idleTimeout time. Duration ) ( * TaskItem [ T ], error ) {
215+ var keys [] string
216+ for _ , queue := range queues {
217+ keys = append ( keys , q . Keys ( queue ). StreamKey )
238218 }
239219
240- // Acknowledge in consumer group
241- err = client .Do (ctx , client .B ().Xack ().Key (keyInfo .StreamKey ).Group (q .groupName ).Id (taskID ).Build ()).Error ()
220+ r , err := recoverCmd .Exec (ctx , client , keys , []string {q .groupName , q .workerName , strconv .FormatInt (idleTimeout .Milliseconds (), 10 ), "0" }).ToArray ()
242221 if err != nil {
243- return fmt .Errorf ("completing task: %w" , err )
244- }
222+ if valkey .IsValkeyNil (err ) {
223+ return nil , nil
224+ }
245225
246- // Delete from stream
247- err = client .Do (ctx , client .B ().Xdel ().Key (keyInfo .StreamKey ).Id (taskID ).Build ()).Error ()
248- if err != nil {
249- return fmt .Errorf ("completing task: %w" , err )
226+ return nil , fmt .Errorf ("recovering abandoned task: %w" , err )
250227 }
251228
252- return nil
253- }
254-
255- func (q * taskQueue [T ]) recover (ctx context.Context , client valkey.Client , queues []workflow.Queue , idleTimeout time.Duration ) (* TaskItem [T ], error ) {
256- for _ , queue := range queues {
257- streamKey := q .Keys (queue ).StreamKey
258-
259- // Try to recover abandoned tasks
260- cmd := client .B ().Xautoclaim ().Key (streamKey ).Group (q .groupName ).Consumer (q .workerName ).MinIdleTime (strconv .FormatInt (idleTimeout .Milliseconds (), 10 )).Start ("0" ).Count (1 )
261- msgs , err := client .Do (ctx , cmd .Build ()).ToArray ()
229+ if len (r ) > 1 {
230+ msgs , err := r [1 ].ToArray ()
262231 if err != nil {
263- // Check if error is due to no data available (nil response)
264- if valkey .IsValkeyNil (err ) {
265- continue
266- }
267232 return nil , fmt .Errorf ("recovering abandoned task: %w" , err )
268233 }
269-
270- if len (msgs ) >= 2 {
271- entries , _ := msgs [1 ].ToArray ()
272- for _ , entry := range entries {
273- arr , _ := entry .ToArray ()
274- if len (arr ) == 2 {
275- id , _ := arr [0 ].ToString ()
276- fieldsArr , _ := arr [1 ].ToArray ()
277- fieldValues := map [string ]string {}
278- for i := 0 ; i + 1 < len (fieldsArr ); i += 2 {
279- key , _ := fieldsArr [i ].ToString ()
280- val , _ := fieldsArr [i + 1 ].ToString ()
281- fieldValues [key ] = val
282- }
283- xEntry := valkey.XRangeEntry {
284- ID : id ,
285- FieldValues : fieldValues ,
286- }
287- return msgToTaskItem [T ](xEntry )
234+ if len (msgs ) > 0 && ! msgs [0 ].IsNil () {
235+ msgData , err := msgs [0 ].ToArray ()
236+ if err != nil {
237+ return nil , fmt .Errorf ("recovering abandoned task: %w" , err )
238+ }
239+ id , err := msgData [0 ].ToString ()
240+ if err != nil {
241+ return nil , fmt .Errorf ("recovering abandoned task: %w" , err )
242+ }
243+ rawValues , err := msgData [1 ].ToArray ()
244+ if err != nil {
245+ return nil , fmt .Errorf ("recovering abandoned task: %w" , err )
246+ }
247+ values := make (map [string ]string )
248+ for i := 0 ; i < len (rawValues ); i += 2 {
249+ key , err := rawValues [i ].ToString ()
250+ if err != nil {
251+ return nil , fmt .Errorf ("recovering abandoned task: %w" , err )
252+ }
253+ value , err := rawValues [i + 1 ].ToString ()
254+ if err != nil {
255+ return nil , fmt .Errorf ("recovering abandoned task: %w" , err )
288256 }
257+ values [key ] = value
289258 }
259+
260+ return msgToTaskItem [T ](valkey.XRangeEntry {
261+ ID : id ,
262+ FieldValues : values ,
263+ })
290264 }
291265 }
292266
0 commit comments