@@ -30,15 +30,19 @@ import (
30
30
"errors"
31
31
"sync/atomic"
32
32
"testing"
33
+ "time"
33
34
34
35
"github.com/golang/mock/gomock"
35
36
"github.com/stretchr/testify/require"
36
37
commonpb "go.temporal.io/api/common/v1"
37
38
historypb "go.temporal.io/api/history/v1"
39
+ protocolpb "go.temporal.io/api/protocol/v1"
38
40
taskqueuepb "go.temporal.io/api/taskqueue/v1"
41
+ "go.temporal.io/api/update/v1"
39
42
"go.temporal.io/api/workflowservice/v1"
40
43
"go.temporal.io/api/workflowservicemock/v1"
41
44
"google.golang.org/grpc"
45
+ "google.golang.org/protobuf/types/known/durationpb"
42
46
)
43
47
44
48
type countingTaskHandler struct {
@@ -222,3 +226,154 @@ func TestWFTCorruption(t *testing.T) {
222
226
// Workflow should not be in cache
223
227
require .Nil (t , cache .getWorkflowContext (runID ))
224
228
}
229
+
230
+ func TestWFTReset (t * testing.T ) {
231
+ cache := NewWorkerCache ()
232
+ params := workerExecutionParameters {
233
+ cache : cache ,
234
+ }
235
+ ensureRequiredParams (& params )
236
+ wfType := commonpb.WorkflowType {Name : t .Name () + "-workflow-type" }
237
+ reg := newRegistry ()
238
+ reg .RegisterWorkflowWithOptions (func (ctx Context ) error {
239
+ _ = SetUpdateHandler (ctx , "update" , func (ctx Context ) error {
240
+ return nil
241
+ }, UpdateHandlerOptions {
242
+ Validator : func (ctx Context ) error {
243
+ return errors .New ("rejecting for test" )
244
+ },
245
+ })
246
+ _ = Sleep (ctx , time .Second )
247
+ return Sleep (ctx , time .Second )
248
+ }, RegisterWorkflowOptions {
249
+ Name : wfType .Name ,
250
+ })
251
+ var (
252
+ taskQueue = taskqueuepb.TaskQueue {Name : t .Name () + "task-queue" }
253
+ history0 = historypb.History {Events : []* historypb.HistoryEvent {
254
+ createTestEventWorkflowExecutionStarted (1 , & historypb.WorkflowExecutionStartedEventAttributes {
255
+ TaskQueue : & taskQueue ,
256
+ }),
257
+ createTestEventWorkflowTaskScheduled (2 , & historypb.WorkflowTaskScheduledEventAttributes {
258
+ TaskQueue : & taskQueue ,
259
+ StartToCloseTimeout : & durationpb.Duration {Seconds : 10 },
260
+ Attempt : 1 ,
261
+ }),
262
+ createTestEventWorkflowTaskStarted (3 ),
263
+ createTestEventWorkflowTaskCompleted (4 , & historypb.WorkflowTaskCompletedEventAttributes {
264
+ ScheduledEventId : 2 ,
265
+ StartedEventId : 3 ,
266
+ }),
267
+ createTestEventTimerStarted (5 , 5 ),
268
+ createTestEventWorkflowTaskScheduled (6 , & historypb.WorkflowTaskScheduledEventAttributes {
269
+ TaskQueue : & taskQueue ,
270
+ StartToCloseTimeout : & durationpb.Duration {Seconds : 10 },
271
+ Attempt : 1 ,
272
+ }),
273
+ createTestEventWorkflowTaskStarted (7 ),
274
+ }}
275
+ messages = []* protocolpb.Message {
276
+ createTestProtocolMessageUpdateRequest ("test-update" , 6 , & update.Request {
277
+ Meta : & update.Meta {
278
+ UpdateId : "test-update" ,
279
+ },
280
+ Input : & update.Input {
281
+ Name : "update" ,
282
+ },
283
+ }),
284
+ }
285
+ history1 = historypb.History {Events : []* historypb.HistoryEvent {
286
+ createTestEventWorkflowTaskCompleted (4 , & historypb.WorkflowTaskCompletedEventAttributes {
287
+ ScheduledEventId : 2 ,
288
+ StartedEventId : 3 ,
289
+ }),
290
+ createTestEventTimerStarted (5 , 5 ),
291
+ createTestEventWorkflowTaskScheduled (6 , & historypb.WorkflowTaskScheduledEventAttributes {
292
+ TaskQueue : & taskQueue ,
293
+ StartToCloseTimeout : & durationpb.Duration {Seconds : 10 },
294
+ Attempt : 1 ,
295
+ }),
296
+ createTestEventWorkflowTaskStarted (7 ),
297
+ }}
298
+ history2 = historypb.History {Events : []* historypb.HistoryEvent {
299
+ createTestEventWorkflowTaskCompleted (4 , & historypb.WorkflowTaskCompletedEventAttributes {
300
+ ScheduledEventId : 2 ,
301
+ StartedEventId : 3 ,
302
+ }),
303
+ createTestEventTimerStarted (5 , 5 ),
304
+ createTestEventTimerFired (6 , 5 ),
305
+ createTestEventWorkflowTaskScheduled (7 , & historypb.WorkflowTaskScheduledEventAttributes {
306
+ TaskQueue : & taskQueue ,
307
+ StartToCloseTimeout : & durationpb.Duration {Seconds : 10 },
308
+ Attempt : 1 ,
309
+ }),
310
+ createTestEventWorkflowTaskStarted (8 ),
311
+ }}
312
+ runID = t .Name () + "-run-id"
313
+ wfID = t .Name () + "-workflow-id"
314
+ wfe = commonpb.WorkflowExecution {RunId : runID , WorkflowId : wfID }
315
+ ctrl = gomock .NewController (t )
316
+ client = workflowservicemock .NewMockWorkflowServiceClient (ctrl )
317
+ innerTaskHandler = newWorkflowTaskHandler (params , nil , reg )
318
+ taskHandler = & countingTaskHandler {WorkflowTaskHandler : innerTaskHandler }
319
+ contextManager = taskHandler
320
+ pollResp0 = workflowservice.PollWorkflowTaskQueueResponse {
321
+ Attempt : 1 ,
322
+ WorkflowExecution : & wfe ,
323
+ WorkflowType : & wfType ,
324
+ History : & history0 ,
325
+ Messages : messages ,
326
+ PreviousStartedEventId : 3 ,
327
+ }
328
+ task0 = workflowTask {task : & pollResp0 }
329
+ pollResp1 = workflowservice.PollWorkflowTaskQueueResponse {
330
+ Attempt : 1 ,
331
+ WorkflowExecution : & wfe ,
332
+ WorkflowType : & wfType ,
333
+ History : & history1 ,
334
+ PreviousStartedEventId : 3 ,
335
+ }
336
+ task1 = workflowTask {task : & pollResp1 }
337
+ pollResp2 = workflowservice.PollWorkflowTaskQueueResponse {
338
+ Attempt : 1 ,
339
+ WorkflowExecution : & wfe ,
340
+ WorkflowType : & wfType ,
341
+ History : & history2 ,
342
+ PreviousStartedEventId : 3 ,
343
+ }
344
+ task2 = workflowTask {task : & pollResp2 }
345
+ )
346
+
347
+ // Return a workflow task to reset the workflow to a previous state
348
+ client .EXPECT ().RespondWorkflowTaskCompleted (gomock .Any (), gomock .Any ()).
349
+ Return (& workflowservice.RespondWorkflowTaskCompletedResponse {
350
+ ResetHistoryEventId : 3 ,
351
+ }, nil ).Times (3 )
352
+ // Return a workflow task to complete the workflow
353
+ client .EXPECT ().RespondWorkflowTaskCompleted (gomock .Any (), gomock .Any ()).
354
+ Return (& workflowservice.RespondWorkflowTaskCompletedResponse {}, nil )
355
+
356
+ poller := newWorkflowTaskPoller (taskHandler , contextManager , client , params )
357
+ // Send a full history as part of the speculative WFT
358
+ require .NoError (t , poller .processWorkflowTask (& task0 ))
359
+ originalCachedExecution := cache .getWorkflowContext (runID )
360
+ require .NotNil (t , originalCachedExecution )
361
+ require .Equal (t , int64 (3 ), originalCachedExecution .previousStartedEventID )
362
+ require .Equal (t , int64 (5 ), originalCachedExecution .lastHandledEventID )
363
+ // Send some fake speculative WFTs to ensure the workflow is reset properly
364
+ require .NoError (t , poller .processWorkflowTask (& task1 ))
365
+ cachedExecution := cache .getWorkflowContext (runID )
366
+ require .True (t , originalCachedExecution == cachedExecution )
367
+ require .Equal (t , int64 (3 ), cachedExecution .previousStartedEventID )
368
+ require .Equal (t , int64 (5 ), cachedExecution .lastHandledEventID )
369
+ require .NoError (t , poller .processWorkflowTask (& task1 ))
370
+ cachedExecution = cache .getWorkflowContext (runID )
371
+ // Check the cached execution is the same as the original
372
+ require .True (t , originalCachedExecution == cachedExecution )
373
+ require .Equal (t , int64 (3 ), cachedExecution .previousStartedEventID )
374
+ require .Equal (t , int64 (5 ), cachedExecution .lastHandledEventID )
375
+ // Send a real WFT with new events
376
+ require .NoError (t , poller .processWorkflowTask (& task2 ))
377
+ cachedExecution = cache .getWorkflowContext (runID )
378
+ require .True (t , originalCachedExecution == cachedExecution )
379
+ }
0 commit comments