Skip to content

Commit 0fc83e9

Browse files
authored
Update internal timers to use NewTimerWithOptions (#1618)
* switch internal timers to use options, expose StaticSummary and StaticDetails * plumb options through AwaitWithTimeout * create new API, don't break existing API * missed a spot to remove API change * add experimental tag, fix test * take out StaticSummary StaticDetails changes * missed a few spots * remove duplicate code * cleaner code share * AwaitOptions * alias AwaitOptions in public package * add unit test * wip * test works with prettifyString logging * clean up * no need for unit test now that we have better E2E test * remove print
1 parent fa54195 commit 0fc83e9

File tree

6 files changed

+120
-13
lines changed

6 files changed

+120
-13
lines changed

internal/interceptor.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ type WorkflowOutboundInterceptor interface {
213213
// AwaitWithTimeout intercepts workflow.AwaitWithTimeout.
214214
AwaitWithTimeout(ctx Context, timeout time.Duration, condition func() bool) (bool, error)
215215

216+
// AwaitWithOptions intercepts workflow.AwaitWithOptions.
217+
//
218+
// NOTE: Experimental
219+
AwaitWithOptions(ctx Context, options AwaitOptions, condition func() bool) (bool, error)
220+
216221
// ExecuteActivity intercepts workflow.ExecuteActivity.
217222
// interceptor.WorkflowHeader will return a non-nil map for this context.
218223
ExecuteActivity(ctx Context, activityType string, args ...interface{}) Future

internal/interceptor_base.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,13 @@ func (w *WorkflowOutboundInterceptorBase) AwaitWithTimeout(ctx Context, timeout
205205
return w.Next.AwaitWithTimeout(ctx, timeout, condition)
206206
}
207207

208+
// AwaitWithOptions implements WorkflowOutboundInterceptor.AwaitWithOptions.
209+
//
210+
// NOTE: Experimental
211+
func (w *WorkflowOutboundInterceptorBase) AwaitWithOptions(ctx Context, options AwaitOptions, condition func() bool) (bool, error) {
212+
return w.Next.AwaitWithOptions(ctx, options, condition)
213+
}
214+
208215
// ExecuteLocalActivity implements WorkflowOutboundInterceptor.ExecuteLocalActivity.
209216
func (w *WorkflowOutboundInterceptorBase) ExecuteLocalActivity(
210217
ctx Context,

internal/workflow.go

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,20 @@ type (
458458
// NOTE: Experimental
459459
Summary string
460460
}
461+
462+
// AwaitOptions are options set when creating an await.
463+
//
464+
// NOTE: Experimental
465+
AwaitOptions struct {
466+
// Timeout is the await timeout if the await condition is not met.
467+
//
468+
// NOTE: Experimental
469+
Timeout time.Duration
470+
// TimerOptions are options set for the underlying timer created.
471+
//
472+
// NOTE: Experimental
473+
TimerOptions TimerOptions
474+
}
461475
)
462476

463477
// Await blocks the calling thread until condition() returns true
@@ -485,34 +499,51 @@ func (wc *workflowEnvironmentInterceptor) Await(ctx Context, condition func() bo
485499
return nil
486500
}
487501

488-
// AwaitWithTimeout blocks the calling thread until condition() returns true
489-
// Returns ok equals to false if timed out and err equals to CanceledError if the ctx is canceled.
490-
func AwaitWithTimeout(ctx Context, timeout time.Duration, condition func() bool) (ok bool, err error) {
491-
assertNotInReadOnlyState(ctx)
492-
state := getState(ctx)
493-
return state.dispatcher.interceptor.AwaitWithTimeout(ctx, timeout, condition)
494-
}
495-
496-
func (wc *workflowEnvironmentInterceptor) AwaitWithTimeout(ctx Context, timeout time.Duration, condition func() bool) (ok bool, err error) {
502+
func (wc *workflowEnvironmentInterceptor) awaitWithOptions(ctx Context, options AwaitOptions, condition func() bool, functionName string) (ok bool, err error) {
497503
state := getState(ctx)
498504
defer state.unblocked()
499-
timer := NewTimer(ctx, timeout)
505+
timer := NewTimerWithOptions(ctx, options.Timeout, options.TimerOptions)
500506
for !condition() {
501507
doneCh := ctx.Done()
502508
// TODO: Consider always returning a channel
503509
if doneCh != nil {
504510
if _, more := doneCh.ReceiveAsyncWithMoreFlag(nil); !more {
505-
return false, NewCanceledError("AwaitWithTimeout context canceled")
511+
return false, NewCanceledError("%s context canceled", functionName)
506512
}
507513
}
508514
if timer.IsReady() {
509515
return false, nil
510516
}
511-
state.yield("AwaitWithTimeout")
517+
state.yield(functionName)
512518
}
513519
return true, nil
514520
}
515521

522+
// AwaitWithTimeout blocks the calling thread until condition() returns true
523+
// Returns ok equals to false if timed out and err equals to CanceledError if the ctx is canceled.
524+
func AwaitWithTimeout(ctx Context, timeout time.Duration, condition func() bool) (ok bool, err error) {
525+
assertNotInReadOnlyState(ctx)
526+
state := getState(ctx)
527+
return state.dispatcher.interceptor.AwaitWithTimeout(ctx, timeout, condition)
528+
}
529+
530+
func (wc *workflowEnvironmentInterceptor) AwaitWithTimeout(ctx Context, timeout time.Duration, condition func() bool) (ok bool, err error) {
531+
options := AwaitOptions{Timeout: timeout, TimerOptions: TimerOptions{Summary: "AwaitWithTimeout"}}
532+
return wc.awaitWithOptions(ctx, options, condition, "AwaitWithTimeout")
533+
}
534+
535+
// AwaitWithOptions blocks the calling thread until condition() returns true
536+
// Returns ok equals to false if timed out and err equals to CanceledError if the ctx is canceled.
537+
func AwaitWithOptions(ctx Context, options AwaitOptions, condition func() bool) (ok bool, err error) {
538+
assertNotInReadOnlyState(ctx)
539+
state := getState(ctx)
540+
return state.dispatcher.interceptor.AwaitWithOptions(ctx, options, condition)
541+
}
542+
543+
func (wc *workflowEnvironmentInterceptor) AwaitWithOptions(ctx Context, options AwaitOptions, condition func() bool) (ok bool, err error) {
544+
return wc.awaitWithOptions(ctx, options, condition, "AwaitWithOptions")
545+
}
546+
516547
// NewChannel create new Channel instance
517548
func NewChannel(ctx Context) Channel {
518549
state := getState(ctx)
@@ -1331,7 +1362,7 @@ func Sleep(ctx Context, d time.Duration) (err error) {
13311362
}
13321363

13331364
func (wc *workflowEnvironmentInterceptor) Sleep(ctx Context, d time.Duration) (err error) {
1334-
t := NewTimer(ctx, d)
1365+
t := NewTimerWithOptions(ctx, d, TimerOptions{Summary: "Sleep"})
13351366
err = t.Get(ctx, nil)
13361367
return
13371368
}

test/integration_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6266,6 +6266,37 @@ func (ts *IntegrationTestSuite) TestUserMetadata() {
62666266
ts.Equal("my-timer", str)
62676267
}
62686268

6269+
func (ts *IntegrationTestSuite) TestAwaitWithOptionsTimeout() {
6270+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
6271+
defer cancel()
6272+
var str string
6273+
6274+
// Start workflow
6275+
opts := ts.startWorkflowOptions("test-await-options" + uuid.New())
6276+
run, err := ts.client.ExecuteWorkflow(ctx, opts,
6277+
ts.workflows.AwaitWithOptions)
6278+
ts.NoError(err)
6279+
6280+
// Confirm workflow has completed
6281+
ts.NoError(run.Get(ctx, nil))
6282+
6283+
// Confirm AwaitWithOptions's underlying timer has fired properly
6284+
iter := ts.client.GetWorkflowHistory(ctx, opts.ID, run.GetRunID(), false, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT)
6285+
var timerEvent *historypb.HistoryEvent
6286+
for iter.HasNext() {
6287+
event, err1 := iter.Next()
6288+
ts.NoError(err1)
6289+
if event.GetTimerStartedEventAttributes() != nil {
6290+
ts.Nil(timerEvent)
6291+
timerEvent = event
6292+
}
6293+
}
6294+
ts.NotNil(timerEvent)
6295+
ts.NoError(converter.GetDefaultDataConverter().FromPayload(
6296+
timerEvent.UserMetadata.Summary, &str))
6297+
ts.Equal("await-timer", str)
6298+
}
6299+
62696300
// executeWorkflow executes a given workflow and waits for the result
62706301
func (ts *IntegrationTestSuite) executeWorkflow(
62716302
wfID string, wfFunc interface{}, retValPtr interface{}, args ...interface{},

test/workflow_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3119,6 +3119,18 @@ func (w *Workflows) UserMetadata(ctx workflow.Context) error {
31193119
).Get(ctx, nil)
31203120
}
31213121

3122+
func (w *Workflows) AwaitWithOptions(ctx workflow.Context) (bool, error) {
3123+
options := workflow.AwaitOptions{
3124+
Timeout: 1 * time.Millisecond,
3125+
TimerOptions: workflow.TimerOptions{Summary: "await-timer"},
3126+
}
3127+
3128+
return workflow.AwaitWithOptions(ctx, options, func() bool {
3129+
return true
3130+
})
3131+
3132+
}
3133+
31223134
func (w *Workflows) RunsLocalAndNonlocalActsWithRetries(ctx workflow.Context, numOfEachActKind int, actFailTimes int) error {
31233135
var activities *Activities
31243136
futures := make([]workflow.Future, 0)
@@ -3268,6 +3280,7 @@ func (w *Workflows) register(worker worker.Worker) {
32683280
worker.RegisterWorkflow(w.UpdateWithMutex)
32693281
worker.RegisterWorkflow(w.UpdateWithSemaphore)
32703282
worker.RegisterWorkflow(w.UserMetadata)
3283+
worker.RegisterWorkflow(w.AwaitWithOptions)
32713284
worker.RegisterWorkflow(w.WorkflowWithRejectableUpdate)
32723285

32733286
worker.RegisterWorkflow(w.child)

workflow/deterministic_wrappers.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ type (
7272
//
7373
// NOTE: Experimental
7474
TimerOptions = internal.TimerOptions
75+
76+
// AwaitOptions are options for [AwaitWithOptions]
77+
//
78+
// NOTE: Experimental
79+
AwaitOptions = internal.AwaitOptions
7580
)
7681

7782
// Await blocks the calling thread until condition() returns true.
@@ -111,6 +116,21 @@ func AwaitWithTimeout(ctx Context, timeout time.Duration, condition func() bool)
111116
return internal.AwaitWithTimeout(ctx, timeout, condition)
112117
}
113118

119+
// AwaitWithOptions blocks the calling thread until condition() returns true
120+
// or blocking time exceeds the passed timeout value.
121+
// Returns ok=false if timed out, and err CanceledError if the ctx is canceled.
122+
// The following code will block until the captured count
123+
// variable is set to 5, or one hour passes.
124+
//
125+
// workflow.AwaitWithOptions(ctx, AwaitOptions{Timeout: time.Hour, TimerOptions: TimerOptions{Summary:"Example"}}, func() bool {
126+
// return count == 5
127+
// })
128+
//
129+
// NOTE: Experimental
130+
func AwaitWithOptions(ctx Context, options AwaitOptions, condition func() bool) (ok bool, err error) {
131+
return internal.AwaitWithOptions(ctx, options, condition)
132+
}
133+
114134
// NewChannel creates a new Channel instance
115135
func NewChannel(ctx Context) Channel {
116136
return internal.NewChannel(ctx)

0 commit comments

Comments
 (0)