Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,348 changes: 1,736 additions & 612 deletions openapi/openapiv2.json

Large diffs are not rendered by default.

1,067 changes: 1,054 additions & 13 deletions openapi/openapiv3.yaml

Large diffs are not rendered by default.

119 changes: 115 additions & 4 deletions temporal/api/activity/v1/message.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ option java_outer_classname = "MessageProto";
option ruby_package = "Temporalio::Api::Activity::V1";
option csharp_namespace = "Temporalio.Api.Activity.V1";

import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";

import "temporal/api/common/v1/message.proto";
import "temporal/api/deployment/v1/message.proto";
import "temporal/api/enums/v1/activity.proto";
import "temporal/api/enums/v1/workflow.proto";
import "temporal/api/failure/v1/message.proto";
import "temporal/api/taskqueue/v1/message.proto";

import "google/protobuf/duration.proto";
import "temporal/api/sdk/v1/user_metadata.proto";

message ActivityOptions {
temporal.api.taskqueue.v1.TaskQueue task_queue = 1;
Expand Down Expand Up @@ -40,6 +46,111 @@ message ActivityOptions {
google.protobuf.Duration start_to_close_timeout = 4;
// Maximum permitted time between successful worker heartbeats.
google.protobuf.Duration heartbeat_timeout = 5;

// The retry policy for the activity. Will never exceed `schedule_to_close_timeout`.
temporal.api.common.v1.RetryPolicy retry_policy = 6;
}
}

// Information about a standalone activity.
message ActivityExecutionInfo {
// Unique identifier of this activity within its namespace along with run ID (below).
string activity_id = 1;
string run_id = 2;

// The type of the activity, a string that maps to a registered activity on a worker.
temporal.api.common.v1.ActivityType activity_type = 3;
// A general status for this activity, indicates whether it is currently running or in one of the terminal statuses.
temporal.api.enums.v1.ActivityExecutionStatus status = 4;
// More detailed breakdown of ACTIVITY_EXECUTION_STATUS_RUNNING.
temporal.api.enums.v1.PendingActivityState run_state = 5;
// Details provided in the last recorded activity heartbeat.
temporal.api.common.v1.Payloads heartbeat_details = 6;
// Time the last heartbeat was recorded.
google.protobuf.Timestamp last_heartbeat_time = 7;
// Time the last attempt was started.
google.protobuf.Timestamp last_started_time = 8;
// The attempt this activity is currently on.
// Incremented each time a new attempt is started.
// TODO(dandavison): Confirm if this is on scheduled or started.
int32 attempt = 9;
int32 maximum_attempts = 10;
// Time the activity was originally scheduled via a StartActivityExecution request.
google.protobuf.Timestamp scheduled_time = 11;
// Scheduled time + schedule to close timeout.
google.protobuf.Timestamp expiration_time = 12;
// Failure details from the last failed attempt.
temporal.api.failure.v1.Failure last_failure = 13;
string last_worker_identity = 14;

// Time from the last attempt failure to the next activity retry.
// If the activity is currently running, this represents the next retry interval in case the attempt fails.
// If activity is currently backing off between attempt, this represents the current retry interval.
// If there is no next retry allowed, this field will be null.
// This interval is typically calculated from the specified retry policy, but may be modified if an activity fails
// with a retryable application failure specifying a retry delay.
google.protobuf.Duration current_retry_interval = 15;

// The time when the last activity attempt completed. If activity has not been completed yet, it will be null.
google.protobuf.Timestamp last_attempt_complete_time = 16;

// The time when the next activity attempt will be scheduled.
// If activity is currently scheduled or started, this field will be null.
google.protobuf.Timestamp next_attempt_schedule_time = 17;

// The Worker Deployment Version this activity was dispatched to most recently.
// If nil, the activity has not yet been dispatched or was last dispatched to an unversioned worker.
temporal.api.deployment.v1.WorkerDeploymentVersion last_deployment_version = 18;

// Priority metadata.
temporal.api.common.v1.Priority priority = 19;

// Current activity options. May be different from the one used to start the activity.
temporal.api.activity.v1.ActivityOptions activity_options = 20;

// Incremented each time the activity's state is mutated in persistence.
int64 state_transition_count = 21;

temporal.api.common.v1.SearchAttributes search_attributes = 22;
temporal.api.common.v1.Header header = 23;
// Metadata for use by user interfaces to display the fixed as-of-start summary and details of the activity.
temporal.api.sdk.v1.UserMetadata user_metadata = 24;

// Set if activity cancelation was requested.
string canceled_reason = 25;
}

