@@ -21,6 +21,7 @@ type futureEvent struct {
21
21
Event * history.Event `json:"event,omitempty"`
22
22
}
23
23
24
+ // Return all events with a visibleAt timestamp in the past. Also remove them from the set
24
25
// KEYS[1] - future event set key
25
26
// ARGV[1] - current timestamp for zrange
26
27
var futureEventsCmd = redis .NewScript (`
@@ -46,14 +47,17 @@ func (rb *redisBackend) GetWorkflowTask(ctx context.Context) (*task.Workflow, er
46
47
return nil , errors .Wrap (err , "could not unmarshal event" )
47
48
}
48
49
49
- if err := addEventToStream (ctx , rb .rdb , pendingEventsKey (event .Instance .InstanceID ), event .Event ); err != nil {
50
+ msgID , err := addEventToStream (ctx , rb .rdb , pendingEventsKey (event .Instance .InstanceID ), event .Event )
51
+ if err != nil {
50
52
return nil , errors .Wrap (err , "could not add future event to stream" )
51
53
}
52
54
53
55
// Instance now has at least one pending event, try to queue task
54
- if _ , err := rb .workflowQueue .Enqueue (ctx , event .Instance .InstanceID , nil ); err != nil {
56
+ if _ , err := rb .workflowQueue .Enqueue (ctx , event .Instance .InstanceID , & workflowTaskData {
57
+ LastPendingEventMessageID : * msgID ,
58
+ }); err != nil {
55
59
if err != taskqueue .ErrTaskAlreadyInQueue {
56
- return nil , errors .Wrap (err , "could not queue workflow" )
60
+ return nil , errors .Wrap (err , "could not queue workflow task " )
57
61
}
58
62
}
59
63
}
@@ -109,10 +113,6 @@ func (rb *redisBackend) GetWorkflowTask(ctx context.Context) (*task.Workflow, er
109
113
newEvents = append (newEvents , event )
110
114
}
111
115
112
- // Remove all pending events
113
- // TODO: What happens if the worker dies and this task gets picked up by another one?
114
- rb .rdb .XTrim (ctx , pendingEventsKey (instanceTask .ID ), 0 )
115
-
116
116
return & task.Workflow {
117
117
ID : instanceTask .TaskID ,
118
118
WorkflowInstance : instanceState .Instance ,
@@ -125,15 +125,31 @@ func (rb *redisBackend) ExtendWorkflowTask(ctx context.Context, taskID string, i
125
125
return rb .workflowQueue .Extend (ctx , taskID )
126
126
}
127
127
128
+ // Remove all pending events before (and including) a given message id
129
+ // KEYS[1] - pending events stream key
130
+ // ARGV[1] - message id
131
+ var removePendingEventsCmd = redis .NewScript (`
132
+ local trimmed = redis.call("XTRIM", KEYS[1], "MINID", ARGV[1])
133
+ local deleted = redis.call("XDEL", KEYS[1], ARGV[1])
134
+ local removed = trimmed + deleted
135
+ return removed
136
+ ` )
137
+
128
138
func (rb * redisBackend ) CompleteWorkflowTask (ctx context.Context , taskID string , instance * core.WorkflowInstance , state backend.WorkflowState , executedEvents []history.Event , activityEvents []history.Event , workflowEvents []history.WorkflowEvent ) error {
139
+ task , err := rb .workflowQueue .Data (ctx , taskID )
140
+ if err != nil {
141
+ return errors .Wrap (err , "could not get workflow task" )
142
+ }
143
+
129
144
// Add executed events to the history
145
+ // TODO: Use pipeline
130
146
for _ , executedEvent := range executedEvents {
131
- if err := addEventToStream (ctx , rb .rdb , historyKey (instance .InstanceID ), & executedEvent ); err != nil {
147
+ if _ , err := addEventToStream (ctx , rb .rdb , historyKey (instance .InstanceID ), & executedEvent ); err != nil {
132
148
return err
133
149
}
134
150
}
135
151
136
- // Send new events to the respective streams
152
+ // Send new workflow events to the respective streams
137
153
groupedEvents := make (map [* workflow.Instance ][]history.Event )
138
154
for _ , m := range workflowEvents {
139
155
if _ , ok := groupedEvents [m .WorkflowInstance ]; ! ok {
@@ -145,46 +161,36 @@ func (rb *redisBackend) CompleteWorkflowTask(ctx context.Context, taskID string,
145
161
146
162
for targetInstance , events := range groupedEvents {
147
163
if instance .InstanceID != targetInstance .InstanceID {
148
- // Try to create a new instance
164
+ // Instance might not exist, try to create a new instance ignoring any duplicates
149
165
if err := createInstance (ctx , rb .rdb , targetInstance , true ); err != nil {
150
166
return err
151
167
}
152
168
}
153
169
154
170
// Insert pending events for target instance
155
- addedPendingEvent := false
171
+ var lastPendingMessageID * string
156
172
173
+ // TODO: use pipelines
157
174
for _ , event := range events {
158
175
if event .VisibleAt != nil {
159
- // Add future event in sorted set
160
- futureEvent := & futureEvent {
161
- Instance : targetInstance ,
162
- Event : & event ,
163
- }
164
-
165
- eventData , err := json .Marshal (futureEvent )
166
- if err != nil {
176
+ // Add future events
177
+ if err := addFutureEvent (ctx , rb .rdb , targetInstance , & event ); err != nil {
167
178
return err
168
179
}
169
-
170
- if err := rb .rdb .ZAdd (ctx , futureEventsKey (), & redis.Z {
171
- Member : eventData ,
172
- Score : float64 (event .VisibleAt .Unix ()),
173
- }).Err (); err != nil {
174
- return errors .Wrap (err , "could not add future event" )
175
- }
176
180
} else {
177
181
// Add pending event to stream
178
- if err := addEventToStream (ctx , rb .rdb , pendingEventsKey (targetInstance .InstanceID ), & event ); err != nil {
182
+ lastPendingMessageID , err = addEventToStream (ctx , rb .rdb , pendingEventsKey (targetInstance .InstanceID ), & event )
183
+ if err != nil {
179
184
return err
180
185
}
181
-
182
- addedPendingEvent = true
183
186
}
184
187
}
185
188
186
- if addedPendingEvent && targetInstance != instance {
187
- if _ , err := rb .workflowQueue .Enqueue (ctx , targetInstance .InstanceID , nil ); err != nil {
189
+ // If any pending message was added, try to queue workflow task
190
+ if lastPendingMessageID != nil && targetInstance != instance {
191
+ if _ , err := rb .workflowQueue .Enqueue (ctx , targetInstance .InstanceID , & workflowTaskData {
192
+ LastPendingEventMessageID : * lastPendingMessageID ,
193
+ }); err != nil {
188
194
if err != taskqueue .ErrTaskAlreadyInQueue {
189
195
return errors .Wrap (err , "could not add instance to locked instances set" )
190
196
}
@@ -215,19 +221,28 @@ func (rb *redisBackend) CompleteWorkflowTask(ctx context.Context, taskID string,
215
221
}
216
222
}
217
223
224
+ // Remove executed pending events
225
+ _ , err = removePendingEventsCmd .Run (ctx , rb .rdb , []string {pendingEventsKey (instance .InstanceID )}, task .Data .LastPendingEventMessageID ).Result ()
226
+ if err != nil {
227
+ return errors .Wrap (err , "could not remove pending events" )
228
+ }
229
+ // log.Printf("Removed %v pending events", removed)
230
+
218
231
// Complete workflow task and unlock instance
219
232
if err := rb .workflowQueue .Complete (ctx , taskID ); err != nil {
220
233
return errors .Wrap (err , "could not complete workflow task" )
221
234
}
222
235
223
236
// If there are pending events, queue the instance again
224
- pendingCount , err := rb .rdb .XLen (ctx , pendingEventsKey (instance .InstanceID )).Result ()
237
+ msgIDs , err := rb .rdb .XRevRangeN (ctx , pendingEventsKey (instance .InstanceID ), "+" , "-" , 1 ).Result ()
225
238
if err != nil {
226
239
return errors .Wrap (err , "could not read event stream" )
227
240
}
228
241
229
- if state != backend .WorkflowStateFinished && pendingCount > 0 {
230
- if _ , err := rb .workflowQueue .Enqueue (ctx , instance .InstanceID , nil ); err != nil {
242
+ if state != backend .WorkflowStateFinished && len (msgIDs ) > 0 {
243
+ if _ , err := rb .workflowQueue .Enqueue (ctx , instance .InstanceID , & workflowTaskData {
244
+ LastPendingEventMessageID : msgIDs [0 ].ID ,
245
+ }); err != nil {
231
246
if err != taskqueue .ErrTaskAlreadyInQueue {
232
247
return errors .Wrap (err , "could not queue workflow" )
233
248
}
@@ -239,12 +254,15 @@ func (rb *redisBackend) CompleteWorkflowTask(ctx context.Context, taskID string,
239
254
240
255
func (rb * redisBackend ) addWorkflowInstanceEvent (ctx context.Context , instance * core.WorkflowInstance , event * history.Event ) error {
241
256
// Add event to pending events for instance
242
- if err := addEventToStream (ctx , rb .rdb , pendingEventsKey (instance .InstanceID ), event ); err != nil {
257
+ msgID , err := addEventToStream (ctx , rb .rdb , pendingEventsKey (instance .InstanceID ), event )
258
+ if err != nil {
243
259
return err
244
260
}
245
261
246
262
// Queue workflow task
247
- if _ , err := rb .workflowQueue .Enqueue (ctx , instance .InstanceID , nil ); err != nil {
263
+ if _ , err := rb .workflowQueue .Enqueue (ctx , instance .InstanceID , & workflowTaskData {
264
+ LastPendingEventMessageID : * msgID ,
265
+ }); err != nil {
248
266
if err != taskqueue .ErrTaskAlreadyInQueue {
249
267
return errors .Wrap (err , "could not queue workflow" )
250
268
}
0 commit comments