Signals and retries

Hello Temporal team

I have a payment workflow with the following two steps:

  1. Make the payment request
  2. Validate the payment

For step 1: we just need to make a request to the payment provider API and this will return back the withdrawal ID and the status processing.

For step 2: there are two options to validate the payment is completed:
Option A: A webhook is sent back to our app by the payment provider.
Option B: A request send from our app to the payment Provider API with the withdrawal ID.

On my workflow, I want to use both methods to validate the transaction, whatever comes first. For option A, when the webhook arrives, we want to signal the workflow to continue to the next step. However, for option B, we can get an activity that retries indefinitely until the API returns that the withdrawal is completed.

I haven’t been to combine both strategies in my workflow at the same time, whatever comes first. All the tutorials I have seen, they show either just signals or just retry an activity.

Any ideas of how to tackle this one, here would be the desire result, the workflow will retry an activity indefinitely, sending API queries to the withdrawal-status end-point. However, if a signal is received then the workflow stop retrying the activity and move the next step right away.

I found a way to this by passing a signal variable to the activity:

yield $this->activity->validateWithdrawal($this->withdrawalValidated);

And the activity looks this:

public function validateWithdrawal($signal): bool
    {
        if ($signal || $queryToExternalAPI == true) {
            return true;
        }

        throw new WithdrawalValidationRetryException("No Withdrawal");
    }

In this case, the activity will retry every 30 seconds and query the api each time. However, the problem with this strategy is that the workflow will have to wait 30 seconds to move to the next step, even if we send the signal the workflow will still have to wait to the next iteration to move to the next step.

The ideal scenario is that the workflow will move right away to the next step as soon the signal was sent. However, if there is not signal, the workflow should keel querying the API every 30 seconds.

Thanks

You could start your polling activity in cancelation scope and when your signal comes in
can cancel the activity, handle activity failure and then continue execution.

Polling samples here

So maybe something like:
Workflow method:

 MyPollingResultActivity activity = Workflow.newActivityStub(MyPollingResultActivity.class, options);
  scope = Workflow.newCancellationScope(() -> result = activity.doPoll());
  try {
    scope.run();
  } catch (ActivityFailure e) {
    if (e.getCause() instanceof CanceledFailure) {
      // here you know scope been canceled by your signal coming in
    }
  }

signal handler

@Override
public void stopPolling() {
  scope.cancel();
  // do whatever else needed here
}

if your activity heartbeats it would receive cancelation on heartbeat and you can ignore the cancelation if needed or do some cleanup if and then confirm it.
If does not you could set in activity options the cancellation type to ActivityCancellationType.TRY_CANCEL