// Limited activity information returned in the list response.
// When adding fields here, ensure that it is also present in ActivityExecutionInfo (note that it
// may already be present in ActivityExecutionInfo but not at the top-level).
message ActivityExecutionListInfo {
// For standalone activity - a unique identifier of this activity within its namespace along with run ID (below).
string activity_id = 1;
// The run ID of the workflow or standalone activity.
string run_id = 2;
// Workflow that contains this activity - only present for workflow activity.
string workflow_id = 3;

// The type of the activity, a string that maps to a registered activity on a worker.
temporal.api.common.v1.ActivityType activity_type = 4;
// Time the activity was originally scheduled via a StartActivityExecution request.
// TODO: Workflows call this schedule_time but it's scheduled_time in PendingActivityInfo, what should we choose for
// consistency?
google.protobuf.Timestamp scheduled_time = 5;
// If the activity is in a terminal status, this field represents the time the activity transitioned to that status.
google.protobuf.Timestamp close_time = 6;
// Only scheduled and terminal statuses appear here. More detailed information in PendingActivityInfo but not
// available in the list response.
temporal.api.enums.v1.ActivityExecutionStatus status = 7;

// Search attributes from the start request.
temporal.api.common.v1.SearchAttributes search_attributes = 8;

// The task queue this activity was scheduled on when it was originally started, updated on activity options update.
string task_queue = 9;
// Updated on terminal status.
int64 state_transition_count = 10;
// Updated once on scheduled and once on terminal status.
int64 state_size_bytes = 11;
Comment on lines +149 to +152
Copy link
Member

@cretz cretz Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why inconsistently make these as fields instead of search attributes like workflow? Are they also in search attributes, or are we just forbidding the use of this information in queries for activities even though we allow it for workflows? By making them fields, we are ensuring that list and describe field behaviors diverge for the same field which is confusing to have to explain for the same field name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is available for workflows though...

string task_queue = 13;
int64 state_transition_count = 14;
int64 history_size_bytes = 15;

Search attributes returned here are custom ones provided by the user in the start request.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I was confused by the name list info here and not there (I probably won't be the last to be confused by this inconsistency). There are other mismatched fields though, but can bring those up separately.

// The difference between close time and scheduled time.
// This field is only populated if the activity is closed.
google.protobuf.Duration execution_duration = 12;
}
9 changes: 8 additions & 1 deletion temporal/api/common/v1/message.proto
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,17 @@ message Link {
string job_id = 1;
}

// A link to an activity.
message Activity {
string namespace = 1;
string activity_id = 2;
string run_id = 3;
}

oneof variant {
WorkflowEvent workflow_event = 1;
BatchJob batch_job = 2;
Activity activity = 3;
}
}

Expand Down Expand Up @@ -338,4 +346,3 @@ message WorkerSelector {
string worker_instance_key = 1;
}
}

68 changes: 68 additions & 0 deletions temporal/api/enums/v1/activity.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
syntax = "proto3";

package temporal.api.enums.v1;

option go_package = "go.temporal.io/api/enums/v1;enums";
option java_package = "io.temporal.api.enums.v1";
option java_multiple_files = true;
option java_outer_classname = "ActivityProto";
option ruby_package = "Temporalio::Api::Enums::V1";
option csharp_namespace = "Temporalio.Api.Enums.V1";

