How to log every `progress` signal until a `done` signal is received

Hi everyone.

My workflow receives 3 signals: progress, done and error. These signals are called by a Webhook.

I want to continuously call an activity on every progress signal received, until I receive either a done or error signal. Something like this:

export const createStockAvatar =
  async (args) => {
    let status = null;

    setHandler(createStockAvatarProgressSignal, (args) => {
      // This signal is called multiple times - 1%, 5%, 15%, etc etc.
      // Activity is considered a side-effect, and I can't have side-effect inside handlers.
      // How to call this?
      await activitySendProgressEventToPubSub();
    });

    setHandler(createStockAvatarFinishedSignal, (response) => {
      status = 'done';
    });

    setHandler(createStockAvatarErrorSignal, (response) => {
      status = 'error';
    });

    await condition(() => status === 'done' || status === 'error');

    if (status === 'done') {
      logger.info('Success');
    } else if (status === 'done') {
      logger.info('Error');
    }

    return {};
  };

What’s the right way to continuously call the activity on every progress received, until the process is done?

TypeScript SDK.

Thanks in advance.

So, I ended up doing like this.

export const createStockAvatar =
  async (args) => {
    let status = null;
    let progress = 0;
    let lastTrackedProgress = 0;

    setHandler(createStockAvatarProgressSignal, (args) => {
      progress = args.progress;
    });

    setHandler(createStockAvatarFinishedSignal, (response) => {
      status = 'done';
    });

    setHandler(createStockAvatarErrorSignal, (response) => {
      status = 'error';
    });

    while (status === null) {
      if (progress > lastTrackedProgress) {
        await activitySendProgressEventToPubSub();
        lastTrackedProgress = progress;
      }
      
      await sleep(100);
    }

    if (status === 'done') {
      logger.info('Success');
    } else if (status === 'done') {
      logger.info('Error');
    }

    return {};
  };

Seems to work. Still, I’m curious to see if there’s a better alternative based on Temporal’s best practices.

Not sure why you couldnt use workflow logger to log from signal handler?
See this sample for reference.

1 Like

Sorry @tihomir - I had a bad example.

In fact, I didn’t want to just log, but also call an async activity that sends the event to my Pub/Sub. I’ve updated my posts above to reflect that.

you should be able to schedule activity inside signal handlers, you cannot inside query handlers. just make it async, so something like:

  setHandler(mySignal, async () => {
    await doSomeActivityWork(input);
  })

you should be able to schedule activity inside signal handlers, you cannot inside query handlers. just make it async, so something like:

+1 to Tiho’s suggestion.

Note however that your Workflow will complete as soon as your Workflow’s main function returns; i.e. pending async signal handlers will not prevent your Workflow from completing, and therefore, it’s possible that your activity would actually never execute.

To avoid that, you may add the following line to the end of your Workflow’s main function:

import * as wf from '@temporalio/workflow';

...

await wf.condition(wf.allHandlersFinished);

See this sample for a practical example of this.

2 Likes

Thanks both! For some reason I thought we couldn’t have async handlers or any async call inside a handler, as if they were considered as side-effects.

It works, and if it’s a good practice, I’m happy with it. Makes my implementation even easier.