Workflow scheduling strategies go-SDK

Workflow scheduling strategies go-SDK

I have a scheduling workflow its task is to wait until a scheduled time before running activities
The question I have is:
After I have started workflow and it’s in a sleep state or waiting on a timer, is there any way to extend the scheduled time for this workflow by sending a new scheduled time using signals?

At this stage of my research, I think my only options for changing a scheduled time in a workflow are

  1. cancel existing workflow and create another one with the new schedule
  2. create a loop which sleeps some increment example per minute, on waking check some schedule value via activity to decide if need to continue to sleep

is there a better way to do this?

  1. cancel existing workflow and create another one with the new schedule

This works, but ugly and has race conditions when multiple such requests are executed in parallel.

  1. create a loop which sleeps some increment example per minute, on waking check some schedule value via activity to decide if need to continue to sleep

Busy wait in workflows is usually an antippatern. If you want to check something periodically do it in an activity. See this post that explains how to do polling. In any case I wouldn’t go with this approach for your use case.

I would recommend a loop that blocks on both a timer and a signal using Selector. If the signal is received the time is recalculated, if the timer fired then activities are executed.

1 Like

@maxim My Manger has told me I have spent too much time resolving this, below is the kind of thing I have tried but it’s not a working solution. could you give me some additional insights?

I think the root problem is that you call s.Select only once. So, when the signal is received it is going to schedule a new timer, but then Select is going to return and continue to the end.

I would rewrite this code to execute Select in a loop until the timer fires.

Here is example code for this scenario.

func SampleTimerWorkflow(ctx workflow.Context, timerDelay time.Duration) error 
{
    logger := workflow.GetLogger(ctx)
    resetCh := workflow.GetSignalChannel(ctx, "reset")

    timerFired := false
    delay := timerDelay
    for ;!timerFired; {
        selector := workflow.NewSelector(ctx)

        logger.Sugar().Infof("Setting up a timer to fire after: %v", delay)
        timerCancelCtx, cancelTimerHandler := workflow.WithCancel(ctx)
        timerFuture := workflow.NewTimer(timerCancelCtx, delay)
        selector.AddFuture(timerFuture, func(f workflow.Future) {
            logger.Info("Timer Fired.")
            timerFired = true
        })

        selector.AddReceive(resetCh, func(c workflow.Channel, more bool) {
            logger.Info("Reset signal received.")
            logger.Info("Cancel outstanding timer.")
            cancelTimerHandler()

            var t int
            c.Receive(ctx, &t)
            logger.Sugar().Infof("Reset delay: %v seconds", t)
            delay = time.Second * time.Duration(t)
        })

        logger.Info("Waiting for timer to fire.")
        selector.Select(ctx)
    }

    workflow.GetLogger(ctx).Info("Workflow completed.")
    return nil
}
1 Like