Wait for signal best practices

I am trying to create a workflow that can be described as follows:

  1. Download some file in the local filesystem.
  2. Upload the local file to some other system in order to process it.
  3. Wait for a callback to signal the processing is done with the results, not dependent on any host.
  4. Save the results and trigger some more operations.

I am implementing it using a “local” queue, using the “host dependent queue” pattern from the FileProcessingWorkflow example to make the steps 1 and 2 run on the same host.

In order to wait for the completion signal, I am using the following pattern:

        while (true) {
            Workflow.sleep(LOOP_TIMEOUT);
            if (!Objects.isNull(result)) {
                log.info("result received!");
                break;
            } else {
                log.info("Still waiting...");
            }
        }

where the signal method just assigns the result field of the workflow class.

I am not sure at all the approach is sound, especially in regards to the sleep loop. I am wondering if it would make more sense in regards to temporal best practices to split my workflow in two, one to download and submit the file for processing and once I get my callback, to trigger a second workflow with the processing result as input.

Second question, more for my understanding of temporal: I started by implementing the wait section described above as:

        while (true) {
            if (!Workflow.await(LOOP_TIMEOUT, () -> !Objects.isNull(result))) {
                break;
            }
        }

But then my test was failing with a “possible deadlock detected: workflow thread did not yield control within 1s”. Why is that ?
I was sending the signal using a mock of my activity using the following code:

        willAnswer((Answer<Void>) invocation -> {
            testEnv.registerDelayedCallback(Duration.ofSeconds(2), () -> {
                workflowById.completeProcessing(result);
            });
            return null;
        }).given(hostSpecificActivity).submitForProcessing(any(), any());

Could you explain why changing from await() to sleep() made it work ? If temporal expects the workflow thread to not block, what is its purpose ?

Busy wait with Workflow.sleep is an anti-pattern. Workflow.await is the way to go. I believe the problem in your case caused by while loop spinning indefinitely when condition !Objects.isNull(result)) becomes true. I don’t understand why you need loop at all when using await. I would change it to:

Workflow.await(MAXIMUM_TIME_TO_WAIT_FOR_SIGNAL, () -> !Objects.isNull(result));

That makes sense… Thanks for the help :slight_smile:

I applied this pattern tweaking from the original HelloSignal example, so that’s why.

Regarding that implementation, would you say it is better to split the workflow in smaller bits ? End the first workflow when the file is submitted for processing and another one when I am signaled the end of it ? Would it make any difference in regards to temporal ?

Regarding that implementation, would you say it is better to split the workflow in smaller bits ? End the first workflow when the file is submitted for processing and another one when I am signaled the end of it ? Would it make any difference in regards to temporal ?

See this post.

@maxim

For this approach,

workflow.AwaitWithTimeout(timeout, conditionFunc)

How and when is the condition checked? What is the frequency of rechecking this if it is time based?

It is checked only when workflow processes new events like signals or activity completions. So it is not “busy wait” from the efficiency point of view and can be used anywhere in the workflow code.