Heartbeating continues after Cancellation is issued
Even after a Cancellation has been issued, an Activity can continue running and heartbeating. When calling activity.RecordHeartbeat after Cancellation has occurred, a WARN log message 'RecordActivityHeartbeat with error Error context canceled' will be logged and a context canceled error will be returned from the call. However, the Heartbeat has still been sent to the server.
WaitForCancellation Activity Option
The ActivityOptions struct includes a WaitForCancellation field. When set to true in activity options, the activity will wait for and respond to cancellation requests before completing execution.
Handle Cancellation in Activity with heartbeating and context.Done()
Activities can listen for cancellation requests by heartbeating and checking if the context is done. Use a for-select loop to periodically call activity.RecordHeartbeat(ctx, ...) and listen for ctx.Done() signal. When ctx.Done() fires, the Activity has been cancelled and should perform cleanup or return early.
Get Activity execution context in Java
Get the activity execution context by calling Activity.getExecutionContext(). This returns an ActivityExecutionContext object that provides access to the task token and other execution metadata.
Example: Start a Standalone Activity and wait later
ActivityHandle<String> handle = client.start(
GreetingActivities.class,
GreetingActivities::composeGreeting,
options,
"Hello",
"World");
System.out.println("Started activity ID: " + ACTIVITY_ID);
// Wait for the result later
String result = handle.getResult();
System.out.println("Activity result: " + result);
Example: Execute a Standalone Activity by string type name
String result = client.execute("ComposeGreeting", String.class, options, "Hello", "World");
Example: Get handle to existing Standalone Activity
ActivityHandle<String> handle = client.getHandle("standalone-activity-id", null, String.class);
handle.getResult() and handle.getResultAsync() for Standalone Activities
Calling client.execute() is equivalent to calling client.start() to durably enqueue the Standalone Activity, and then calling handle.getResult() to block until the Activity completes and return the result. To wait asynchronously without blocking the calling thread, use handle.getResultAsync(), which returns a CompletableFuture<R>.
Example: Wait for Standalone Activity result asynchronously
CompletableFuture<String> future = handle.getResultAsync();
ActivityClient.listExecutions() to list Standalone Activities
Use client.listExecutions() to list Standalone Activity Executions that match a List Filter query. The result is a Stream<ActivityExecutionMetadata> that fetches pages from the server on demand as the stream is consumed. These APIs return only Standalone Activity Executions. Activities running inside Workflows are not included.
Example: List Standalone Activities
client.listExecutions("TaskQueue = '" + TASK_QUEUE + "'")
.forEach(
info ->
System.out.printf(
"ActivityID: %s, Type: %s, Status: %s%n",
info.getActivityId(), info.getActivityType(), info.getStatus()));
Example: Count Standalone Activities
ActivityExecutionCount resp = client.countExecutions("TaskQueue = '" + TASK_QUEUE + "'");
System.out.println("Total activities: " + resp.getCount());
resp.getGroups()
.forEach(
group ->
System.out.println("Group " + group.getGroupValues() + ": " + group.getCount()));
Example: Execute a Standalone Activity with typed API
ActivityClient client = ActivityClient.newInstance(service, ActivityClientOptions.newBuilder().setNamespace(profile.getNamespace()).build());
StartActivityOptions options = StartActivityOptions.newBuilder()
.setId(ACTIVITY_ID)
.setTaskQueue(TASK_QUEUE)
.setStartToCloseTimeout(Duration.ofSeconds(10))
.build();
String result = client.execute(
GreetingActivities.class,
GreetingActivities::composeGreeting,
options,
"Hello",
"World");
System.out.println("Activity result: " + result);
StartActivityOptions required fields
StartActivityOptions requires id, taskQueue, and at least one of startToCloseTimeout or scheduleToCloseTimeout.
ActivityClient.execute() for Standalone Activities
Use ActivityClient.execute() to execute a Standalone Activity and block until it completes. Call this from your application code, not from inside a Workflow Definition. This durably enqueues your Standalone Activity in the Temporal Server, waits for it to be executed on your Worker, and then returns the typed result. The typed execute() API takes the Activity interface class and an unbound method reference. You can also call Activities by string type name.
ActivityOptions reference table
ActivityOptions available for Activity invocation:
| Option | Required | Type |
|--------|----------|------|
| setScheduleToCloseTimeout | Yes (if StartToCloseTimeout is not specified) | Duration |
| setScheduleToStartTimeout | No | Duration |
| setStartToCloseTimeout | Yes (if ScheduleToCloseTimeout is not specified) | Duration |
| setHeartbeatTimeout | No | Duration |
| setTaskQueue | No | String |
| setRetryOptions | No | RetryOptions |
| setCancellationType | No | ActivityCancellationType |
ActivityOptions can be set using an ActivityStub within a Workflow implementation, or per-Activity using WorkflowImplementationOptions within a Worker. If options are defined per-Activity Type with WorkflowImplementationOptions.setActivityOptions(), setting them again specifically with ActivityStub in a Workflow will override this setting.
Synchronous activity invocation example
Example of synchronous Activity invocation in a Workflow:
```java
public class FileProcessingWorkflowImpl implements FileProcessingWorkflow {
private final FileProcessingActivities activities;
public FileProcessingWorkflowImpl() {
this.activities = Workflow.newActivityStub(
FileProcessingActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofHours(1))
.build());
}
@Override
public void processFile(Arguments args) {
String localName = null;
String processedName = null;
try {
localName = activities.download(args.getSourceBucketName(), args.getSourceFilename());
processedName = activities.processFile(localName);
activities.upload(args.getTargetBucketName(), args.getTargetFilename(), processedName);
} finally {
if (localName != null) {
activities.deleteLocalFile(localName);
}
if (processedName != null) {
activities.deleteLocalFile(processedName);
}
}
}
}
```
Multiple activity stubs with different options
A Workflow can have multiple Activity stubs. Each Activity stub can have its own ActivityOptions defined. Example:
```java
public FileProcessingWorkflowImpl() {
ActivityOptions options1 = ActivityOptions.newBuilder()
.setTaskQueue("taskQueue1")
.setStartToCloseTimeout(Duration.ofMinutes(10))
.build();
this.store1 = Workflow.newActivityStub(FileProcessingActivities.class, options1);
ActivityOptions options2 = ActivityOptions.newBuilder()
.setTaskQueue("taskQueue2")
.setStartToCloseTimeout(Duration.ofMinutes(5))
.build();
this.store2 = Workflow.newActivityStub(FileProcessingActivities.class, options2);
}
```
Untyped activity stub for unknown types
Use Workflow.newUntypedActivityStub to invoke Activities without referencing the interface it implements. This is useful when the Activity type is not known at compile time, or to invoke Activities implemented in different programming languages.
Example:
```java
ActivityOptions activityOptions =
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(3))
.setTaskQueue("simple-queue-node")
.build();
ActivityStub activity = Workflow.newUntypedActivityStub(activityOptions);
activity.execute("ComposeGreeting", String.class, "Hello World", "Spanish");
```
ActivityExecutionContext provides workflow invocation information
ActivityExecutionContext is a context object passed to each Activity implementation by default. Access it via Activity.getExecutionContext() within the Activity thread. It provides getters to access information about the Workflow that invoked the Activity. The Activity context information is stored in a thread-local variable, so calls to getExecutionContext() succeed only within the thread that invoked the Activity function.
ActivityExecutionContext usage example
Example of accessing ActivityExecutionContext:
```java
public class FileProcessingActivitiesImpl implements FileProcessingActivities {
@Override
public String download(String bucketName, String remoteName, String localName) {
ActivityExecutionContext ctx = Activity.getExecutionContext();
ActivityInfo info = ctx.getInfo();
log.info("namespace=" + info.getActivityNamespace());
log.info("workflowId=" + info.getWorkflowId());
log.info("runId=" + info.getRunId());
log.info("activityId=" + info.getActivityId());
log.info("activityTimeout=" + info.getStartToCloseTimeout());
return downloadFileFromS3(bucketName, remoteName, localDirectory + localName);
}
}
```
StartToCloseTimeout configuration
StartToCloseTimeout is set using ActivityOptions.newBuilder.setStartToCloseTimeout(). This or ScheduleToCloseTimeout must be set. Type: Duration. Default: Defaults to ScheduleToCloseTimeout value.
With ActivityStub:
```java
GreetingActivities activities = Workflow.newActivityStub(GreetingActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(2))
.build());
```
With WorkflowImplementationOptions:
```java
WorkflowImplementationOptions options =
WorkflowImplementationOptions.newBuilder()
.setActivityOptions(
ImmutableMap.of(
"EmailCustomerGreeting",
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(2))
.build()))
.build();
```
HeartbeatTimeout configuration
HeartbeatTimeout is set using ActivityOptions.newBuilder.setHeartbeatTimeout(). Type: Duration. Default: None.
With ActivityStub:
```java
private final GreetingActivities activities =
Workflow.newActivityStub(
GreetingActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(5))
.setHeartbeatTimeout(Duration.ofSeconds(2))
.build());
```
With WorkflowImplementationOptions:
```java
WorkflowImplementationOptions options =
WorkflowImplementationOptions.newBuilder()
.setActivityOptions(
ImmutableMap.of(
"EmailCustomerGreeting",
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(5))
.setHeartbeatTimeout(Duration.ofSeconds(2))
.build()))
.build();
```
TaskQueue option configuration
TaskQueue is set using ActivityOptions.newBuilder.setTaskQueue(). Type: String. Default: Defaults to the Task Queue that the Workflow was started with.
With ActivityStub:
```java
GreetingActivities activities = Workflow.newActivityStub(GreetingActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(5))
.setTaskQueue("yourTaskQueue")
.build());
```
With WorkflowImplementationOptions:
```java
WorkflowImplementationOptions options =
WorkflowImplementationOptions.newBuilder()
.setActivityOptions(
ImmutableMap.of(
"EmailCustomerGreeting",
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(5))
.setTaskQueue("yourTaskQueue")
.build()))
.build();
```
RetryOptions configuration
RetryOptions is set using ActivityOptions.newBuilder.setRetryOptions(). Type: RetryOptions. Default: Server-defined Activity Retry policy.
With ActivityStub:
```java
private final ActivityOptions options =
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(5))
.setRetryOptions(
RetryOptions.newBuilder()
.setInitialInterval(Duration.ofSeconds(1))
.setMaximumInterval(Duration.ofSeconds(10))
.build())
.build();
```
With WorkflowImplementationOptions:
```java
WorkflowImplementationOptions options =
WorkflowImplementationOptions.newBuilder()
.setActivityOptions(
ImmutableMap.of(
"EmailCustomerGreeting",
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(5))
.setRetryOptions(
RetryOptions.newBuilder()
.setDoNotRetry(NullPointerException.class.getName())
.build())
.build()))
.build();
```
setCancellationType configuration
setCancellationType is set using ActivityOptions.newBuilder.setCancellationType(). Type: ActivityCancellationType. Default: ActivityCancellationType.TRY_CANCEL.
With ActivityStub:
```java
private final GreetingActivities activities =
Workflow.newActivityStub(
GreetingActivities.class,
ActivityOptions.newBuilder()
.setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)
.build());
```
With WorkflowImplementationOptions:
```java
WorkflowImplementationOptions options =
WorkflowImplementationOptions.newBuilder()
.setActivityOptions(
ImmutableMap.of(
"EmailCustomerGreeting",
ActivityOptions.newBuilder()
.setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)
.build()))
.build();
```
Setting Heartbeat Timeout with ActivityStub example
Example of setting Heartbeat Timeout with ActivityStub: GreetingActivities activities = Workflow.newActivityStub(GreetingActivities.class, ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).setHeartbeatTimeout(Duration.ofSeconds(2)).build());
Heartbeat Timeout configuration
To set a Heartbeat Timeout, use ActivityOptions.newBuilder().setHeartbeatTimeout() with a Duration parameter. Type: Duration. Default: None. Heartbeat Timeout works in conjunction with Activity Heartbeats to detect stalled Activity Executions.
Setting Activity Timeouts with WorkflowImplementationOptions
Activity Timeouts can be set per-Activity using WorkflowImplementationOptions within a Worker. Example: WorkflowImplementationOptions options = WorkflowImplementationOptions.newBuilder().setActivityOptions(ImmutableMap.of("GetCustomerGreeting", ActivityOptions.newBuilder().setScheduleToCloseTimeout(Duration.ofSeconds(5)).build())).build();
ActivityStub overrides WorkflowImplementationOptions timeout settings
If you define Activity Options per-Activity Type using WorkflowImplementationOptions.setActivityOptions(), setting them again specifically with ActivityStub in a Workflow will override the WorkflowImplementationOptions setting.
Extract heartbeat details on Activity retry
In the case of Activity retries, the last Heartbeat details are available and can be extracted from the last failed attempt by using Activity.getExecutionContext().getHeartbeatDetails(Class<V> detailsClass).
ActivityOptions.Builder timeout configuration fields
The ActivityOptions.Builder class provides three timeout configuration methods: setScheduleToCloseTimeout, setStartToCloseTimeout, and setScheduleToStartTimeout. Each takes a Duration parameter.
Setting Activity Timeouts with ActivityStub
Activity Timeouts can be set using an ActivityStub within a Workflow implementation. Example: GreetingActivities activities = Workflow.newActivityStub(GreetingActivities.class, ActivityOptions.newBuilder().setScheduleToCloseTimeout(Duration.ofSeconds(5)).build());
Setting Heartbeat Timeout with WorkflowImplementationOptions example
Example of setting Heartbeat Timeout per-Activity with WorkflowImplementationOptions: WorkflowImplementationOptions options = WorkflowImplementationOptions.newBuilder().setActivityOptions(ImmutableMap.of("EmailCustomerGreeting", ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).setHeartbeatTimeout(Duration.ofSeconds(2)).build())).build();
Heartbeat an Activity Execution in Java
To Heartbeat an Activity Execution in Java, use the Activity.getExecutionContext().heartbeat() class method. Example: public class YourActivityDefinitionImpl implements YourActivityDefinition { @Override public String yourActivityMethod(YourActivityMethodParam param) { Activity.getExecutionContext().heartbeat(details); } }
Heartbeat method accepts optional details parameter
The Activity.getExecutionContext().heartbeat() method takes an optional argument representing the latest progress of the Activity Execution. This method can accept a variety of types such as an exception object, custom object, or string.
Activity timeout configuration
Configure activity timeout using ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build() to specify the maximum duration for activity execution.
Cancel an Activity using CancellationScope.cancel()
To cancel an Activity from a Workflow Execution, call the cancel() method on the CancellationScope that the activity was started in.
Example: Cancel activities within a CancellationScope
Example showing how to create a CancellationScope, run multiple activities asynchronously within it, wait for one to complete, then cancel all uncompleted activities:
public class GreetingWorkflowImpl implements GreetingWorkflow {
@Override
public String getGreeting(String name) {
List<Promise<String>> results = new ArrayList<>(greetings.length);
CancellationScope scope =
Workflow.newCancellationScope(
() -> {
for (String greeting : greetings) {
results.add(Async.function(activities::composeGreeting, greeting, name));
}
});
scope.run();
String result = Promise.anyOf(results).get();
scope.cancel();
for (Promise<String> activityResult : results) {
try {
activityResult.get();
} catch (ActivityFailure e) {
if (!(e.getCause() instanceof CanceledFailure)) {
throw e;
}
}
}
return result;
}
}
Activity promise then method for promise chains
Activity promise exposes a then method to construct promise chains, enabling fluent asynchronous handling of Activity results.
Activity promise return without yield for asynchronous invocation
Invoking an activity stub without the use of yield will return the Activity result promise which can be resolved at a later moment. Calling yield on a promise blocks until a result is available.
S3 file processing activities implementation example
Example of Activity implementation that uploads, downloads, processes, and deletes files:
```php
class FileProcessingActivitiesImpl implements FileProcessingActivities {
private S3Client $s3Client;
private string $localDirectory;
public function __construct(S3Client $s3Client, string $localDirectory) {
$this->s3Client = $s3Client;
$this->localDirectory = $localDirectory;
}
public function upload(string $bucketName, string $localName, string $targetName): void
{
$this->s3Client->putObject(
$bucketName,
$targetName,
fopen($this->localDirectory . $localName, 'rb+')
);
}
public function download(
string $bucketName,
string $remoteName,
string $localName
): void
{
$this->s3Client->downloadObject(
$bucketName,
$remoteName,
fopen($this->localDirectory .$localName, 'wb+')
);
}
public function processFile(string $localName): string
{
return compressFile($this->localDirectory . $localName);
}
public function deleteLocalFile(string $fileName): void
{
unlink($this->localDirectory . $fileName);
}
}
```
Activity stub creation with timeout example
Example of creating an Activity stub with a Start-To-Close timeout:
```php
class GreetingWorkflow implements GreetingWorkflowInterface
{
private $greetingActivity;
public function __construct()
{
$this->greetingActivity = Workflow::newActivityStub(
GreetingActivityInterface::class,
ActivityOptions::new()->withStartToCloseTimeout(\DateInterval::createFromDateString('30 seconds'))
);
}
public function greet(string $name): \Generator
{
return yield $this->greetingActivity->composeGreeting('Hello', $name);
}
}
```
Multiple activity stubs with different options
If different Activities need different options, like timeouts or a task queue, multiple client-side stubs can be created with different options using separate Workflow::newActivityStub calls.
Activity invocation via yield blocks until completion
Calling a method on an Activity client-side stub via yield invokes an Activity that implements this method. An Activity invocation synchronously blocks until the Activity completes, fails, or times out. Even if Activity Execution takes months, the Workflow code sees it as a single synchronous invocation.
Workflow::newActivityStub creates activity client-side stub
Workflow::newActivityStub returns a client-side stub that implements an Activity interface. It takes the Activity's type and ActivityOptions as arguments. The client-side stub can be used within the Workflow code.
Schedule-To-Close Timeout definition
The Schedule-To-Close Timeout is the maximum amount of time allowed for the overall Activity Execution.
Activity timeout methods in PHP SDK
The available methods to set timeouts in PHP SDK are: withScheduleToCloseTimeout(), withStartToCloseTimeout(), and withScheduleToStartTimeout().
Activity stub setup with timeout in PHP
Activity stubs are created using Workflow::newActivityStub() with ActivityOptions::new() to configure timeouts. Use withScheduleToCloseTimeout(CarbonInterval::seconds(n)) to set the Schedule-To-Close Timeout duration.
Start-To-Close Timeout definition
The Start-To-Close Timeout is the maximum time allowed for a single Activity Task Execution.
Schedule-To-Start Timeout definition
The Schedule-To-Start Timeout is the maximum amount of time that is allowed from when an Activity Task is scheduled to when a Worker starts that Activity Task. This timeout is non-retryable by design.
Activity Execution requires timeout configuration
An Activity Execution must have either the Start-To-Close or the Schedule-To-Close Timeout set.
Heartbeat Timeout behavior
If the Temporal Service does not receive a Heartbeat within a Heartbeat Timeout time period, the Activity will be considered failed and another Activity Task Execution may be scheduled according to the Retry Policy.
Heartbeat details for progress tracking
Heartbeats can contain a details field describing the Activity's current progress. If an Activity gets retried, the Activity can access the details from the last Heartbeat that was sent to the Temporal Service.
Activity Heartbeat PHP method
Call Activity::heartbeat() to send a heartbeat from within an Activity. You can pass a details parameter to track progress information.
Activity Heartbeat details timeout behavior
If an Activity times out, the last value of details from the heartbeat is included in the TimeoutFailure delivered to a Workflow. The Workflow can then pass these details to the next Activity invocation.
Get Heartbeat details from Activity
Call Activity::getHeartbeatDetails() from within an Activity to access the details from the last successful Heartbeat. This is useful when an Activity is retried after a failure.
Activity Heartbeat example with progress callback
Example of Activity Heartbeat usage in PHP:
```php
use Temporal\Activity;
class FileProcessingActivitiesImpl implements FileProcessingActivities
{
public function download(
string $bucketName,
string $remoteName,
string $localName
): void
{
$this->dowloader->downloadWithProgress(
$bucketName,
$remoteName,
$localName,
function ($progress) {
Activity::heartbeat($progress);
}
);
Activity::heartbeat(100); // download complete
}
}
```
This shows how to send heartbeat details during a download operation.
Heartbeat throttling behavior
Heartbeats may not always be sent to the Temporal Service—they may be throttled by the Worker.
Activity execution with timeout
When creating an Activity stub, use ActivityOptions::new()->withStartToCloseTimeout(5) to set a timeout in seconds for the Activity to complete from start to close.