Workflow that only responds to signals

Apologies in advance for a very n00b-ish question. I have a use case where I want to create a workflow that only responds to signals and never actually completes. Essentially a “state machine” kind of thing where the workflow begins waiting for signals as soon it’s started and only ever waits for new signals and reacts to them. Here’s a kind of silly example that should illustrate the barrier I’m running into:

type Person struct {
    Name string
}

func PersonNameWorkflow(ctx workflow.Context, person Person) error {
    log := workflow.GetLogger(ctx)

    var name string
    nameChan := workflow.GetSignalChannel(ctx, "name-channel")

    s := workflow.NewSelector(ctx)

    s.AddReceive(nameChan, func(c workflow.ReceiveChannel, more bool) {
        c.Receive(ctx, &name)
        person.Name = name
        log.Info("received signal")
    })

    s.Select(ctx)

    return nil
}

In this case, if a signal is received the workflow proceeds and completes. What I’d like it to do, though, is to simply continue waiting until it’s canceled by a client and make the current state queryable.

Is there a #blessed way to do this?

A simple way is to call PersonNameWorkflow again after receiving signal using workflow.NewContinueAsNewError
Workflows in Go | Temporal documentation

If you are waiting for a single signal type you don’t need the selector. Just call nameChan.Receive in a loop. As @mleow pointed out if you need to process a very large number of signals then it is recommended to call ContinueAsNew periodically (let’s say every 1k signals received) to keep the workflow event history size bounded.

@mleow In this particular case I was kind of hoping to retain the same run ID across signals, so unfortunately this approach doesn’t quite work for me, but I appreciate the help! The NewContinueAsNewError trick is something I always forget but it’s super handy.

@maxim In this case I actually do need to handle multiple signal types (which wasn’t reflected in my trivial example). Is there a convenient way to do that?

I don’t think calling ContinueAsNew after each signal makes sense. It is only needed if a very large number of signals is received.

If you listen on multiple signal types then use Selector to listen. But change your code to call Selector.Select(…) in a loop instead of once.