// Status of a standalone activity.
// The status is updated once, when the activity is originally scheduled, and again when the activity reaches a terminal
// status.
// TODO: Should this be a common execution status? Seems like the other archetypes will share this status.
// (-- api-linter: core::0216::synonyms=disabled
// aip.dev/not-precedent: Named consistently with WorkflowExecutionStatus. --)
enum ActivityExecutionStatus {
ACTIVITY_EXECUTION_STATUS_UNSPECIFIED = 0;
// The activity is not in a terminal status. This does not necessarily mean that there is a currently running
// attempt. The activity may be backing off between attempts or waiting for a worker to pick it up.
ACTIVITY_EXECUTION_STATUS_RUNNING = 1;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will likely add a PAUSED status here since this is the direction workflow pause is going.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think either are a wise direction. What is happening between "running" and "done" are not traditionally execution statuses. Are you going to have a "backing off" status too? Execution status has traditionally been "running or done", and this changes those expectations unnecessarily. Can you use a separate enum to provide more detailed information about the state the running activity/workflow is in and leave the execution status alone?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what we are doing for workflow pause, we should be consistent for any type of execution.

Copy link
Member

@cretz cretz Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I am saying we shouldn't do it for workflow pause either (and have mentioned it in those situations too). It's the same reason that "backing off" doesn't need to be a status. Sure it is effectively paused during backoff, but that doesn't affect that Temporal defines activities and workflows as running if they are not completed.

There are many many cases where the expectations would be violated. For instance, people expect ID reuse policy to only apply to not-running workflows, and ID conflict policy to only apply to running workflows. This violates both of those. People expect ExecutionStatus != Running query to give them all completed workflows. I could come up with many more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd really nice to be able to see paused executions and their counts in the main list view. I don't think "backing off" is the same as pause, we don't update visibility and don't have enough of an incentive to do that for intermediate transient states.

In any case, we shouldn't be having this debate here. Whatever we decide to do for workflows we will copy to activities. For now, we are not even implementing standalone activity pause. That will likely come in the second phase of this project.

Copy link
Member

@cretz cretz Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd really nice to be able to see paused executions and their counts in the main list view

Sure, but that doesn't mean affecting existing fields/concepts like status that have a clear defined expectation and always have

In any case, we shouldn't be having this debate here

👍

// The activity completed successfully.
ACTIVITY_EXECUTION_STATUS_COMPLETED = 2;
// The activity completed with failure.
ACTIVITY_EXECUTION_STATUS_FAILED = 3;
// The activity completed as canceled.
// Requesting to cancel an activity does not automatically transition the activity to canceled status. If the
// activity has a currently running attempt, the activity will only transition to canceled status if the current
// attempt is unsuccessful.
// TODO: Clarify what happens if there are no more allowed retries after the current attempt.
ACTIVITY_EXECUTION_STATUS_CANCELED = 4;
// The activity was terminated. Termination does not reach the worker and the activity code cannot react to it.
// A terminated activity may have a running attempt and will be requested to be canceled by the server when it
// heartbeats.
ACTIVITY_EXECUTION_STATUS_TERMINATED = 5;
// The activity has timed out by reaching the specified shedule-to-start or schedule-to-close timeouts.
// TODO: Clarify if there are other conditions where the activity can end up in timed out status.
ACTIVITY_EXECUTION_STATUS_TIMED_OUT = 6;
}

// Defines whether to allow re-using an activity ID from a previously *closed* activity.
// If the request is denied, the server returns an `ActivityExecutionAlreadyStarted` error.
//
// See `ActivityIdConflictPolicy` for handling ID duplication with a *running* activity.
enum ActivityIdReusePolicy {
ACTIVITY_ID_REUSE_POLICY_UNSPECIFIED = 0;
// Always allow starting an activity using the same activity ID.
ACTIVITY_ID_REUSE_POLICY_ALLOW_DUPLICATE = 1;
// Allow starting an activity using the same ID only when the last activity's final state is one
// of {failed, canceled, terminated, timed out}.
ACTIVITY_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY = 2;
// Do not permit re-use of the ID for this activity. Future start requests could potentially change the policy,
// allowing re-use of the ID.
ACTIVITY_ID_REUSE_POLICY_REJECT_DUPLICATE = 3;
}

// Defines what to do when trying to start an activity with the same ID as a *running* activity.
// Note that it is *never* valid to have two running instances of the same activity ID.
//
// See `ActivityIdReusePolicy` for handling activity ID duplication with a *closed* activity.
enum ActivityIdConflictPolicy {
ACTIVITY_ID_CONFLICT_POLICY_UNSPECIFIED = 0;
// Don't start a new activity; instead return `ActivityExecutionAlreadyStarted` error.
ACTIVITY_ID_CONFLICT_POLICY_FAIL = 1;
// Don't start a new activity; instead return a handle for the running activity.
ACTIVITY_ID_CONFLICT_POLICY_USE_EXISTING = 2;
}
8 changes: 8 additions & 0 deletions temporal/api/errordetails/v1/message.proto
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,11 @@ message MultiOperationExecutionFailure {
repeated google.protobuf.Any details = 3;
}
}

// An error indicating that an activity execution failed to start. Returned when there is an existing activity with the
// given activity ID, and the given ID reuse and conflict policies do not permit starting a new one or attaching to an
// existing one.
message ActivityExecutionAlreadyStartedFailure {
string start_request_id = 1;
string run_id = 2;
}
Loading
Loading