Asynchronous Activity Completion
How to asynchronously complete an Activity
Asynchronous Activity Completion enables the Activity Function to return without the Activity Execution completing.
There are three steps to follow:
- The Activity provides the external system with identifying information needed to complete the Activity Execution. Identifying information can be a Task Token, or a combination of Namespace, Workflow Id, and Activity Id.
- The Activity Function completes in a way that identifies it as waiting to be completed by an external system.
- The Temporal Client is used to Heartbeat and complete the Activity.
Sometimes Workflows need to perform certain operations in parallel.
Invoking activity stub without the use of yield
will return the Activity result promise which can be resolved at later moment.
Calling yield
on promise blocks until a result is available.
Activity promise also exposes
then
method to construct promise chains. Read more about Promises here.
Alternatively you can explicitly wrap your code (including yield
constucts) using Workflow::async
which will execute nested code in parallel with main Workflow code.
Call yeild
on Promise returned by Workflow::async
to merge execution result back to primary Workflow method.
public function greet(string $name): \Generator
{
// Workflow::async runs it's activities and child workflows in a separate coroutine. Use keyword yield to merge
// it back to parent process.
$first = Workflow::async(
function () use ($name) {
$hello = yield $this->greetingActivity->composeGreeting('Hello', $name);
$bye = yield $this->greetingActivity->composeGreeting('Bye', $name);
return $hello . '; ' . $bye;
}
);
$second = Workflow::async(
function () use ($name) {
$hello = yield $this->greetingActivity->composeGreeting('Hola', $name);
$bye = yield $this->greetingActivity->composeGreeting('Chao', $name);
return $hello . '; ' . $bye;
}
);
// blocks until $first and $second complete
return (yield $first) . "\n" . (yield $second);
}
Async completion
There are certain scenarios when moving on from an Activity upon completion of its function is not possible or desirable. For example, you might have an application that requires user input to complete the Activity. You could implement the Activity with a polling mechanism, but a simpler and less resource-intensive implementation is to asynchronously complete a Temporal Activity.
There are two parts to implementing an asynchronously completed Activity:
- The Activity provides the information necessary for completion from an external system and notifies the Temporal service that it is waiting for that outside callback.
- The external service calls the Temporal service to complete the Activity.
The following example demonstrates the first part:
app/src/AsyncActivityCompletion/GreetingActivity.php
class GreetingActivity implements GreetingActivityInterface
{
private LoggerInterface $logger;
public function __construct()
{
$this->logger = new Logger();
}
/**
* Demonstrates how to implement an Activity asynchronously.
* When {@link Activity::doNotCompleteOnReturn()} is called,
* the Activity implementation function that returns doesn't complete the Activity.
*/
public function composeGreeting(string $greeting, string $name): string
{
// In real life this request can be executed anywhere. By a separate service for example.
$this->logger->info(sprintf('GreetingActivity token: %s', base64_encode(Activity::getInfo()->taskToken)));
// Send the taskToken to the external service that will complete the Activity.
// Return from the Activity a function indicating that Temporal should wait
// for an async completion message.
Activity::doNotCompleteOnReturn();
// When doNotCompleteOnReturn() is invoked the return value is ignored.
return 'ignored';
}
}
The following code demonstrates how to complete the Activity successfully using WorkflowClient
:
app/src/AsyncActivityCompletion/CompleteCommand.php
$client = $this->workflowClient->newActivityCompletionClient();
// Complete the Activity.
$client->completeByToken(
base64_decode($input->getArgument('token')),
$input->getArgument('message')
);
To fail the Activity, you would do the following:
// Fail the Activity.
$activityClient->completeExceptionallyByToken($taskToken, new \Error("activity failed"));