From 9109f60c3687976d267128f98adfb798fd24e97c Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Mon, 14 Sep 2026 15:58:31 -0700 Subject: [PATCH 01/30] Added the stream messages, commands, events, and task carriage. --- temporal/api/command/v1/message.proto | 27 ++++++++++ temporal/api/enums/v1/command_type.proto | 2 + temporal/api/enums/v1/event_type.proto | 8 +++ temporal/api/history/v1/message.proto | 36 +++++++++++++ temporal/api/stream/v1/message.proto | 51 +++++++++++++++++++ .../workflowservice/v1/request_response.proto | 5 ++ 6 files changed, 129 insertions(+) create mode 100644 temporal/api/stream/v1/message.proto diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index ee839115b..311b918e4 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -14,6 +14,7 @@ import "google/protobuf/duration.proto"; import "temporal/api/enums/v1/workflow.proto"; import "temporal/api/enums/v1/command_type.proto"; import "temporal/api/common/v1/message.proto"; +import "temporal/api/stream/v1/message.proto"; import "temporal/api/failure/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; import "temporal/api/workflow/v1/message.proto"; @@ -324,5 +325,31 @@ message Command { ScheduleNexusOperationCommandAttributes schedule_nexus_operation_command_attributes = 18; RequestCancelNexusOperationCommandAttributes request_cancel_nexus_operation_command_attributes = 19; + AddStreamMessagesCommandAttributes add_stream_messages_command_attributes = 20; + SubscribeStreamCommandAttributes subscribe_stream_command_attributes = 21; } } + +// Appends to a stream the Workflow owns. Applied inside the Workflow Task's own +// commit, so it emits no History Event and does not schedule further work. +message AddStreamMessagesCommandAttributes { + // Empty means the Workflow's default output stream. + string stream_id = 1; + repeated temporal.api.stream.v1.StreamMessage messages = 2; +} + +// Subscribe this Workflow to a stream, so later Workflow Tasks carry the ranges +// it has not consumed yet. +// +// The stream's addressing is resolved by the server rather than supplied here. +// A Workflow cannot look it up without doing I/O, and a value it carried would +// be a reading rather than a fact, so it could differ on replay. +message SubscribeStreamCommandAttributes { + // Stream to consume. A stream in another execution is addressed by its id; + // one this Workflow owns is addressed by the name it was published under. + string stream_id = 1; + // Where to start. Negative means from wherever the stream is when the + // subscription is registered, which the server resolves and records so + // replay does not resolve it again. + int64 start_offset = 2; +} diff --git a/temporal/api/enums/v1/command_type.proto b/temporal/api/enums/v1/command_type.proto index 067d95391..677ed3b2d 100644 --- a/temporal/api/enums/v1/command_type.proto +++ b/temporal/api/enums/v1/command_type.proto @@ -29,4 +29,6 @@ enum CommandType { COMMAND_TYPE_MODIFY_WORKFLOW_PROPERTIES = 16; COMMAND_TYPE_SCHEDULE_NEXUS_OPERATION = 17; COMMAND_TYPE_REQUEST_CANCEL_NEXUS_OPERATION = 18; + COMMAND_TYPE_ADD_STREAM_MESSAGES = 19; + COMMAND_TYPE_SUBSCRIBE_STREAM = 20; } diff --git a/temporal/api/enums/v1/event_type.proto b/temporal/api/enums/v1/event_type.proto index b879f51e8..b515acb1d 100644 --- a/temporal/api/enums/v1/event_type.proto +++ b/temporal/api/enums/v1/event_type.proto @@ -175,4 +175,12 @@ enum EventType { EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED = 59; // An event that indicates time skipping advanced time or was disabled automatically after a bound was reached. EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED = 60; + // A Workflow subscribed to a stream. Recorded once per subscription, not + // per message: the offsets a task consumed ride WorkflowTaskCompleted and + // the payloads never enter History at all. + EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED = 61; + // A Workflow published a batch of messages to a stream. Recorded per + // batch, and carrying only the offset range it landed at: the bodies go to + // the stream's own log, never into History. + EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED = 62; } diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index b40324d68..089dccf78 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -17,6 +17,7 @@ import "temporal/api/enums/v1/failed_cause.proto"; import "temporal/api/enums/v1/update.proto"; import "temporal/api/enums/v1/workflow.proto"; import "temporal/api/common/v1/message.proto"; +import "temporal/api/stream/v1/message.proto"; import "temporal/api/deployment/v1/message.proto"; import "temporal/api/failure/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; @@ -369,6 +370,12 @@ message WorkflowTaskCompletedEventAttributes { // execution. UNSPECIFIED means the task was completed by an unversioned worker. This value // updates workflow execution's `versioning_info.behavior`. temporal.api.enums.v1.VersioningBehavior versioning_behavior = 8; + + // Offset ranges this Workflow Task consumed from streams it subscribes to. + // Recorded on every task where a subscription is active, including when it + // observed nothing: an empty range is a fact replay must reproduce, and + // omitting it would let replay deliver messages the Workflow did not have. + repeated temporal.api.stream.v1.StreamCursor stream_cursors = 20; // The Worker Deployment Version that completed this task. Must be set if `versioning_behavior` // is set. This value updates workflow execution's `versioning_info.version`. // Deprecated. Replaced with `deployment_version`. @@ -955,6 +962,33 @@ message ActivityPropertiesModifiedExternallyEventAttributes { temporal.api.common.v1.RetryPolicy new_retry_policy = 2; } +message WorkflowStreamSubscribedEventAttributes { + // The WorkflowTaskCompleted event of the task whose command created this + // subscription. + int64 workflow_task_completed_event_id = 1; + // Stream the Workflow subscribed to. + string stream_id = 2; + // The offset the subscription actually starts from. Resolved by the server + // when the subscription is registered and recorded here, so replay reads + // the resolved value rather than resolving it again against a stream that + // has since moved. + int64 start_offset = 3; +} + +message WorkflowStreamMessagesAddedEventAttributes { + // The WorkflowTaskCompleted event of the task whose command published this + // batch. + int64 workflow_task_completed_event_id = 1; + // Stream the Workflow published to. + string stream_id = 2; + // Offset the first message of the batch landed at. + int64 first_offset = 3; + // How many messages the batch held. With first_offset this names the range + // without carrying any of it, which is what keeps this event a fixed size + // no matter how large the batch or its payloads are. + int64 message_count = 4; +} + message WorkflowExecutionUpdateAcceptedEventAttributes { // The instance ID of the update protocol that generated this event. string protocol_instance_id = 1; @@ -1278,6 +1312,8 @@ message HistoryEvent { WorkflowExecutionPausedEventAttributes workflow_execution_paused_event_attributes = 63; WorkflowExecutionUnpausedEventAttributes workflow_execution_unpaused_event_attributes = 64; WorkflowExecutionTimeSkippingTransitionedEventAttributes workflow_execution_time_skipping_transitioned_event_attributes = 65; + WorkflowStreamSubscribedEventAttributes workflow_stream_subscribed_event_attributes = 66; + WorkflowStreamMessagesAddedEventAttributes workflow_stream_messages_added_event_attributes = 67; } } diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto new file mode 100644 index 000000000..a1702e3c7 --- /dev/null +++ b/temporal/api/stream/v1/message.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package temporal.api.stream.v1; + +option go_package = "go.temporal.io/api/stream/v1;stream"; +option java_package = "io.temporal.api.stream.v1"; +option java_multiple_files = true; +option java_outer_classname = "MessageProto"; +option ruby_package = "Temporalio::Api::Stream::V1"; +option csharp_namespace = "Temporalio.Api.Stream.V1"; + +import "temporal/api/common/v1/message.proto"; + +message StreamMessage { + temporal.api.common.v1.Payload body = 1; + map metadata = 2; + string topic = 3; + int64 topic_sequence = 4; +} + +// A contiguous range of a stream delivered to a Workflow Task, along with the +// offsets it covers. The offsets are what History records; the messages +// themselves are never written to History. +message StreamSlice { + string stream_id = 1; + string run_id = 2; + // Inclusive. + int64 from_offset = 3; + // Exclusive. Equal to from_offset when the subscription observed nothing, + // which is a fact replay has to reproduce rather than an absence of one. + int64 to_offset = 4; + repeated StreamMessage messages = 5; + // The WorkflowTaskCompleted event whose stream_cursors recorded this range. + // Set only when the server is re-supplying a range for a task being + // replayed; a slice for the task now being started leaves it unset, because + // the event closing that task does not exist yet. + // + // Replay needs this because a Workflow Task response carries one slice set + // while a cache miss replays every prior task, so the ranges have to be + // matched to the events that recorded them rather than to the response. + int64 workflow_task_completed_event_id = 6; +} + +// The offsets a Workflow Task consumed, without the payloads. Recorded on +// WorkflowTaskCompleted so History grows with Workflow Tasks rather than with +// messages. +message StreamCursor { + string stream_id = 1; + int64 from_offset = 2; + int64 to_offset = 3; +} diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 1aae988d8..93cd0e8b4 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -24,6 +24,7 @@ import "temporal/api/enums/v1/activity.proto"; import "temporal/api/enums/v1/nexus.proto"; import "temporal/api/activity/v1/message.proto"; import "temporal/api/common/v1/message.proto"; +import "temporal/api/stream/v1/message.proto"; import "temporal/api/history/v1/message.proto"; import "temporal/api/workflow/v1/message.proto"; import "temporal/api/command/v1/message.proto"; @@ -384,6 +385,10 @@ message PollWorkflowTaskQueueResponse { // 3. If every group has some pending polls, assign the next poll to a group randomly // according to the weights. temporal.api.taskqueue.v1.PollerGroupsInfo poller_groups_info = 19; + + // Stream data attached to this task. Delivered out of band so the payloads + // never enter History; only the offset ranges are recorded there. + repeated temporal.api.stream.v1.StreamSlice stream_slices = 20; } message RespondWorkflowTaskCompletedRequest { From 7caf5c39a841ada0a3e82d86539dcab9116ad32b Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Wed, 16 Sep 2026 14:48:13 -0700 Subject: [PATCH 02/30] Exempted the stream proto from the prepositions rule. A half-open range reads as `from_offset`/`to_offset` in the server's own protos, and AIP-140 rejects both names. --- api-linter.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api-linter.yaml b/api-linter.yaml index 290842854..5a38d2b4c 100644 --- a/api-linter.yaml +++ b/api-linter.yaml @@ -12,6 +12,11 @@ disabled_rules: - "core::0123::resource-annotation" # We don't require resource annotations on all messages -- https://linter.aip.dev/123/resource-annotation +- included_paths: + - "**/stream/v1/message.proto" + disabled_rules: + - "core::0140::prepositions" # Half-open offset ranges are spelled `from_offset`/`to_offset` here and in the server's own protos -- https://linter.aip.dev/140/prepositions + - included_paths: - "**/workflowservice/v1/request_response.proto" - "**/operatorservice/v1/request_response.proto" From 2b05496592da2a818e137895ec417203c6b36d5f Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Wed, 16 Sep 2026 14:48:13 -0700 Subject: [PATCH 03/30] Regenerated the OpenAPI specs. `make http-api-docs` picks up the stream slices on the poll response and the two new event types, so the committed specs were stale. --- openapi/openapiv2.json | 143 ++++++++++++++++++++++++++++++++++++++++- openapi/openapiv3.yaml | 126 ++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+), 2 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 0b643ceb7..541226bd6 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -16116,10 +16116,12 @@ "EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED", "EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED", "EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED", - "EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED" + "EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED", + "EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED", + "EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED" ], "default": "EVENT_TYPE_UNSPECIFIED", - "description": "- EVENT_TYPE_UNSPECIFIED: Place holder and should never appear in a Workflow execution history\n - EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: Workflow execution has been triggered/started\nIt contains Workflow execution inputs, as well as Workflow timeout configurations\n - EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED: Workflow execution has successfully completed and contains Workflow execution results\n - EVENT_TYPE_WORKFLOW_EXECUTION_FAILED: Workflow execution has unsuccessfully completed and contains the Workflow execution error\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT: Workflow execution has timed out by the Temporal Server\nUsually due to the Workflow having not been completed within timeout settings\n - EVENT_TYPE_WORKFLOW_TASK_SCHEDULED: Workflow Task has been scheduled and the SDK client should now be able to process any new history events\n - EVENT_TYPE_WORKFLOW_TASK_STARTED: Workflow Task has started and the SDK client has picked up the Workflow Task and is processing new history events\n - EVENT_TYPE_WORKFLOW_TASK_COMPLETED: Workflow Task has completed\nThe SDK client picked up the Workflow Task and processed new history events\nSDK client may or may not ask the Temporal Server to do additional work, such as:\nEVENT_TYPE_ACTIVITY_TASK_SCHEDULED\nEVENT_TYPE_TIMER_STARTED\nEVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES\nEVENT_TYPE_MARKER_RECORDED\nEVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED\nEVENT_TYPE_WORKFLOW_EXECUTION_FAILED\nEVENT_TYPE_WORKFLOW_EXECUTION_CANCELED\nEVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW\n - EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: Workflow Task encountered a timeout\nEither an SDK client with a local cache was not available at the time, or it took too long for the SDK client to process the task\n - EVENT_TYPE_WORKFLOW_TASK_FAILED: Workflow Task encountered a failure\nUsually this means that the Workflow was non-deterministic\nHowever, the Workflow reset functionality also uses this event\n - EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: Activity Task was scheduled\nThe SDK client should pick up this activity task and execute\nThis event type contains activity inputs, as well as activity timeout configurations\n - EVENT_TYPE_ACTIVITY_TASK_STARTED: Activity Task has started executing\nThe SDK client has picked up the Activity Task and is processing the Activity invocation\n - EVENT_TYPE_ACTIVITY_TASK_COMPLETED: Activity Task has finished successfully\nThe SDK client has picked up and successfully completed the Activity Task\nThis event type contains Activity execution results\n - EVENT_TYPE_ACTIVITY_TASK_FAILED: Activity Task has finished unsuccessfully\nThe SDK picked up the Activity Task but unsuccessfully completed it\nThis event type contains Activity execution errors\n - EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT: Activity has timed out according to the Temporal Server\nActivity did not complete within the timeout settings\n - EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED: A request to cancel the Activity has occurred\nThe SDK client will be able to confirm cancellation of an Activity during an Activity heartbeat\n - EVENT_TYPE_ACTIVITY_TASK_CANCELED: Activity has been cancelled\n - EVENT_TYPE_TIMER_STARTED: A timer has started\n - EVENT_TYPE_TIMER_FIRED: A timer has fired\n - EVENT_TYPE_TIMER_CANCELED: A time has been cancelled\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED: A request has been made to cancel the Workflow execution\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: SDK client has confirmed the cancellation request and the Workflow execution has been cancelled\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Workflow has requested that the Temporal Server try to cancel another Workflow\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server could not cancel the targeted Workflow\nThis is usually because the target Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED: Temporal Server has successfully requested the cancellation of the target Workflow\n - EVENT_TYPE_MARKER_RECORDED: A marker has been recorded.\nThis event type is transparent to the Temporal Server\nThe Server will only store it and will not try to understand it.\n - EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: Workflow has received a Signal event\nThe event type contains the Signal name, as well as a Signal payload\n - EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: Workflow execution has been forcefully terminated\nThis is usually because the terminate Workflow API was called\n - EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: Workflow has successfully completed and a new Workflow has been started within the same transaction\nContains last Workflow execution results as well as new Workflow execution inputs\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to start a child Workflow\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution cannot be started/triggered\nUsually due to a child Workflow ID collision\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED: Child Workflow execution has successfully started/triggered\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED: Child Workflow execution has successfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution has unsuccessfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED: Child Workflow execution has been cancelled\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT: Child Workflow execution has timed out by the Temporal Server\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED: Child Workflow execution has been terminated\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to Signal the targeted Workflow\nContains the Signal name, as well as a Signal payload\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server cannot Signal the targeted Workflow\nUsually because the Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED: Temporal Server has successfully Signaled the targeted Workflow\n - EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: Workflow search attributes should be updated and synchronized with the visibility store\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: An update was admitted. Note that not all admitted updates result in this\nevent. See UpdateAdmittedEventOrigin for situations in which this event\nis created.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED: An update was accepted (i.e. passed validation, perhaps because no validator was defined)\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REJECTED: This event is never written to history.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED: An update completed\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of the workflow as a whole have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of an already-scheduled activity have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: Workflow properties modified by user workflow code\n - EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: A Nexus operation was scheduled using a ScheduleNexusOperation command.\n - EVENT_TYPE_NEXUS_OPERATION_STARTED: An asynchronous Nexus operation was started by a Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_COMPLETED: A Nexus operation completed successfully.\n - EVENT_TYPE_NEXUS_OPERATION_FAILED: A Nexus operation failed.\n - EVENT_TYPE_NEXUS_OPERATION_CANCELED: A Nexus operation completed as canceled.\n - EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: A Nexus operation timed out.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: A Nexus operation was requested to be canceled using a RequestCancelNexusOperation command.\n - EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: Workflow execution options updated by user.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED: A cancellation request for a Nexus operation was successfully delivered to the Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED: A cancellation request for a Nexus operation resulted in an error.\n - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED: An event that indicates that the workflow execution has been paused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED: An event that indicates that the previously paused workflow execution has been unpaused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED: An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.", + "description": "- EVENT_TYPE_UNSPECIFIED: Place holder and should never appear in a Workflow execution history\n - EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: Workflow execution has been triggered/started\nIt contains Workflow execution inputs, as well as Workflow timeout configurations\n - EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED: Workflow execution has successfully completed and contains Workflow execution results\n - EVENT_TYPE_WORKFLOW_EXECUTION_FAILED: Workflow execution has unsuccessfully completed and contains the Workflow execution error\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT: Workflow execution has timed out by the Temporal Server\nUsually due to the Workflow having not been completed within timeout settings\n - EVENT_TYPE_WORKFLOW_TASK_SCHEDULED: Workflow Task has been scheduled and the SDK client should now be able to process any new history events\n - EVENT_TYPE_WORKFLOW_TASK_STARTED: Workflow Task has started and the SDK client has picked up the Workflow Task and is processing new history events\n - EVENT_TYPE_WORKFLOW_TASK_COMPLETED: Workflow Task has completed\nThe SDK client picked up the Workflow Task and processed new history events\nSDK client may or may not ask the Temporal Server to do additional work, such as:\nEVENT_TYPE_ACTIVITY_TASK_SCHEDULED\nEVENT_TYPE_TIMER_STARTED\nEVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES\nEVENT_TYPE_MARKER_RECORDED\nEVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED\nEVENT_TYPE_WORKFLOW_EXECUTION_FAILED\nEVENT_TYPE_WORKFLOW_EXECUTION_CANCELED\nEVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW\n - EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: Workflow Task encountered a timeout\nEither an SDK client with a local cache was not available at the time, or it took too long for the SDK client to process the task\n - EVENT_TYPE_WORKFLOW_TASK_FAILED: Workflow Task encountered a failure\nUsually this means that the Workflow was non-deterministic\nHowever, the Workflow reset functionality also uses this event\n - EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: Activity Task was scheduled\nThe SDK client should pick up this activity task and execute\nThis event type contains activity inputs, as well as activity timeout configurations\n - EVENT_TYPE_ACTIVITY_TASK_STARTED: Activity Task has started executing\nThe SDK client has picked up the Activity Task and is processing the Activity invocation\n - EVENT_TYPE_ACTIVITY_TASK_COMPLETED: Activity Task has finished successfully\nThe SDK client has picked up and successfully completed the Activity Task\nThis event type contains Activity execution results\n - EVENT_TYPE_ACTIVITY_TASK_FAILED: Activity Task has finished unsuccessfully\nThe SDK picked up the Activity Task but unsuccessfully completed it\nThis event type contains Activity execution errors\n - EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT: Activity has timed out according to the Temporal Server\nActivity did not complete within the timeout settings\n - EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED: A request to cancel the Activity has occurred\nThe SDK client will be able to confirm cancellation of an Activity during an Activity heartbeat\n - EVENT_TYPE_ACTIVITY_TASK_CANCELED: Activity has been cancelled\n - EVENT_TYPE_TIMER_STARTED: A timer has started\n - EVENT_TYPE_TIMER_FIRED: A timer has fired\n - EVENT_TYPE_TIMER_CANCELED: A time has been cancelled\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED: A request has been made to cancel the Workflow execution\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: SDK client has confirmed the cancellation request and the Workflow execution has been cancelled\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Workflow has requested that the Temporal Server try to cancel another Workflow\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server could not cancel the targeted Workflow\nThis is usually because the target Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED: Temporal Server has successfully requested the cancellation of the target Workflow\n - EVENT_TYPE_MARKER_RECORDED: A marker has been recorded.\nThis event type is transparent to the Temporal Server\nThe Server will only store it and will not try to understand it.\n - EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: Workflow has received a Signal event\nThe event type contains the Signal name, as well as a Signal payload\n - EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: Workflow execution has been forcefully terminated\nThis is usually because the terminate Workflow API was called\n - EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: Workflow has successfully completed and a new Workflow has been started within the same transaction\nContains last Workflow execution results as well as new Workflow execution inputs\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to start a child Workflow\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution cannot be started/triggered\nUsually due to a child Workflow ID collision\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED: Child Workflow execution has successfully started/triggered\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED: Child Workflow execution has successfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution has unsuccessfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED: Child Workflow execution has been cancelled\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT: Child Workflow execution has timed out by the Temporal Server\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED: Child Workflow execution has been terminated\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to Signal the targeted Workflow\nContains the Signal name, as well as a Signal payload\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server cannot Signal the targeted Workflow\nUsually because the Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED: Temporal Server has successfully Signaled the targeted Workflow\n - EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: Workflow search attributes should be updated and synchronized with the visibility store\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: An update was admitted. Note that not all admitted updates result in this\nevent. See UpdateAdmittedEventOrigin for situations in which this event\nis created.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED: An update was accepted (i.e. passed validation, perhaps because no validator was defined)\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REJECTED: This event is never written to history.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED: An update completed\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of the workflow as a whole have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of an already-scheduled activity have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: Workflow properties modified by user workflow code\n - EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: A Nexus operation was scheduled using a ScheduleNexusOperation command.\n - EVENT_TYPE_NEXUS_OPERATION_STARTED: An asynchronous Nexus operation was started by a Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_COMPLETED: A Nexus operation completed successfully.\n - EVENT_TYPE_NEXUS_OPERATION_FAILED: A Nexus operation failed.\n - EVENT_TYPE_NEXUS_OPERATION_CANCELED: A Nexus operation completed as canceled.\n - EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: A Nexus operation timed out.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: A Nexus operation was requested to be canceled using a RequestCancelNexusOperation command.\n - EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: Workflow execution options updated by user.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED: A cancellation request for a Nexus operation was successfully delivered to the Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED: A cancellation request for a Nexus operation resulted in an error.\n - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED: An event that indicates that the workflow execution has been paused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED: An event that indicates that the previously paused workflow execution has been unpaused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED: An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.\n - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: A Workflow subscribed to a stream. Recorded once per subscription, not\nper message: the offsets a task consumed ride WorkflowTaskCompleted and\nthe payloads never enter History at all.\n - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED: A Workflow published a batch of messages to a stream. Recorded per\nbatch, and carrying only the offset range it landed at: the bodies go to\nthe stream's own log, never into History.", "title": "Whenever this list of events is changed do change the function shouldBufferEvent in mutableStateBuilder.go to make sure to do the correct event ordering" }, "v1Execution": { @@ -16786,6 +16788,12 @@ }, "workflowExecutionTimeSkippingTransitionedEventAttributes": { "$ref": "#/definitions/v1WorkflowExecutionTimeSkippingTransitionedEventAttributes" + }, + "workflowStreamSubscribedEventAttributes": { + "$ref": "#/definitions/v1WorkflowStreamSubscribedEventAttributes" + }, + "workflowStreamMessagesAddedEventAttributes": { + "$ref": "#/definitions/v1WorkflowStreamMessagesAddedEventAttributes" } }, "description": "History events are the method by which Temporal SDKs advance (or recreate) workflow state.\nSee the `EventType` enum for more info about what each event is for." @@ -18560,6 +18568,14 @@ "pollerGroupsInfo": { "$ref": "#/definitions/v1PollerGroupsInfo", "description": "The weighted, versioned list of poller groups IDs that client should use for future polls to\nthis task queue. Client should ignore this if it has already applied a snapshot with a\nversion greater than or equal to `poller_groups_info.version`. Client is expected to:\n 1. Maintain minimum number of pollers no less than the number of groups.\n 2. Try to assign the next poll to a group without any pending polls,\n 3. If every group has some pending polls, assign the next poll to a group randomly\n according to the weights." + }, + "streamSlices": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1StreamSlice" + }, + "description": "Stream data attached to this task. Delivered out of band so the payloads\nnever enter History; only the offset ranges are recorded there." } } }, @@ -20049,6 +20065,78 @@ } } }, + "v1StreamCursor": { + "type": "object", + "properties": { + "streamId": { + "type": "string" + }, + "fromOffset": { + "type": "string", + "format": "int64" + }, + "toOffset": { + "type": "string", + "format": "int64" + } + }, + "description": "The offsets a Workflow Task consumed, without the payloads. Recorded on\nWorkflowTaskCompleted so History grows with Workflow Tasks rather than with\nmessages." + }, + "v1StreamMessage": { + "type": "object", + "properties": { + "body": { + "$ref": "#/definitions/v1Payload" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1Payload" + } + }, + "topic": { + "type": "string" + }, + "topicSequence": { + "type": "string", + "format": "int64" + } + } + }, + "v1StreamSlice": { + "type": "object", + "properties": { + "streamId": { + "type": "string" + }, + "runId": { + "type": "string" + }, + "fromOffset": { + "type": "string", + "format": "int64", + "description": "Inclusive." + }, + "toOffset": { + "type": "string", + "format": "int64", + "description": "Exclusive. Equal to from_offset when the subscription observed nothing,\nwhich is a fact replay has to reproduce rather than an absence of one." + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1StreamMessage" + } + }, + "workflowTaskCompletedEventId": { + "type": "string", + "format": "int64", + "description": "The WorkflowTaskCompleted event whose stream_cursors recorded this range.\nSet only when the server is re-supplying a range for a task being\nreplayed; a slice for the task now being started leaves it unset, because\nthe event closing that task does not exist yet.\n\nReplay needs this because a Workflow Task response carries one slice set\nwhile a cache miss replays every prior task, so the ranges have to be\nmatched to the events that recorded them rather than to the response." + } + }, + "description": "A contiguous range of a stream delivered to a Workflow Task, along with the\noffsets it covers. The offsets are what History records; the messages\nthemselves are never written to History." + }, "v1StructuredCalendarSpec": { "type": "object", "properties": { @@ -22425,6 +22513,49 @@ } } }, + "v1WorkflowStreamMessagesAddedEventAttributes": { + "type": "object", + "properties": { + "workflowTaskCompletedEventId": { + "type": "string", + "format": "int64", + "description": "The WorkflowTaskCompleted event of the task whose command published this\nbatch." + }, + "streamId": { + "type": "string", + "description": "Stream the Workflow published to." + }, + "firstOffset": { + "type": "string", + "format": "int64", + "description": "Offset the first message of the batch landed at." + }, + "messageCount": { + "type": "string", + "format": "int64", + "description": "How many messages the batch held. With first_offset this names the range\nwithout carrying any of it, which is what keeps this event a fixed size\nno matter how large the batch or its payloads are." + } + } + }, + "v1WorkflowStreamSubscribedEventAttributes": { + "type": "object", + "properties": { + "workflowTaskCompletedEventId": { + "type": "string", + "format": "int64", + "description": "The WorkflowTaskCompleted event of the task whose command created this\nsubscription." + }, + "streamId": { + "type": "string", + "description": "Stream the Workflow subscribed to." + }, + "startOffset": { + "type": "string", + "format": "int64", + "description": "The offset the subscription actually starts from. Resolved by the server\nwhen the subscription is registered and recorded here, so replay reads\nthe resolved value rather than resolving it again against a stream that\nhas since moved." + } + } + }, "v1WorkflowTaskCompletedEventAttributes": { "type": "object", "properties": { @@ -22466,6 +22597,14 @@ "$ref": "#/definitions/v1VersioningBehavior", "description": "Versioning behavior sent by the worker that completed this task for this particular workflow\nexecution. UNSPECIFIED means the task was completed by an unversioned worker. This value\nupdates workflow execution's `versioning_info.behavior`." }, + "streamCursors": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1StreamCursor" + }, + "description": "Offset ranges this Workflow Task consumed from streams it subscribes to.\nRecorded on every task where a subscription is active, including when it\nobserved nothing: an empty range is a fact replay must reproduce, and\nomitting it would let replay deliver messages the Workflow did not have." + }, "workerDeploymentVersion": { "type": "string", "description": "The Worker Deployment Version that completed this task. Must be set if `versioning_behavior`\nis set. This value updates workflow execution's `versioning_info.version`.\nDeprecated. Replaced with `deployment_version`." diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 2aa7c45bf..45d644b78 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -12878,6 +12878,8 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED + - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED + - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED type: string format: enum version: @@ -13043,6 +13045,10 @@ components: $ref: '#/components/schemas/WorkflowExecutionUnpausedEventAttributes' workflowExecutionTimeSkippingTransitionedEventAttributes: $ref: '#/components/schemas/WorkflowExecutionTimeSkippingTransitionedEventAttributes' + workflowStreamSubscribedEventAttributes: + $ref: '#/components/schemas/WorkflowStreamSubscribedEventAttributes' + workflowStreamMessagesAddedEventAttributes: + $ref: '#/components/schemas/WorkflowStreamMessagesAddedEventAttributes' description: |- History events are the method by which Temporal SDKs advance (or recreate) workflow state. See the `EventType` enum for more info about what each event is for. @@ -14853,6 +14859,13 @@ components: 2. Try to assign the next poll to a group without any pending polls, 3. If every group has some pending polls, assign the next poll to a group randomly according to the weights. + streamSlices: + type: array + items: + $ref: '#/components/schemas/StreamSlice' + description: |- + Stream data attached to this task. Delivered out of band so the payloads + never enter History; only the offset ranges are recorded there. PollerGroupInfo: type: object properties: @@ -15529,6 +15542,8 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED + - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED + - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED type: string description: The event type of the history event generated by the request. format: enum @@ -17680,6 +17695,66 @@ components: type: type: string description: The type of the driver, required. + StreamCursor: + type: object + properties: + streamId: + type: string + fromOffset: + type: string + toOffset: + type: string + description: |- + The offsets a Workflow Task consumed, without the payloads. Recorded on + WorkflowTaskCompleted so History grows with Workflow Tasks rather than with + messages. + StreamMessage: + type: object + properties: + body: + $ref: '#/components/schemas/Payload' + metadata: + type: object + additionalProperties: + $ref: '#/components/schemas/Payload' + topic: + type: string + topicSequence: + type: string + StreamSlice: + type: object + properties: + streamId: + type: string + runId: + type: string + fromOffset: + type: string + description: Inclusive. + toOffset: + type: string + description: |- + Exclusive. Equal to from_offset when the subscription observed nothing, + which is a fact replay has to reproduce rather than an absence of one. + messages: + type: array + items: + $ref: '#/components/schemas/StreamMessage' + workflowTaskCompletedEventId: + type: string + description: |- + The WorkflowTaskCompleted event whose stream_cursors recorded this range. + Set only when the server is re-supplying a range for a task being + replayed; a slice for the task now being started leaves it unset, because + the event closing that task does not exist yet. + + Replay needs this because a Workflow Task response carries one slice set + while a cache miss replays every prior task, so the ranges have to be + matched to the events that recorded them rather than to the response. + description: |- + A contiguous range of a stream delivered to a Workflow Task, along with the + offsets it covers. The offsets are what History records; the messages + themselves are never written to History. StructuredCalendarSpec: type: object properties: @@ -19830,6 +19905,8 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED + - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED + - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED type: string format: enum description: EventReference is a direct reference to a history event through the event ID. @@ -19901,6 +19978,8 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED + - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED + - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED type: string format: enum description: RequestIdReference is a indirect reference to a history event through the request ID. @@ -21023,6 +21102,44 @@ components: * BETWEEN ... AND STARTS_WITH description: Activity trigger will be triggered when an activity is about to start. + WorkflowStreamMessagesAddedEventAttributes: + type: object + properties: + workflowTaskCompletedEventId: + type: string + description: |- + The WorkflowTaskCompleted event of the task whose command published this + batch. + streamId: + type: string + description: Stream the Workflow published to. + firstOffset: + type: string + description: Offset the first message of the batch landed at. + messageCount: + type: string + description: |- + How many messages the batch held. With first_offset this names the range + without carrying any of it, which is what keeps this event a fixed size + no matter how large the batch or its payloads are. + WorkflowStreamSubscribedEventAttributes: + type: object + properties: + workflowTaskCompletedEventId: + type: string + description: |- + The WorkflowTaskCompleted event of the task whose command created this + subscription. + streamId: + type: string + description: Stream the Workflow subscribed to. + startOffset: + type: string + description: |- + The offset the subscription actually starts from. Resolved by the server + when the subscription is registered and recorded here, so replay reads + the resolved value rather than resolving it again against a stream that + has since moved. WorkflowTaskCompletedEventAttributes: type: object properties: @@ -21077,6 +21194,15 @@ components: execution. UNSPECIFIED means the task was completed by an unversioned worker. This value updates workflow execution's `versioning_info.behavior`. format: enum + streamCursors: + type: array + items: + $ref: '#/components/schemas/StreamCursor' + description: |- + Offset ranges this Workflow Task consumed from streams it subscribes to. + Recorded on every task where a subscription is active, including when it + observed nothing: an empty range is a fact replay must reproduce, and + omitting it would let replay deliver messages the Workflow did not have. workerDeploymentVersion: type: string description: |- From e65008a03349ae574c25f6dc823f8e2e06096dc8 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 18 Sep 2026 16:27:48 -0700 Subject: [PATCH 04/30] Added workflow task failure causes for the stream commands. The server fails the task instead of the RPC when a stream command is rejected or a recorded range cannot be served, and needs causes the SDK can show. --- temporal/api/enums/v1/failed_cause.proto | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/temporal/api/enums/v1/failed_cause.proto b/temporal/api/enums/v1/failed_cause.proto index f902aa9a1..989febb8e 100644 --- a/temporal/api/enums/v1/failed_cause.proto +++ b/temporal/api/enums/v1/failed_cause.proto @@ -90,6 +90,14 @@ enum WorkflowTaskFailedCause { WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED = 39; // A workflow task failed because the request exceeded a size limit. WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE = 40; + // A workflow task completed with an invalid AddStreamMessages command. + WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES = 41; + // A workflow task completed with an invalid SubscribeStream command. + WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES = 42; + // A workflow task could not be started because a stream range it consumed and recorded in + // History can no longer be served, for example after truncation or because it exceeds the + // replay bound. Check the workflow task failure message for more information. + WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE = 43; } // Activity tasks can fail for various reasons. Note that some of these reasons can only originate From 228b478636ee4d4a8a4b454f8230057a636698a3 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 18 Sep 2026 16:27:48 -0700 Subject: [PATCH 05/30] Documented the stream proto fields and relocated stream_cursors. The publish command comment claimed no event. The run id, message fields and name precedence had no comments. --- temporal/api/command/v1/message.proto | 6 +++++- temporal/api/history/v1/message.proto | 13 +++++++------ temporal/api/stream/v1/message.proto | 7 +++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 311b918e4..4e848ece2 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -331,7 +331,8 @@ message Command { } // Appends to a stream the Workflow owns. Applied inside the Workflow Task's own -// commit, so it emits no History Event and does not schedule further work. +// commit. Produces one `WorkflowStreamMessagesAdded` event carrying the offset +// range and none of the payload; it schedules no further work. message AddStreamMessagesCommandAttributes { // Empty means the Workflow's default output stream. string stream_id = 1; @@ -347,6 +348,9 @@ message AddStreamMessagesCommandAttributes { message SubscribeStreamCommandAttributes { // Stream to consume. A stream in another execution is addressed by its id; // one this Workflow owns is addressed by the name it was published under. + // The server resolves an owned name first and falls back to a standalone + // id, so a Workflow that owns a stream under this name cannot reach a + // standalone stream with the same id. string stream_id = 1; // Where to start. Negative means from wherever the stream is when the // subscription is registered, which the server resolves and records so diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 089dccf78..11023509e 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -370,12 +370,6 @@ message WorkflowTaskCompletedEventAttributes { // execution. UNSPECIFIED means the task was completed by an unversioned worker. This value // updates workflow execution's `versioning_info.behavior`. temporal.api.enums.v1.VersioningBehavior versioning_behavior = 8; - - // Offset ranges this Workflow Task consumed from streams it subscribes to. - // Recorded on every task where a subscription is active, including when it - // observed nothing: an empty range is a fact replay must reproduce, and - // omitting it would let replay deliver messages the Workflow did not have. - repeated temporal.api.stream.v1.StreamCursor stream_cursors = 20; // The Worker Deployment Version that completed this task. Must be set if `versioning_behavior` // is set. This value updates workflow execution's `versioning_info.version`. // Deprecated. Replaced with `deployment_version`. @@ -386,6 +380,13 @@ message WorkflowTaskCompletedEventAttributes { // The Worker Deployment Version that completed this task. Must be set if `versioning_behavior` // is set. This value updates workflow execution's `versioning_info.deployment_version`. temporal.api.deployment.v1.WorkerDeploymentVersion deployment_version = 11; + + // Offset ranges this Workflow Task consumed from streams it subscribes to. + // Recorded on every task where a subscription is active, including when it + // observed nothing: an empty range is a fact replay must reproduce, and + // omitting it would let replay deliver messages the Workflow did not have. + // Numbered 20 to leave 14 through 19 free for fields added on the main line. + repeated temporal.api.stream.v1.StreamCursor stream_cursors = 20; } message WorkflowTaskTimedOutEventAttributes { diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto index a1702e3c7..396510f5e 100644 --- a/temporal/api/stream/v1/message.proto +++ b/temporal/api/stream/v1/message.proto @@ -12,9 +12,14 @@ option csharp_namespace = "Temporalio.Api.Stream.V1"; import "temporal/api/common/v1/message.proto"; message StreamMessage { + // The message itself, stored as sent. temporal.api.common.v1.Payload body = 1; + // Producer-supplied provenance, stored as sent. map metadata = 2; + // Producer-supplied grouping label, stored as sent. string topic = 3; + // Producer-supplied position within `topic`. The server stores it as sent + // and does not assign, validate or order by it. int64 topic_sequence = 4; } @@ -23,6 +28,8 @@ message StreamMessage { // themselves are never written to History. message StreamSlice { string stream_id = 1; + // Run id of the execution that owns the stream. Set on both a slice for the + // task being started and a re-supplied one. string run_id = 2; // Inclusive. int64 from_offset = 3; From 1aace128a29293181e0a324e5904f80797bc7b43 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 18 Sep 2026 16:27:48 -0700 Subject: [PATCH 06/30] Regenerated the OpenAPI specs. --- openapi/openapiv2.json | 38 +++++++++++++++++++++++--------------- openapi/openapiv3.yaml | 34 ++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 541226bd6..599bb39f1 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20086,20 +20086,24 @@ "type": "object", "properties": { "body": { - "$ref": "#/definitions/v1Payload" + "$ref": "#/definitions/v1Payload", + "description": "The message itself, stored as sent." }, "metadata": { "type": "object", "additionalProperties": { "$ref": "#/definitions/v1Payload" - } + }, + "description": "Producer-supplied provenance, stored as sent." }, "topic": { - "type": "string" + "type": "string", + "description": "Producer-supplied grouping label, stored as sent." }, "topicSequence": { "type": "string", - "format": "int64" + "format": "int64", + "description": "Producer-supplied position within `topic`. The server stores it as sent\nand does not assign, validate or order by it." } } }, @@ -20110,7 +20114,8 @@ "type": "string" }, "runId": { - "type": "string" + "type": "string", + "description": "Run id of the execution that owns the stream. Set on both a slice for the\ntask being started and a re-supplied one." }, "fromOffset": { "type": "string", @@ -22597,14 +22602,6 @@ "$ref": "#/definitions/v1VersioningBehavior", "description": "Versioning behavior sent by the worker that completed this task for this particular workflow\nexecution. UNSPECIFIED means the task was completed by an unversioned worker. This value\nupdates workflow execution's `versioning_info.behavior`." }, - "streamCursors": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/v1StreamCursor" - }, - "description": "Offset ranges this Workflow Task consumed from streams it subscribes to.\nRecorded on every task where a subscription is active, including when it\nobserved nothing: an empty range is a fact replay must reproduce, and\nomitting it would let replay deliver messages the Workflow did not have." - }, "workerDeploymentVersion": { "type": "string", "description": "The Worker Deployment Version that completed this task. Must be set if `versioning_behavior`\nis set. This value updates workflow execution's `versioning_info.version`.\nDeprecated. Replaced with `deployment_version`." @@ -22616,6 +22613,14 @@ "deploymentVersion": { "$ref": "#/definitions/v1WorkerDeploymentVersion", "description": "The Worker Deployment Version that completed this task. Must be set if `versioning_behavior`\nis set. This value updates workflow execution's `versioning_info.deployment_version`." + }, + "streamCursors": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1StreamCursor" + }, + "description": "Offset ranges this Workflow Task consumed from streams it subscribes to.\nRecorded on every task where a subscription is active, including when it\nobserved nothing: an empty range is a fact replay must reproduce, and\nomitting it would let replay deliver messages the Workflow did not have.\nNumbered 20 to leave 14 through 19 free for fields added on the main line." } } }, @@ -22692,10 +22697,13 @@ "WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE", "WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE", "WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED", - "WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE" + "WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE", + "WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES", + "WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES", + "WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE" ], "default": "WORKFLOW_TASK_FAILED_CAUSE_UNSPECIFIED", - "description": "Workflow tasks can fail for various reasons. Note that some of these reasons can only originate\nfrom the server, and some of them can only originate from the SDK/worker.\n\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND: Between starting and completing the workflow task (with a workflow completion command), some\nnew command (like a signal) was processed into workflow history. The outstanding task will be\nfailed with this reason, and a worker must pick up a new task.\n - WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE: The worker wishes to fail the task and have the next one be generated on a normal, not sticky\nqueue. Generally workers should prefer to use the explicit `ResetStickyTaskQueue` RPC call.\n - WORKFLOW_TASK_FAILED_CAUSE_NON_DETERMINISTIC_ERROR: The worker encountered a mismatch while replaying history between what was expected, and\nwhat the workflow code actually did.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_CHILD_WORKFLOWS_LIMIT_EXCEEDED: We send the below error codes to users when their requests would violate a size constraint\nof their workflow. We do this to ensure that the state of their workflow does not become too\nlarge because that can cause severe performance degradation. You can modify the thresholds for\neach of these errors within your dynamic config.\n\nSpawning a new child workflow would cause this workflow to exceed its limit of pending child\nworkflows.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_ACTIVITIES_LIMIT_EXCEEDED: Starting a new activity would cause this workflow to exceed its limit of pending activities\nthat we track.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_SIGNALS_LIMIT_EXCEEDED: A workflow has a buffer of signals that have not yet reached their destination. We return this\nerror when sending a new signal would exceed the capacity of this buffer.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_REQUEST_CANCEL_LIMIT_EXCEEDED: Similarly, we have a buffer of pending requests to cancel other workflows. We return this error\nwhen our capacity for pending cancel requests is already reached.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UPDATE_WORKFLOW_EXECUTION_MESSAGE: Workflow execution update message (update.Acceptance, update.Rejection, or update.Response)\nhas wrong format, or missing required fields.\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_UPDATE: Similar to WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND, but for updates.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid ScheduleNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_NEXUS_OPERATIONS_LIMIT_EXCEEDED: A workflow task completed requesting to schedule a Nexus Operation exceeding the server configured limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid RequestCancelNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_FEATURE_DISABLED: A workflow task completed requesting a feature that's disabled on the server (either system wide or - typically -\nfor the workflow's namespace).\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE: A workflow task failed because a grpc message was too large.\n - WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE: A workflow task failed because payloads were too large.\n - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE: A workflow task failed because an external storage operation failed.\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED: A workflow task is failed because the workflow is paused before the task is started.\n - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE: A workflow task failed because the request exceeded a size limit." + "description": "Workflow tasks can fail for various reasons. Note that some of these reasons can only originate\nfrom the server, and some of them can only originate from the SDK/worker.\n\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND: Between starting and completing the workflow task (with a workflow completion command), some\nnew command (like a signal) was processed into workflow history. The outstanding task will be\nfailed with this reason, and a worker must pick up a new task.\n - WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE: The worker wishes to fail the task and have the next one be generated on a normal, not sticky\nqueue. Generally workers should prefer to use the explicit `ResetStickyTaskQueue` RPC call.\n - WORKFLOW_TASK_FAILED_CAUSE_NON_DETERMINISTIC_ERROR: The worker encountered a mismatch while replaying history between what was expected, and\nwhat the workflow code actually did.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_CHILD_WORKFLOWS_LIMIT_EXCEEDED: We send the below error codes to users when their requests would violate a size constraint\nof their workflow. We do this to ensure that the state of their workflow does not become too\nlarge because that can cause severe performance degradation. You can modify the thresholds for\neach of these errors within your dynamic config.\n\nSpawning a new child workflow would cause this workflow to exceed its limit of pending child\nworkflows.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_ACTIVITIES_LIMIT_EXCEEDED: Starting a new activity would cause this workflow to exceed its limit of pending activities\nthat we track.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_SIGNALS_LIMIT_EXCEEDED: A workflow has a buffer of signals that have not yet reached their destination. We return this\nerror when sending a new signal would exceed the capacity of this buffer.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_REQUEST_CANCEL_LIMIT_EXCEEDED: Similarly, we have a buffer of pending requests to cancel other workflows. We return this error\nwhen our capacity for pending cancel requests is already reached.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UPDATE_WORKFLOW_EXECUTION_MESSAGE: Workflow execution update message (update.Acceptance, update.Rejection, or update.Response)\nhas wrong format, or missing required fields.\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_UPDATE: Similar to WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND, but for updates.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid ScheduleNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_NEXUS_OPERATIONS_LIMIT_EXCEEDED: A workflow task completed requesting to schedule a Nexus Operation exceeding the server configured limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid RequestCancelNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_FEATURE_DISABLED: A workflow task completed requesting a feature that's disabled on the server (either system wide or - typically -\nfor the workflow's namespace).\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE: A workflow task failed because a grpc message was too large.\n - WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE: A workflow task failed because payloads were too large.\n - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE: A workflow task failed because an external storage operation failed.\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED: A workflow task is failed because the workflow is paused before the task is started.\n - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE: A workflow task failed because the request exceeded a size limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES: A workflow task completed with an invalid AddStreamMessages command.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES: A workflow task completed with an invalid SubscribeStream command.\n - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE: A workflow task could not be started because a stream range it consumed and recorded in\nHistory can no longer be served, for example after truncation or because it exceeds the\nreplay bound. Check the workflow task failure message for more information." }, "v1WorkflowTaskFailedEventAttributes": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 45d644b78..92c4b24f1 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17712,15 +17712,22 @@ components: type: object properties: body: - $ref: '#/components/schemas/Payload' + allOf: + - $ref: '#/components/schemas/Payload' + description: The message itself, stored as sent. metadata: type: object additionalProperties: $ref: '#/components/schemas/Payload' + description: Producer-supplied provenance, stored as sent. topic: type: string + description: Producer-supplied grouping label, stored as sent. topicSequence: type: string + description: |- + Producer-supplied position within `topic`. The server stores it as sent + and does not assign, validate or order by it. StreamSlice: type: object properties: @@ -17728,6 +17735,9 @@ components: type: string runId: type: string + description: |- + Run id of the execution that owns the stream. Set on both a slice for the + task being started and a re-supplied one. fromOffset: type: string description: Inclusive. @@ -21194,15 +21204,6 @@ components: execution. UNSPECIFIED means the task was completed by an unversioned worker. This value updates workflow execution's `versioning_info.behavior`. format: enum - streamCursors: - type: array - items: - $ref: '#/components/schemas/StreamCursor' - description: |- - Offset ranges this Workflow Task consumed from streams it subscribes to. - Recorded on every task where a subscription is active, including when it - observed nothing: an empty range is a fact replay must reproduce, and - omitting it would let replay deliver messages the Workflow did not have. workerDeploymentVersion: type: string description: |- @@ -21220,6 +21221,16 @@ components: description: |- The Worker Deployment Version that completed this task. Must be set if `versioning_behavior` is set. This value updates workflow execution's `versioning_info.deployment_version`. + streamCursors: + type: array + items: + $ref: '#/components/schemas/StreamCursor' + description: |- + Offset ranges this Workflow Task consumed from streams it subscribes to. + Recorded on every task where a subscription is active, including when it + observed nothing: an empty range is a fact replay must reproduce, and + omitting it would let replay deliver messages the Workflow did not have. + Numbered 20 to leave 14 through 19 free for fields added on the main line. WorkflowTaskCompletedMetadata: type: object properties: @@ -21327,6 +21338,9 @@ components: - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE + - WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES + - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES + - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE type: string format: enum failure: From 43546df7f9949ba94b80cd9f7756af66a48654c6 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Mon, 21 Sep 2026 01:22:58 -0700 Subject: [PATCH 07/30] Named the stream record on the wire and carried its producer identity. The record is the wire format every store keeps and every language reads, so it carries its kind, producer, attempt and sequence itself instead of a private envelope inside the body. The body is the user's payload, so a codec applies to it like to any other payload. --- temporal/api/command/v1/message.proto | 4 ++- temporal/api/stream/v1/message.proto | 48 ++++++++++++++++++++------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 4e848ece2..047c5e142 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -336,7 +336,9 @@ message Command { message AddStreamMessagesCommandAttributes { // Empty means the Workflow's default output stream. string stream_id = 1; - repeated temporal.api.stream.v1.StreamMessage messages = 2; + // Stored in order. The server sets `producer_id` to empty on each record, + // because the owning Workflow is the producer here. + repeated temporal.api.stream.v1.StreamRecord records = 2; } // Subscribe this Workflow to a stream, so later Workflow Tasks carry the ranges diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto index 396510f5e..d470009e4 100644 --- a/temporal/api/stream/v1/message.proto +++ b/temporal/api/stream/v1/message.proto @@ -11,20 +11,30 @@ option csharp_namespace = "Temporalio.Api.Stream.V1"; import "temporal/api/common/v1/message.proto"; -message StreamMessage { - // The message itself, stored as sent. +// One entry in a stream. The record is the wire format: stores keep it +// serialized as is and readers in every language decode the same bytes. +message StreamRecord { + // The value the producer published, stored as sent. A payload codec + // applies here as it does to any other payload. temporal.api.common.v1.Payload body = 1; // Producer-supplied provenance, stored as sent. map metadata = 2; // Producer-supplied grouping label, stored as sent. string topic = 3; - // Producer-supplied position within `topic`. The server stores it as sent - // and does not assign, validate or order by it. - int64 topic_sequence = 4; + // How to read this record. Unspecified is read as DATA. + StreamRecordKind kind = 4; + // Who wrote the record. Empty when the owning Workflow did. + string producer_id = 5; + // The producer's attempt. Readers treat a later attempt by the same + // producer as superseding what the earlier one wrote. + int64 attempt = 6; + // The producer's position within its attempt, or -1 when unnumbered. + // Stored as sent; the server does not assign, validate or order by it. + int64 sequence = 7; } // A contiguous range of a stream delivered to a Workflow Task, along with the -// offsets it covers. The offsets are what History records; the messages +// offsets it covers. The offsets are what History records; the records // themselves are never written to History. message StreamSlice { string stream_id = 1; @@ -36,11 +46,11 @@ message StreamSlice { // Exclusive. Equal to from_offset when the subscription observed nothing, // which is a fact replay has to reproduce rather than an absence of one. int64 to_offset = 4; - repeated StreamMessage messages = 5; - // The WorkflowTaskCompleted event whose stream_cursors recorded this range. - // Set only when the server is re-supplying a range for a task being - // replayed; a slice for the task now being started leaves it unset, because - // the event closing that task does not exist yet. + repeated StreamRecord records = 5; + // The WorkflowTaskCompleted event whose stream_cursors recorded this + // range. Set only when the server is re-supplying a range for a task + // being replayed; a slice for the task now being started leaves it unset, + // because the event closing that task does not exist yet. // // Replay needs this because a Workflow Task response carries one slice set // while a cache miss replays every prior task, so the ranges have to be @@ -50,9 +60,23 @@ message StreamSlice { // The offsets a Workflow Task consumed, without the payloads. Recorded on // WorkflowTaskCompleted so History grows with Workflow Tasks rather than with -// messages. +// records. message StreamCursor { string stream_id = 1; + // Inclusive. int64 from_offset = 2; + // Exclusive. int64 to_offset = 3; } + +// What a record means to a reader. Kept on the record itself so every store +// and every language reads it the same way without a private envelope. +enum StreamRecordKind { + // Read as DATA. + STREAM_RECORD_KIND_UNSPECIFIED = 0; + // A value the producer published; `body` carries it. + STREAM_RECORD_KIND_DATA = 1; + // The producer named by `producer_id` writes nothing more on `topic`. + // Says nothing about that producer's outcome and does not end the stream. + STREAM_RECORD_KIND_FINISH = 2; +} From 5efc2779e1a955b0ff88cd754c4f44e0b0c44db5 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Mon, 21 Sep 2026 01:23:08 -0700 Subject: [PATCH 08/30] Renamed the stream command, event and range to the record vocabulary. The SDKs call an entry a record and a cursor a single position, and `temporal.api` already uses "message" for protocol envelopes. One noun on the wire keeps History readable next to user code. --- api-linter.yaml | 2 +- temporal/api/command/v1/message.proto | 11 ++++++----- temporal/api/enums/v1/command_type.proto | 2 +- temporal/api/enums/v1/event_type.proto | 6 +++--- temporal/api/enums/v1/failed_cause.proto | 4 ++-- temporal/api/history/v1/message.proto | 18 +++++++++--------- temporal/api/stream/v1/message.proto | 6 +++--- 7 files changed, 25 insertions(+), 24 deletions(-) diff --git a/api-linter.yaml b/api-linter.yaml index 5a38d2b4c..7717df60e 100644 --- a/api-linter.yaml +++ b/api-linter.yaml @@ -15,7 +15,7 @@ - included_paths: - "**/stream/v1/message.proto" disabled_rules: - - "core::0140::prepositions" # Half-open offset ranges are spelled `from_offset`/`to_offset` here and in the server's own protos -- https://linter.aip.dev/140/prepositions + - "core::0140::prepositions" # Half-open offset ranges on StreamSlice and StreamRange are spelled `from_offset`/`to_offset`, as in the server's own protos -- https://linter.aip.dev/140/prepositions - included_paths: - "**/workflowservice/v1/request_response.proto" diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 047c5e142..7d10e441b 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -325,15 +325,16 @@ message Command { ScheduleNexusOperationCommandAttributes schedule_nexus_operation_command_attributes = 18; RequestCancelNexusOperationCommandAttributes request_cancel_nexus_operation_command_attributes = 19; - AddStreamMessagesCommandAttributes add_stream_messages_command_attributes = 20; + AppendStreamRecordsCommandAttributes append_stream_records_command_attributes = 20; SubscribeStreamCommandAttributes subscribe_stream_command_attributes = 21; } } -// Appends to a stream the Workflow owns. Applied inside the Workflow Task's own -// commit. Produces one `WorkflowStreamMessagesAdded` event carrying the offset -// range and none of the payload; it schedules no further work. -message AddStreamMessagesCommandAttributes { +// Appends records to a stream the Workflow owns. Applied inside the Workflow +// Task's own commit. Produces one `WorkflowStreamRecordsAppended` event +// carrying the offset range and none of the payload; it schedules no further +// work. +message AppendStreamRecordsCommandAttributes { // Empty means the Workflow's default output stream. string stream_id = 1; // Stored in order. The server sets `producer_id` to empty on each record, diff --git a/temporal/api/enums/v1/command_type.proto b/temporal/api/enums/v1/command_type.proto index 677ed3b2d..967169b19 100644 --- a/temporal/api/enums/v1/command_type.proto +++ b/temporal/api/enums/v1/command_type.proto @@ -29,6 +29,6 @@ enum CommandType { COMMAND_TYPE_MODIFY_WORKFLOW_PROPERTIES = 16; COMMAND_TYPE_SCHEDULE_NEXUS_OPERATION = 17; COMMAND_TYPE_REQUEST_CANCEL_NEXUS_OPERATION = 18; - COMMAND_TYPE_ADD_STREAM_MESSAGES = 19; + COMMAND_TYPE_APPEND_STREAM_RECORDS = 19; COMMAND_TYPE_SUBSCRIBE_STREAM = 20; } diff --git a/temporal/api/enums/v1/event_type.proto b/temporal/api/enums/v1/event_type.proto index b515acb1d..a386b1db7 100644 --- a/temporal/api/enums/v1/event_type.proto +++ b/temporal/api/enums/v1/event_type.proto @@ -176,11 +176,11 @@ enum EventType { // An event that indicates time skipping advanced time or was disabled automatically after a bound was reached. EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED = 60; // A Workflow subscribed to a stream. Recorded once per subscription, not - // per message: the offsets a task consumed ride WorkflowTaskCompleted and + // per record: the offsets a task consumed ride WorkflowTaskCompleted and // the payloads never enter History at all. EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED = 61; - // A Workflow published a batch of messages to a stream. Recorded per + // A Workflow appended a batch of records to a stream. Recorded per // batch, and carrying only the offset range it landed at: the bodies go to // the stream's own log, never into History. - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED = 62; + EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED = 62; } diff --git a/temporal/api/enums/v1/failed_cause.proto b/temporal/api/enums/v1/failed_cause.proto index 989febb8e..16f876a87 100644 --- a/temporal/api/enums/v1/failed_cause.proto +++ b/temporal/api/enums/v1/failed_cause.proto @@ -90,8 +90,8 @@ enum WorkflowTaskFailedCause { WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED = 39; // A workflow task failed because the request exceeded a size limit. WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE = 40; - // A workflow task completed with an invalid AddStreamMessages command. - WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES = 41; + // A workflow task completed with an invalid AppendStreamRecords command. + WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES = 41; // A workflow task completed with an invalid SubscribeStream command. WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES = 42; // A workflow task could not be started because a stream range it consumed and recorded in diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 11023509e..cf5f944fe 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -384,9 +384,9 @@ message WorkflowTaskCompletedEventAttributes { // Offset ranges this Workflow Task consumed from streams it subscribes to. // Recorded on every task where a subscription is active, including when it // observed nothing: an empty range is a fact replay must reproduce, and - // omitting it would let replay deliver messages the Workflow did not have. + // omitting it would let replay deliver records the Workflow did not have. // Numbered 20 to leave 14 through 19 free for fields added on the main line. - repeated temporal.api.stream.v1.StreamCursor stream_cursors = 20; + repeated temporal.api.stream.v1.StreamRange consumed_stream_ranges = 20; } message WorkflowTaskTimedOutEventAttributes { @@ -976,18 +976,18 @@ message WorkflowStreamSubscribedEventAttributes { int64 start_offset = 3; } -message WorkflowStreamMessagesAddedEventAttributes { - // The WorkflowTaskCompleted event of the task whose command published this +message WorkflowStreamRecordsAppendedEventAttributes { + // The WorkflowTaskCompleted event of the task whose command appended this // batch. int64 workflow_task_completed_event_id = 1; - // Stream the Workflow published to. + // Stream the Workflow appended to. string stream_id = 2; - // Offset the first message of the batch landed at. + // Offset the first record of the batch landed at. int64 first_offset = 3; - // How many messages the batch held. With first_offset this names the range + // How many records the batch held. With first_offset this names the range // without carrying any of it, which is what keeps this event a fixed size // no matter how large the batch or its payloads are. - int64 message_count = 4; + int64 record_count = 4; } message WorkflowExecutionUpdateAcceptedEventAttributes { @@ -1314,7 +1314,7 @@ message HistoryEvent { WorkflowExecutionUnpausedEventAttributes workflow_execution_unpaused_event_attributes = 64; WorkflowExecutionTimeSkippingTransitionedEventAttributes workflow_execution_time_skipping_transitioned_event_attributes = 65; WorkflowStreamSubscribedEventAttributes workflow_stream_subscribed_event_attributes = 66; - WorkflowStreamMessagesAddedEventAttributes workflow_stream_messages_added_event_attributes = 67; + WorkflowStreamRecordsAppendedEventAttributes workflow_stream_records_appended_event_attributes = 67; } } diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto index d470009e4..9f0c88dfb 100644 --- a/temporal/api/stream/v1/message.proto +++ b/temporal/api/stream/v1/message.proto @@ -47,8 +47,8 @@ message StreamSlice { // which is a fact replay has to reproduce rather than an absence of one. int64 to_offset = 4; repeated StreamRecord records = 5; - // The WorkflowTaskCompleted event whose stream_cursors recorded this - // range. Set only when the server is re-supplying a range for a task + // The WorkflowTaskCompleted event whose consumed_stream_ranges recorded + // this range. Set only when the server is re-supplying a range for a task // being replayed; a slice for the task now being started leaves it unset, // because the event closing that task does not exist yet. // @@ -61,7 +61,7 @@ message StreamSlice { // The offsets a Workflow Task consumed, without the payloads. Recorded on // WorkflowTaskCompleted so History grows with Workflow Tasks rather than with // records. -message StreamCursor { +message StreamRange { string stream_id = 1; // Inclusive. int64 from_offset = 2; From 2cffd47fac0d62201cd26a128da4fb0b189b1977 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Mon, 21 Sep 2026 01:23:09 -0700 Subject: [PATCH 09/30] Regenerated the OpenAPI specs. --- openapi/openapiv2.json | 82 +++++++++++++++++++++++++++-------------- openapi/openapiv3.yaml | 83 +++++++++++++++++++++++++++--------------- 2 files changed, 107 insertions(+), 58 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 599bb39f1..ec6248d92 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -16118,10 +16118,10 @@ "EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED", "EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED", "EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED", - "EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED" + "EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED" ], "default": "EVENT_TYPE_UNSPECIFIED", - "description": "- EVENT_TYPE_UNSPECIFIED: Place holder and should never appear in a Workflow execution history\n - EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: Workflow execution has been triggered/started\nIt contains Workflow execution inputs, as well as Workflow timeout configurations\n - EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED: Workflow execution has successfully completed and contains Workflow execution results\n - EVENT_TYPE_WORKFLOW_EXECUTION_FAILED: Workflow execution has unsuccessfully completed and contains the Workflow execution error\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT: Workflow execution has timed out by the Temporal Server\nUsually due to the Workflow having not been completed within timeout settings\n - EVENT_TYPE_WORKFLOW_TASK_SCHEDULED: Workflow Task has been scheduled and the SDK client should now be able to process any new history events\n - EVENT_TYPE_WORKFLOW_TASK_STARTED: Workflow Task has started and the SDK client has picked up the Workflow Task and is processing new history events\n - EVENT_TYPE_WORKFLOW_TASK_COMPLETED: Workflow Task has completed\nThe SDK client picked up the Workflow Task and processed new history events\nSDK client may or may not ask the Temporal Server to do additional work, such as:\nEVENT_TYPE_ACTIVITY_TASK_SCHEDULED\nEVENT_TYPE_TIMER_STARTED\nEVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES\nEVENT_TYPE_MARKER_RECORDED\nEVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED\nEVENT_TYPE_WORKFLOW_EXECUTION_FAILED\nEVENT_TYPE_WORKFLOW_EXECUTION_CANCELED\nEVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW\n - EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: Workflow Task encountered a timeout\nEither an SDK client with a local cache was not available at the time, or it took too long for the SDK client to process the task\n - EVENT_TYPE_WORKFLOW_TASK_FAILED: Workflow Task encountered a failure\nUsually this means that the Workflow was non-deterministic\nHowever, the Workflow reset functionality also uses this event\n - EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: Activity Task was scheduled\nThe SDK client should pick up this activity task and execute\nThis event type contains activity inputs, as well as activity timeout configurations\n - EVENT_TYPE_ACTIVITY_TASK_STARTED: Activity Task has started executing\nThe SDK client has picked up the Activity Task and is processing the Activity invocation\n - EVENT_TYPE_ACTIVITY_TASK_COMPLETED: Activity Task has finished successfully\nThe SDK client has picked up and successfully completed the Activity Task\nThis event type contains Activity execution results\n - EVENT_TYPE_ACTIVITY_TASK_FAILED: Activity Task has finished unsuccessfully\nThe SDK picked up the Activity Task but unsuccessfully completed it\nThis event type contains Activity execution errors\n - EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT: Activity has timed out according to the Temporal Server\nActivity did not complete within the timeout settings\n - EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED: A request to cancel the Activity has occurred\nThe SDK client will be able to confirm cancellation of an Activity during an Activity heartbeat\n - EVENT_TYPE_ACTIVITY_TASK_CANCELED: Activity has been cancelled\n - EVENT_TYPE_TIMER_STARTED: A timer has started\n - EVENT_TYPE_TIMER_FIRED: A timer has fired\n - EVENT_TYPE_TIMER_CANCELED: A time has been cancelled\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED: A request has been made to cancel the Workflow execution\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: SDK client has confirmed the cancellation request and the Workflow execution has been cancelled\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Workflow has requested that the Temporal Server try to cancel another Workflow\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server could not cancel the targeted Workflow\nThis is usually because the target Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED: Temporal Server has successfully requested the cancellation of the target Workflow\n - EVENT_TYPE_MARKER_RECORDED: A marker has been recorded.\nThis event type is transparent to the Temporal Server\nThe Server will only store it and will not try to understand it.\n - EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: Workflow has received a Signal event\nThe event type contains the Signal name, as well as a Signal payload\n - EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: Workflow execution has been forcefully terminated\nThis is usually because the terminate Workflow API was called\n - EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: Workflow has successfully completed and a new Workflow has been started within the same transaction\nContains last Workflow execution results as well as new Workflow execution inputs\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to start a child Workflow\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution cannot be started/triggered\nUsually due to a child Workflow ID collision\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED: Child Workflow execution has successfully started/triggered\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED: Child Workflow execution has successfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution has unsuccessfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED: Child Workflow execution has been cancelled\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT: Child Workflow execution has timed out by the Temporal Server\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED: Child Workflow execution has been terminated\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to Signal the targeted Workflow\nContains the Signal name, as well as a Signal payload\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server cannot Signal the targeted Workflow\nUsually because the Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED: Temporal Server has successfully Signaled the targeted Workflow\n - EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: Workflow search attributes should be updated and synchronized with the visibility store\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: An update was admitted. Note that not all admitted updates result in this\nevent. See UpdateAdmittedEventOrigin for situations in which this event\nis created.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED: An update was accepted (i.e. passed validation, perhaps because no validator was defined)\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REJECTED: This event is never written to history.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED: An update completed\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of the workflow as a whole have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of an already-scheduled activity have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: Workflow properties modified by user workflow code\n - EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: A Nexus operation was scheduled using a ScheduleNexusOperation command.\n - EVENT_TYPE_NEXUS_OPERATION_STARTED: An asynchronous Nexus operation was started by a Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_COMPLETED: A Nexus operation completed successfully.\n - EVENT_TYPE_NEXUS_OPERATION_FAILED: A Nexus operation failed.\n - EVENT_TYPE_NEXUS_OPERATION_CANCELED: A Nexus operation completed as canceled.\n - EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: A Nexus operation timed out.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: A Nexus operation was requested to be canceled using a RequestCancelNexusOperation command.\n - EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: Workflow execution options updated by user.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED: A cancellation request for a Nexus operation was successfully delivered to the Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED: A cancellation request for a Nexus operation resulted in an error.\n - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED: An event that indicates that the workflow execution has been paused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED: An event that indicates that the previously paused workflow execution has been unpaused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED: An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.\n - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: A Workflow subscribed to a stream. Recorded once per subscription, not\nper message: the offsets a task consumed ride WorkflowTaskCompleted and\nthe payloads never enter History at all.\n - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED: A Workflow published a batch of messages to a stream. Recorded per\nbatch, and carrying only the offset range it landed at: the bodies go to\nthe stream's own log, never into History.", + "description": "- EVENT_TYPE_UNSPECIFIED: Place holder and should never appear in a Workflow execution history\n - EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: Workflow execution has been triggered/started\nIt contains Workflow execution inputs, as well as Workflow timeout configurations\n - EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED: Workflow execution has successfully completed and contains Workflow execution results\n - EVENT_TYPE_WORKFLOW_EXECUTION_FAILED: Workflow execution has unsuccessfully completed and contains the Workflow execution error\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT: Workflow execution has timed out by the Temporal Server\nUsually due to the Workflow having not been completed within timeout settings\n - EVENT_TYPE_WORKFLOW_TASK_SCHEDULED: Workflow Task has been scheduled and the SDK client should now be able to process any new history events\n - EVENT_TYPE_WORKFLOW_TASK_STARTED: Workflow Task has started and the SDK client has picked up the Workflow Task and is processing new history events\n - EVENT_TYPE_WORKFLOW_TASK_COMPLETED: Workflow Task has completed\nThe SDK client picked up the Workflow Task and processed new history events\nSDK client may or may not ask the Temporal Server to do additional work, such as:\nEVENT_TYPE_ACTIVITY_TASK_SCHEDULED\nEVENT_TYPE_TIMER_STARTED\nEVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES\nEVENT_TYPE_MARKER_RECORDED\nEVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED\nEVENT_TYPE_WORKFLOW_EXECUTION_FAILED\nEVENT_TYPE_WORKFLOW_EXECUTION_CANCELED\nEVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW\n - EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: Workflow Task encountered a timeout\nEither an SDK client with a local cache was not available at the time, or it took too long for the SDK client to process the task\n - EVENT_TYPE_WORKFLOW_TASK_FAILED: Workflow Task encountered a failure\nUsually this means that the Workflow was non-deterministic\nHowever, the Workflow reset functionality also uses this event\n - EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: Activity Task was scheduled\nThe SDK client should pick up this activity task and execute\nThis event type contains activity inputs, as well as activity timeout configurations\n - EVENT_TYPE_ACTIVITY_TASK_STARTED: Activity Task has started executing\nThe SDK client has picked up the Activity Task and is processing the Activity invocation\n - EVENT_TYPE_ACTIVITY_TASK_COMPLETED: Activity Task has finished successfully\nThe SDK client has picked up and successfully completed the Activity Task\nThis event type contains Activity execution results\n - EVENT_TYPE_ACTIVITY_TASK_FAILED: Activity Task has finished unsuccessfully\nThe SDK picked up the Activity Task but unsuccessfully completed it\nThis event type contains Activity execution errors\n - EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT: Activity has timed out according to the Temporal Server\nActivity did not complete within the timeout settings\n - EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED: A request to cancel the Activity has occurred\nThe SDK client will be able to confirm cancellation of an Activity during an Activity heartbeat\n - EVENT_TYPE_ACTIVITY_TASK_CANCELED: Activity has been cancelled\n - EVENT_TYPE_TIMER_STARTED: A timer has started\n - EVENT_TYPE_TIMER_FIRED: A timer has fired\n - EVENT_TYPE_TIMER_CANCELED: A time has been cancelled\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED: A request has been made to cancel the Workflow execution\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: SDK client has confirmed the cancellation request and the Workflow execution has been cancelled\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Workflow has requested that the Temporal Server try to cancel another Workflow\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server could not cancel the targeted Workflow\nThis is usually because the target Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED: Temporal Server has successfully requested the cancellation of the target Workflow\n - EVENT_TYPE_MARKER_RECORDED: A marker has been recorded.\nThis event type is transparent to the Temporal Server\nThe Server will only store it and will not try to understand it.\n - EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: Workflow has received a Signal event\nThe event type contains the Signal name, as well as a Signal payload\n - EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: Workflow execution has been forcefully terminated\nThis is usually because the terminate Workflow API was called\n - EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: Workflow has successfully completed and a new Workflow has been started within the same transaction\nContains last Workflow execution results as well as new Workflow execution inputs\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to start a child Workflow\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution cannot be started/triggered\nUsually due to a child Workflow ID collision\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED: Child Workflow execution has successfully started/triggered\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED: Child Workflow execution has successfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution has unsuccessfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED: Child Workflow execution has been cancelled\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT: Child Workflow execution has timed out by the Temporal Server\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED: Child Workflow execution has been terminated\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to Signal the targeted Workflow\nContains the Signal name, as well as a Signal payload\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server cannot Signal the targeted Workflow\nUsually because the Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED: Temporal Server has successfully Signaled the targeted Workflow\n - EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: Workflow search attributes should be updated and synchronized with the visibility store\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: An update was admitted. Note that not all admitted updates result in this\nevent. See UpdateAdmittedEventOrigin for situations in which this event\nis created.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED: An update was accepted (i.e. passed validation, perhaps because no validator was defined)\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REJECTED: This event is never written to history.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED: An update completed\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of the workflow as a whole have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of an already-scheduled activity have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: Workflow properties modified by user workflow code\n - EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: A Nexus operation was scheduled using a ScheduleNexusOperation command.\n - EVENT_TYPE_NEXUS_OPERATION_STARTED: An asynchronous Nexus operation was started by a Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_COMPLETED: A Nexus operation completed successfully.\n - EVENT_TYPE_NEXUS_OPERATION_FAILED: A Nexus operation failed.\n - EVENT_TYPE_NEXUS_OPERATION_CANCELED: A Nexus operation completed as canceled.\n - EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: A Nexus operation timed out.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: A Nexus operation was requested to be canceled using a RequestCancelNexusOperation command.\n - EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: Workflow execution options updated by user.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED: A cancellation request for a Nexus operation was successfully delivered to the Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED: A cancellation request for a Nexus operation resulted in an error.\n - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED: An event that indicates that the workflow execution has been paused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED: An event that indicates that the previously paused workflow execution has been unpaused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED: An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.\n - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: A Workflow subscribed to a stream. Recorded once per subscription, not\nper record: the offsets a task consumed ride WorkflowTaskCompleted and\nthe payloads never enter History at all.\n - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED: A Workflow appended a batch of records to a stream. Recorded per\nbatch, and carrying only the offset range it landed at: the bodies go to\nthe stream's own log, never into History.", "title": "Whenever this list of events is changed do change the function shouldBufferEvent in mutableStateBuilder.go to make sure to do the correct event ordering" }, "v1Execution": { @@ -16792,8 +16792,8 @@ "workflowStreamSubscribedEventAttributes": { "$ref": "#/definitions/v1WorkflowStreamSubscribedEventAttributes" }, - "workflowStreamMessagesAddedEventAttributes": { - "$ref": "#/definitions/v1WorkflowStreamMessagesAddedEventAttributes" + "workflowStreamRecordsAppendedEventAttributes": { + "$ref": "#/definitions/v1WorkflowStreamRecordsAppendedEventAttributes" } }, "description": "History events are the method by which Temporal SDKs advance (or recreate) workflow state.\nSee the `EventType` enum for more info about what each event is for." @@ -20065,7 +20065,7 @@ } } }, - "v1StreamCursor": { + "v1StreamRange": { "type": "object", "properties": { "streamId": { @@ -20073,21 +20073,23 @@ }, "fromOffset": { "type": "string", - "format": "int64" + "format": "int64", + "description": "Inclusive." }, "toOffset": { "type": "string", - "format": "int64" + "format": "int64", + "description": "Exclusive." } }, - "description": "The offsets a Workflow Task consumed, without the payloads. Recorded on\nWorkflowTaskCompleted so History grows with Workflow Tasks rather than with\nmessages." + "description": "The offsets a Workflow Task consumed, without the payloads. Recorded on\nWorkflowTaskCompleted so History grows with Workflow Tasks rather than with\nrecords." }, - "v1StreamMessage": { + "v1StreamRecord": { "type": "object", "properties": { "body": { "$ref": "#/definitions/v1Payload", - "description": "The message itself, stored as sent." + "description": "The value the producer published, stored as sent. A payload codec\napplies here as it does to any other payload." }, "metadata": { "type": "object", @@ -20100,12 +20102,36 @@ "type": "string", "description": "Producer-supplied grouping label, stored as sent." }, - "topicSequence": { + "kind": { + "$ref": "#/definitions/v1StreamRecordKind", + "description": "How to read this record. Unspecified is read as DATA." + }, + "producerId": { + "type": "string", + "description": "Who wrote the record. Empty when the owning Workflow did." + }, + "attempt": { + "type": "string", + "format": "int64", + "description": "The producer's attempt. Readers treat a later attempt by the same\nproducer as superseding what the earlier one wrote." + }, + "sequence": { "type": "string", "format": "int64", - "description": "Producer-supplied position within `topic`. The server stores it as sent\nand does not assign, validate or order by it." + "description": "The producer's position within its attempt, or -1 when unnumbered.\nStored as sent; the server does not assign, validate or order by it." } - } + }, + "description": "One entry in a stream. The record is the wire format: stores keep it\nserialized as is and readers in every language decode the same bytes." + }, + "v1StreamRecordKind": { + "type": "string", + "enum": [ + "STREAM_RECORD_KIND_UNSPECIFIED", + "STREAM_RECORD_KIND_DATA", + "STREAM_RECORD_KIND_FINISH" + ], + "default": "STREAM_RECORD_KIND_UNSPECIFIED", + "description": "What a record means to a reader. Kept on the record itself so every store\nand every language reads it the same way without a private envelope.\n\n - STREAM_RECORD_KIND_UNSPECIFIED: Read as DATA.\n - STREAM_RECORD_KIND_DATA: A value the producer published; `body` carries it.\n - STREAM_RECORD_KIND_FINISH: The producer named by `producer_id` writes nothing more on `topic`.\nSays nothing about that producer's outcome and does not end the stream." }, "v1StreamSlice": { "type": "object", @@ -20127,20 +20153,20 @@ "format": "int64", "description": "Exclusive. Equal to from_offset when the subscription observed nothing,\nwhich is a fact replay has to reproduce rather than an absence of one." }, - "messages": { + "records": { "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1StreamMessage" + "$ref": "#/definitions/v1StreamRecord" } }, "workflowTaskCompletedEventId": { "type": "string", "format": "int64", - "description": "The WorkflowTaskCompleted event whose stream_cursors recorded this range.\nSet only when the server is re-supplying a range for a task being\nreplayed; a slice for the task now being started leaves it unset, because\nthe event closing that task does not exist yet.\n\nReplay needs this because a Workflow Task response carries one slice set\nwhile a cache miss replays every prior task, so the ranges have to be\nmatched to the events that recorded them rather than to the response." + "description": "The WorkflowTaskCompleted event whose consumed_stream_ranges recorded\nthis range. Set only when the server is re-supplying a range for a task\nbeing replayed; a slice for the task now being started leaves it unset,\nbecause the event closing that task does not exist yet.\n\nReplay needs this because a Workflow Task response carries one slice set\nwhile a cache miss replays every prior task, so the ranges have to be\nmatched to the events that recorded them rather than to the response." } }, - "description": "A contiguous range of a stream delivered to a Workflow Task, along with the\noffsets it covers. The offsets are what History records; the messages\nthemselves are never written to History." + "description": "A contiguous range of a stream delivered to a Workflow Task, along with the\noffsets it covers. The offsets are what History records; the records\nthemselves are never written to History." }, "v1StructuredCalendarSpec": { "type": "object", @@ -22518,27 +22544,27 @@ } } }, - "v1WorkflowStreamMessagesAddedEventAttributes": { + "v1WorkflowStreamRecordsAppendedEventAttributes": { "type": "object", "properties": { "workflowTaskCompletedEventId": { "type": "string", "format": "int64", - "description": "The WorkflowTaskCompleted event of the task whose command published this\nbatch." + "description": "The WorkflowTaskCompleted event of the task whose command appended this\nbatch." }, "streamId": { "type": "string", - "description": "Stream the Workflow published to." + "description": "Stream the Workflow appended to." }, "firstOffset": { "type": "string", "format": "int64", - "description": "Offset the first message of the batch landed at." + "description": "Offset the first record of the batch landed at." }, - "messageCount": { + "recordCount": { "type": "string", "format": "int64", - "description": "How many messages the batch held. With first_offset this names the range\nwithout carrying any of it, which is what keeps this event a fixed size\nno matter how large the batch or its payloads are." + "description": "How many records the batch held. With first_offset this names the range\nwithout carrying any of it, which is what keeps this event a fixed size\nno matter how large the batch or its payloads are." } } }, @@ -22614,13 +22640,13 @@ "$ref": "#/definitions/v1WorkerDeploymentVersion", "description": "The Worker Deployment Version that completed this task. Must be set if `versioning_behavior`\nis set. This value updates workflow execution's `versioning_info.deployment_version`." }, - "streamCursors": { + "consumedStreamRanges": { "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1StreamCursor" + "$ref": "#/definitions/v1StreamRange" }, - "description": "Offset ranges this Workflow Task consumed from streams it subscribes to.\nRecorded on every task where a subscription is active, including when it\nobserved nothing: an empty range is a fact replay must reproduce, and\nomitting it would let replay deliver messages the Workflow did not have.\nNumbered 20 to leave 14 through 19 free for fields added on the main line." + "description": "Offset ranges this Workflow Task consumed from streams it subscribes to.\nRecorded on every task where a subscription is active, including when it\nobserved nothing: an empty range is a fact replay must reproduce, and\nomitting it would let replay deliver records the Workflow did not have.\nNumbered 20 to leave 14 through 19 free for fields added on the main line." } } }, @@ -22698,12 +22724,12 @@ "WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE", "WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED", "WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE", - "WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES", + "WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES", "WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES", "WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE" ], "default": "WORKFLOW_TASK_FAILED_CAUSE_UNSPECIFIED", - "description": "Workflow tasks can fail for various reasons. Note that some of these reasons can only originate\nfrom the server, and some of them can only originate from the SDK/worker.\n\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND: Between starting and completing the workflow task (with a workflow completion command), some\nnew command (like a signal) was processed into workflow history. The outstanding task will be\nfailed with this reason, and a worker must pick up a new task.\n - WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE: The worker wishes to fail the task and have the next one be generated on a normal, not sticky\nqueue. Generally workers should prefer to use the explicit `ResetStickyTaskQueue` RPC call.\n - WORKFLOW_TASK_FAILED_CAUSE_NON_DETERMINISTIC_ERROR: The worker encountered a mismatch while replaying history between what was expected, and\nwhat the workflow code actually did.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_CHILD_WORKFLOWS_LIMIT_EXCEEDED: We send the below error codes to users when their requests would violate a size constraint\nof their workflow. We do this to ensure that the state of their workflow does not become too\nlarge because that can cause severe performance degradation. You can modify the thresholds for\neach of these errors within your dynamic config.\n\nSpawning a new child workflow would cause this workflow to exceed its limit of pending child\nworkflows.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_ACTIVITIES_LIMIT_EXCEEDED: Starting a new activity would cause this workflow to exceed its limit of pending activities\nthat we track.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_SIGNALS_LIMIT_EXCEEDED: A workflow has a buffer of signals that have not yet reached their destination. We return this\nerror when sending a new signal would exceed the capacity of this buffer.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_REQUEST_CANCEL_LIMIT_EXCEEDED: Similarly, we have a buffer of pending requests to cancel other workflows. We return this error\nwhen our capacity for pending cancel requests is already reached.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UPDATE_WORKFLOW_EXECUTION_MESSAGE: Workflow execution update message (update.Acceptance, update.Rejection, or update.Response)\nhas wrong format, or missing required fields.\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_UPDATE: Similar to WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND, but for updates.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid ScheduleNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_NEXUS_OPERATIONS_LIMIT_EXCEEDED: A workflow task completed requesting to schedule a Nexus Operation exceeding the server configured limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid RequestCancelNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_FEATURE_DISABLED: A workflow task completed requesting a feature that's disabled on the server (either system wide or - typically -\nfor the workflow's namespace).\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE: A workflow task failed because a grpc message was too large.\n - WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE: A workflow task failed because payloads were too large.\n - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE: A workflow task failed because an external storage operation failed.\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED: A workflow task is failed because the workflow is paused before the task is started.\n - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE: A workflow task failed because the request exceeded a size limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES: A workflow task completed with an invalid AddStreamMessages command.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES: A workflow task completed with an invalid SubscribeStream command.\n - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE: A workflow task could not be started because a stream range it consumed and recorded in\nHistory can no longer be served, for example after truncation or because it exceeds the\nreplay bound. Check the workflow task failure message for more information." + "description": "Workflow tasks can fail for various reasons. Note that some of these reasons can only originate\nfrom the server, and some of them can only originate from the SDK/worker.\n\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND: Between starting and completing the workflow task (with a workflow completion command), some\nnew command (like a signal) was processed into workflow history. The outstanding task will be\nfailed with this reason, and a worker must pick up a new task.\n - WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE: The worker wishes to fail the task and have the next one be generated on a normal, not sticky\nqueue. Generally workers should prefer to use the explicit `ResetStickyTaskQueue` RPC call.\n - WORKFLOW_TASK_FAILED_CAUSE_NON_DETERMINISTIC_ERROR: The worker encountered a mismatch while replaying history between what was expected, and\nwhat the workflow code actually did.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_CHILD_WORKFLOWS_LIMIT_EXCEEDED: We send the below error codes to users when their requests would violate a size constraint\nof their workflow. We do this to ensure that the state of their workflow does not become too\nlarge because that can cause severe performance degradation. You can modify the thresholds for\neach of these errors within your dynamic config.\n\nSpawning a new child workflow would cause this workflow to exceed its limit of pending child\nworkflows.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_ACTIVITIES_LIMIT_EXCEEDED: Starting a new activity would cause this workflow to exceed its limit of pending activities\nthat we track.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_SIGNALS_LIMIT_EXCEEDED: A workflow has a buffer of signals that have not yet reached their destination. We return this\nerror when sending a new signal would exceed the capacity of this buffer.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_REQUEST_CANCEL_LIMIT_EXCEEDED: Similarly, we have a buffer of pending requests to cancel other workflows. We return this error\nwhen our capacity for pending cancel requests is already reached.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UPDATE_WORKFLOW_EXECUTION_MESSAGE: Workflow execution update message (update.Acceptance, update.Rejection, or update.Response)\nhas wrong format, or missing required fields.\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_UPDATE: Similar to WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND, but for updates.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid ScheduleNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_NEXUS_OPERATIONS_LIMIT_EXCEEDED: A workflow task completed requesting to schedule a Nexus Operation exceeding the server configured limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid RequestCancelNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_FEATURE_DISABLED: A workflow task completed requesting a feature that's disabled on the server (either system wide or - typically -\nfor the workflow's namespace).\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE: A workflow task failed because a grpc message was too large.\n - WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE: A workflow task failed because payloads were too large.\n - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE: A workflow task failed because an external storage operation failed.\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED: A workflow task is failed because the workflow is paused before the task is started.\n - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE: A workflow task failed because the request exceeded a size limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES: A workflow task completed with an invalid AppendStreamRecords command.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES: A workflow task completed with an invalid SubscribeStream command.\n - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE: A workflow task could not be started because a stream range it consumed and recorded in\nHistory can no longer be served, for example after truncation or because it exceeds the\nreplay bound. Check the workflow task failure message for more information." }, "v1WorkflowTaskFailedEventAttributes": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 92c4b24f1..5c4fdd8bc 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -12879,7 +12879,7 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED + - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED type: string format: enum version: @@ -13047,8 +13047,8 @@ components: $ref: '#/components/schemas/WorkflowExecutionTimeSkippingTransitionedEventAttributes' workflowStreamSubscribedEventAttributes: $ref: '#/components/schemas/WorkflowStreamSubscribedEventAttributes' - workflowStreamMessagesAddedEventAttributes: - $ref: '#/components/schemas/WorkflowStreamMessagesAddedEventAttributes' + workflowStreamRecordsAppendedEventAttributes: + $ref: '#/components/schemas/WorkflowStreamRecordsAppendedEventAttributes' description: |- History events are the method by which Temporal SDKs advance (or recreate) workflow state. See the `EventType` enum for more info about what each event is for. @@ -15543,7 +15543,7 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED + - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED type: string description: The event type of the history event generated by the request. format: enum @@ -17695,26 +17695,30 @@ components: type: type: string description: The type of the driver, required. - StreamCursor: + StreamRange: type: object properties: streamId: type: string fromOffset: type: string + description: Inclusive. toOffset: type: string + description: Exclusive. description: |- The offsets a Workflow Task consumed, without the payloads. Recorded on WorkflowTaskCompleted so History grows with Workflow Tasks rather than with - messages. - StreamMessage: + records. + StreamRecord: type: object properties: body: allOf: - $ref: '#/components/schemas/Payload' - description: The message itself, stored as sent. + description: |- + The value the producer published, stored as sent. A payload codec + applies here as it does to any other payload. metadata: type: object additionalProperties: @@ -17723,11 +17727,30 @@ components: topic: type: string description: Producer-supplied grouping label, stored as sent. - topicSequence: + kind: + enum: + - STREAM_RECORD_KIND_UNSPECIFIED + - STREAM_RECORD_KIND_DATA + - STREAM_RECORD_KIND_FINISH + type: string + description: How to read this record. Unspecified is read as DATA. + format: enum + producerId: + type: string + description: Who wrote the record. Empty when the owning Workflow did. + attempt: + type: string + description: |- + The producer's attempt. Readers treat a later attempt by the same + producer as superseding what the earlier one wrote. + sequence: type: string description: |- - Producer-supplied position within `topic`. The server stores it as sent - and does not assign, validate or order by it. + The producer's position within its attempt, or -1 when unnumbered. + Stored as sent; the server does not assign, validate or order by it. + description: |- + One entry in a stream. The record is the wire format: stores keep it + serialized as is and readers in every language decode the same bytes. StreamSlice: type: object properties: @@ -17746,24 +17769,24 @@ components: description: |- Exclusive. Equal to from_offset when the subscription observed nothing, which is a fact replay has to reproduce rather than an absence of one. - messages: + records: type: array items: - $ref: '#/components/schemas/StreamMessage' + $ref: '#/components/schemas/StreamRecord' workflowTaskCompletedEventId: type: string description: |- - The WorkflowTaskCompleted event whose stream_cursors recorded this range. - Set only when the server is re-supplying a range for a task being - replayed; a slice for the task now being started leaves it unset, because - the event closing that task does not exist yet. + The WorkflowTaskCompleted event whose consumed_stream_ranges recorded + this range. Set only when the server is re-supplying a range for a task + being replayed; a slice for the task now being started leaves it unset, + because the event closing that task does not exist yet. Replay needs this because a Workflow Task response carries one slice set while a cache miss replays every prior task, so the ranges have to be matched to the events that recorded them rather than to the response. description: |- A contiguous range of a stream delivered to a Workflow Task, along with the - offsets it covers. The offsets are what History records; the messages + offsets it covers. The offsets are what History records; the records themselves are never written to History. StructuredCalendarSpec: type: object @@ -19916,7 +19939,7 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED + - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED type: string format: enum description: EventReference is a direct reference to a history event through the event ID. @@ -19989,7 +20012,7 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - - EVENT_TYPE_WORKFLOW_STREAM_MESSAGES_ADDED + - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED type: string format: enum description: RequestIdReference is a indirect reference to a history event through the request ID. @@ -21112,24 +21135,24 @@ components: * BETWEEN ... AND STARTS_WITH description: Activity trigger will be triggered when an activity is about to start. - WorkflowStreamMessagesAddedEventAttributes: + WorkflowStreamRecordsAppendedEventAttributes: type: object properties: workflowTaskCompletedEventId: type: string description: |- - The WorkflowTaskCompleted event of the task whose command published this + The WorkflowTaskCompleted event of the task whose command appended this batch. streamId: type: string - description: Stream the Workflow published to. + description: Stream the Workflow appended to. firstOffset: type: string - description: Offset the first message of the batch landed at. - messageCount: + description: Offset the first record of the batch landed at. + recordCount: type: string description: |- - How many messages the batch held. With first_offset this names the range + How many records the batch held. With first_offset this names the range without carrying any of it, which is what keeps this event a fixed size no matter how large the batch or its payloads are. WorkflowStreamSubscribedEventAttributes: @@ -21221,15 +21244,15 @@ components: description: |- The Worker Deployment Version that completed this task. Must be set if `versioning_behavior` is set. This value updates workflow execution's `versioning_info.deployment_version`. - streamCursors: + consumedStreamRanges: type: array items: - $ref: '#/components/schemas/StreamCursor' + $ref: '#/components/schemas/StreamRange' description: |- Offset ranges this Workflow Task consumed from streams it subscribes to. Recorded on every task where a subscription is active, including when it observed nothing: an empty range is a fact replay must reproduce, and - omitting it would let replay deliver messages the Workflow did not have. + omitting it would let replay deliver records the Workflow did not have. Numbered 20 to leave 14 through 19 free for fields added on the main line. WorkflowTaskCompletedMetadata: type: object @@ -21338,7 +21361,7 @@ components: - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE - - WORKFLOW_TASK_FAILED_CAUSE_BAD_ADD_STREAM_MESSAGES_ATTRIBUTES + - WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE type: string From f2156b1851b6df299e3b1a98fb930256fbd6d561 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 25 Sep 2026 15:29:27 -0700 Subject: [PATCH 10/30] Named the stream command fields for what they address. The append command only ever names a stream the workflow owns, and the subscribe command takes a name or an id and resolves them in that order. Calling both of them stream_id told a reader the wrong thing. --- temporal/api/command/v1/message.proto | 31 +++++++++++++++++---------- temporal/api/history/v1/message.proto | 4 +++- temporal/api/stream/v1/message.proto | 6 ++++++ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 7d10e441b..890e79e62 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -335,8 +335,11 @@ message Command { // carrying the offset range and none of the payload; it schedules no further // work. message AppendStreamRecordsCommandAttributes { - // Empty means the Workflow's default output stream. - string stream_id = 1; + // Name of a stream this Workflow owns, scoped to the Workflow. Created on + // first use. Empty means the Workflow's default output stream. A Workflow + // cannot append to a stream in another execution, so this is never the id + // of a standalone stream. + string stream_name = 1; // Stored in order. The server sets `producer_id` to empty on each record, // because the owning Workflow is the producer here. repeated temporal.api.stream.v1.StreamRecord records = 2; @@ -349,14 +352,20 @@ message AppendStreamRecordsCommandAttributes { // A Workflow cannot look it up without doing I/O, and a value it carried would // be a reading rather than a fact, so it could differ on replay. message SubscribeStreamCommandAttributes { - // Stream to consume. A stream in another execution is addressed by its id; - // one this Workflow owns is addressed by the name it was published under. - // The server resolves an owned name first and falls back to a standalone - // id, so a Workflow that owns a stream under this name cannot reach a - // standalone stream with the same id. - string stream_id = 1; - // Where to start. Negative means from wherever the stream is when the - // subscription is registered, which the server resolves and records so - // replay does not resolve it again. + // Stream to consume, named either way round: a stream this Workflow owns + // by the name it appends under, a stream in another execution by its id. + // The server tries them in that order, so a Workflow that owns a stream + // under this name cannot reach a standalone stream with the same id. When + // neither exists the Workflow gets a stream of its own by that name, which + // is how a reader subscribes before the first record is written. + string stream_name_or_id = 1; + // Where to start, as an absolute offset. Any negative value means the head + // of the stream as of registration, and they all mean the same thing. The + // server resolves it and records the result, so replay does not resolve it + // again. + // + // There is no way to ask for the earliest readable offset of a stream that + // has been truncated. Reading it from DescribeStream and passing it here + // races with further truncation. int64 start_offset = 2; } diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index cf5f944fe..9de884c47 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -967,7 +967,9 @@ message WorkflowStreamSubscribedEventAttributes { // The WorkflowTaskCompleted event of the task whose command created this // subscription. int64 workflow_task_completed_event_id = 1; - // Stream the Workflow subscribed to. + // The stream the Workflow subscribed to, as the command addressed it: + // either the name of a stream this Workflow owns or the id of one in + // another execution. string stream_id = 2; // The offset the subscription actually starts from. Resolved by the server // when the subscription is registered and recorded here, so replay reads diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto index 9f0c88dfb..bfe97e1df 100644 --- a/temporal/api/stream/v1/message.proto +++ b/temporal/api/stream/v1/message.proto @@ -37,6 +37,9 @@ message StreamRecord { // offsets it covers. The offsets are what History records; the records // themselves are never written to History. message StreamSlice { + // The stream, as the subscribing command addressed it: either the name of + // a stream the consuming Workflow owns or the id of one in another + // execution. string stream_id = 1; // Run id of the execution that owns the stream. Set on both a slice for the // task being started and a re-supplied one. @@ -62,6 +65,9 @@ message StreamSlice { // WorkflowTaskCompleted so History grows with Workflow Tasks rather than with // records. message StreamRange { + // The stream, as the subscribing command addressed it: either the name of + // a stream the consuming Workflow owns or the id of one in another + // execution. string stream_id = 1; // Inclusive. int64 from_offset = 2; From b3947f9401cb3975ba1c0a6fab4711a90cbbcaf3 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 25 Sep 2026 15:29:36 -0700 Subject: [PATCH 11/30] Reserved the history numbers held for the main line. A comment saying 14 through 19 are left free stops nothing. Reserving them makes a rebase that lands an upstream field there fail loudly instead of colliding at 20 later. --- temporal/api/history/v1/message.proto | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 9de884c47..6d5124748 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -385,8 +385,11 @@ message WorkflowTaskCompletedEventAttributes { // Recorded on every task where a subscription is active, including when it // observed nothing: an empty range is a fact replay must reproduce, and // omitting it would let replay deliver records the Workflow did not have. - // Numbered 20 to leave 14 through 19 free for fields added on the main line. repeated temporal.api.stream.v1.StreamRange consumed_stream_ranges = 20; + + // Held for fields added on the main line, so a rebase does not land one of + // them on a number this fork already writes. + reserved 14 to 19; } message WorkflowTaskTimedOutEventAttributes { From 92b0399609d39ebd258358c782bd7bcbee5a23e1 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 25 Sep 2026 15:29:36 -0700 Subject: [PATCH 12/30] Gave the appended event the range fields every range has. A batch was named by a first offset and a count while StreamRange and StreamSlice bound the same idea with from_offset and to_offset. One vocabulary for one concept. The path-wide linter exemption gives way to the per-field disables the rest of this API uses. --- api-linter.yaml | 5 ----- temporal/api/history/v1/message.proto | 20 +++++++++++++------- temporal/api/stream/v1/message.proto | 8 ++++++++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/api-linter.yaml b/api-linter.yaml index 7717df60e..290842854 100644 --- a/api-linter.yaml +++ b/api-linter.yaml @@ -12,11 +12,6 @@ disabled_rules: - "core::0123::resource-annotation" # We don't require resource annotations on all messages -- https://linter.aip.dev/123/resource-annotation -- included_paths: - - "**/stream/v1/message.proto" - disabled_rules: - - "core::0140::prepositions" # Half-open offset ranges on StreamSlice and StreamRange are spelled `from_offset`/`to_offset`, as in the server's own protos -- https://linter.aip.dev/140/prepositions - - included_paths: - "**/workflowservice/v1/request_response.proto" - "**/operatorservice/v1/request_response.proto" diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 6d5124748..2f432020d 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -985,14 +985,20 @@ message WorkflowStreamRecordsAppendedEventAttributes { // The WorkflowTaskCompleted event of the task whose command appended this // batch. int64 workflow_task_completed_event_id = 1; - // Stream the Workflow appended to. + // Name of the stream the Workflow appended to. string stream_id = 2; - // Offset the first record of the batch landed at. - int64 first_offset = 3; - // How many records the batch held. With first_offset this names the range - // without carrying any of it, which is what keeps this event a fixed size - // no matter how large the batch or its payloads are. - int64 record_count = 4; + // Inclusive. Same range vocabulary as StreamRange and StreamSlice, so a + // reader does not have to remember which of the three counts and which + // bounds. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) + int64 from_offset = 3; + // Exclusive. With from_offset this names the range without carrying any of + // it, which is what keeps this event a fixed size no matter how large the + // batch or its payloads are. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) + int64 to_offset = 4; } message WorkflowExecutionUpdateAcceptedEventAttributes { diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto index bfe97e1df..995fddb4f 100644 --- a/temporal/api/stream/v1/message.proto +++ b/temporal/api/stream/v1/message.proto @@ -45,9 +45,13 @@ message StreamSlice { // task being started and a re-supplied one. string run_id = 2; // Inclusive. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) int64 from_offset = 3; // Exclusive. Equal to from_offset when the subscription observed nothing, // which is a fact replay has to reproduce rather than an absence of one. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) int64 to_offset = 4; repeated StreamRecord records = 5; // The WorkflowTaskCompleted event whose consumed_stream_ranges recorded @@ -70,8 +74,12 @@ message StreamRange { // execution. string stream_id = 1; // Inclusive. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) int64 from_offset = 2; // Exclusive. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) int64 to_offset = 3; } From 9df53d903fcd980cffe9c86dbc61e9585654026e Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 25 Sep 2026 15:29:45 -0700 Subject: [PATCH 13/30] Dropped the unnumbered sequence sentinel. An unset int64 reads as zero, so a record whose producer does not number its records claimed position zero while the comment reserved -1 for that case. Zero is now what it looks like, and the stream's offsets are what order a read. --- temporal/api/stream/v1/message.proto | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto index 995fddb4f..6b5933420 100644 --- a/temporal/api/stream/v1/message.proto +++ b/temporal/api/stream/v1/message.proto @@ -28,8 +28,9 @@ message StreamRecord { // The producer's attempt. Readers treat a later attempt by the same // producer as superseding what the earlier one wrote. int64 attempt = 6; - // The producer's position within its attempt, or -1 when unnumbered. - // Stored as sent; the server does not assign, validate or order by it. + // The producer's position within its attempt, zero when it does not number + // its records. Stored as sent; the server does not assign, validate or + // order by it, and the stream's own offsets are what order a read. int64 sequence = 7; } From 35f43bd4e9a8ddd418f0564879e03de81b6543c9 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 25 Sep 2026 15:29:45 -0700 Subject: [PATCH 14/30] Scoped the payload codec claim to the paths this API owns. A codec reaches records on the append command and on the Workflow Task response, both of which this API's payload visitor walks. It does not reach the stream service, whose messages live outside this module. --- temporal/api/stream/v1/message.proto | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto index 6b5933420..868c9fce6 100644 --- a/temporal/api/stream/v1/message.proto +++ b/temporal/api/stream/v1/message.proto @@ -14,8 +14,13 @@ import "temporal/api/common/v1/message.proto"; // One entry in a stream. The record is the wire format: stores keep it // serialized as is and readers in every language decode the same bytes. message StreamRecord { - // The value the producer published, stored as sent. A payload codec - // applies here as it does to any other payload. + // The value the producer published, stored as sent. + // + // A payload codec applies on the paths this API owns: the append command on + // RespondWorkflowTaskCompleted, and the slices on PollWorkflowTaskQueue. + // Records a producer writes or reads through the stream service take a + // different path, whose messages are not part of this API yet and so are + // outside what a codec-applying proxy walks. temporal.api.common.v1.Payload body = 1; // Producer-supplied provenance, stored as sent. map metadata = 2; From 6e49c7b10f237800bca11b22f9969b47c4f345ac Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 25 Sep 2026 15:29:45 -0700 Subject: [PATCH 15/30] Regenerated the OpenAPI specs. --- openapi/openapiv2.json | 24 ++++++++------- openapi/openapiv3.yaml | 66 ++++++++++++++++++++++++++++++++---------- 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index ec6248d92..aba2a2e0f 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -20069,7 +20069,8 @@ "type": "object", "properties": { "streamId": { - "type": "string" + "type": "string", + "description": "The stream, as the subscribing command addressed it: either the name of\na stream the consuming Workflow owns or the id of one in another\nexecution." }, "fromOffset": { "type": "string", @@ -20089,7 +20090,7 @@ "properties": { "body": { "$ref": "#/definitions/v1Payload", - "description": "The value the producer published, stored as sent. A payload codec\napplies here as it does to any other payload." + "description": "The value the producer published, stored as sent.\n\nA payload codec applies on the paths this API owns: the append command on\nRespondWorkflowTaskCompleted, and the slices on PollWorkflowTaskQueue.\nRecords a producer writes or reads through the stream service take a\ndifferent path, whose messages are not part of this API yet and so are\noutside what a codec-applying proxy walks." }, "metadata": { "type": "object", @@ -20118,7 +20119,7 @@ "sequence": { "type": "string", "format": "int64", - "description": "The producer's position within its attempt, or -1 when unnumbered.\nStored as sent; the server does not assign, validate or order by it." + "description": "The producer's position within its attempt, zero when it does not number\nits records. Stored as sent; the server does not assign, validate or\norder by it, and the stream's own offsets are what order a read." } }, "description": "One entry in a stream. The record is the wire format: stores keep it\nserialized as is and readers in every language decode the same bytes." @@ -20137,7 +20138,8 @@ "type": "object", "properties": { "streamId": { - "type": "string" + "type": "string", + "description": "The stream, as the subscribing command addressed it: either the name of\na stream the consuming Workflow owns or the id of one in another\nexecution." }, "runId": { "type": "string", @@ -22554,17 +22556,17 @@ }, "streamId": { "type": "string", - "description": "Stream the Workflow appended to." + "description": "Name of the stream the Workflow appended to." }, - "firstOffset": { + "fromOffset": { "type": "string", "format": "int64", - "description": "Offset the first record of the batch landed at." + "description": "Inclusive. Same range vocabulary as StreamRange and StreamSlice, so a\nreader does not have to remember which of the three counts and which\nbounds." }, - "recordCount": { + "toOffset": { "type": "string", "format": "int64", - "description": "How many records the batch held. With first_offset this names the range\nwithout carrying any of it, which is what keeps this event a fixed size\nno matter how large the batch or its payloads are." + "description": "Exclusive. With from_offset this names the range without carrying any of\nit, which is what keeps this event a fixed size no matter how large the\nbatch or its payloads are." } } }, @@ -22578,7 +22580,7 @@ }, "streamId": { "type": "string", - "description": "Stream the Workflow subscribed to." + "description": "The stream the Workflow subscribed to, as the command addressed it:\neither the name of a stream this Workflow owns or the id of one in\nanother execution." }, "startOffset": { "type": "string", @@ -22646,7 +22648,7 @@ "type": "object", "$ref": "#/definitions/v1StreamRange" }, - "description": "Offset ranges this Workflow Task consumed from streams it subscribes to.\nRecorded on every task where a subscription is active, including when it\nobserved nothing: an empty range is a fact replay must reproduce, and\nomitting it would let replay deliver records the Workflow did not have.\nNumbered 20 to leave 14 through 19 free for fields added on the main line." + "description": "Offset ranges this Workflow Task consumed from streams it subscribes to.\nRecorded on every task where a subscription is active, including when it\nobserved nothing: an empty range is a fact replay must reproduce, and\nomitting it would let replay deliver records the Workflow did not have." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 5c4fdd8bc..770462047 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -17700,12 +17700,22 @@ components: properties: streamId: type: string + description: |- + The stream, as the subscribing command addressed it: either the name of + a stream the consuming Workflow owns or the id of one in another + execution. fromOffset: type: string - description: Inclusive. + description: |- + Inclusive. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) toOffset: type: string - description: Exclusive. + description: |- + Exclusive. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) description: |- The offsets a Workflow Task consumed, without the payloads. Recorded on WorkflowTaskCompleted so History grows with Workflow Tasks rather than with @@ -17717,8 +17727,13 @@ components: allOf: - $ref: '#/components/schemas/Payload' description: |- - The value the producer published, stored as sent. A payload codec - applies here as it does to any other payload. + The value the producer published, stored as sent. + + A payload codec applies on the paths this API owns: the append command on + RespondWorkflowTaskCompleted, and the slices on PollWorkflowTaskQueue. + Records a producer writes or reads through the stream service take a + different path, whose messages are not part of this API yet and so are + outside what a codec-applying proxy walks. metadata: type: object additionalProperties: @@ -17746,8 +17761,9 @@ components: sequence: type: string description: |- - The producer's position within its attempt, or -1 when unnumbered. - Stored as sent; the server does not assign, validate or order by it. + The producer's position within its attempt, zero when it does not number + its records. Stored as sent; the server does not assign, validate or + order by it, and the stream's own offsets are what order a read. description: |- One entry in a stream. The record is the wire format: stores keep it serialized as is and readers in every language decode the same bytes. @@ -17756,6 +17772,10 @@ components: properties: streamId: type: string + description: |- + The stream, as the subscribing command addressed it: either the name of + a stream the consuming Workflow owns or the id of one in another + execution. runId: type: string description: |- @@ -17763,12 +17783,17 @@ components: task being started and a re-supplied one. fromOffset: type: string - description: Inclusive. + description: |- + Inclusive. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) toOffset: type: string description: |- Exclusive. Equal to from_offset when the subscription observed nothing, which is a fact replay has to reproduce rather than an absence of one. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) records: type: array items: @@ -21145,16 +21170,23 @@ components: batch. streamId: type: string - description: Stream the Workflow appended to. - firstOffset: + description: Name of the stream the Workflow appended to. + fromOffset: type: string - description: Offset the first record of the batch landed at. - recordCount: + description: |- + Inclusive. Same range vocabulary as StreamRange and StreamSlice, so a + reader does not have to remember which of the three counts and which + bounds. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) + toOffset: type: string description: |- - How many records the batch held. With first_offset this names the range - without carrying any of it, which is what keeps this event a fixed size - no matter how large the batch or its payloads are. + Exclusive. With from_offset this names the range without carrying any of + it, which is what keeps this event a fixed size no matter how large the + batch or its payloads are. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "from" and "to" name a half-open offset range. --) WorkflowStreamSubscribedEventAttributes: type: object properties: @@ -21165,7 +21197,10 @@ components: subscription. streamId: type: string - description: Stream the Workflow subscribed to. + description: |- + The stream the Workflow subscribed to, as the command addressed it: + either the name of a stream this Workflow owns or the id of one in + another execution. startOffset: type: string description: |- @@ -21253,7 +21288,6 @@ components: Recorded on every task where a subscription is active, including when it observed nothing: an empty range is a fact replay must reproduce, and omitting it would let replay deliver records the Workflow did not have. - Numbered 20 to leave 14 through 19 free for fields added on the main line. WorkflowTaskCompletedMetadata: type: object properties: From 656645b86e742c4435dc7ad3bee958a5cbfd9803 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Mon, 28 Sep 2026 16:11:23 -0700 Subject: [PATCH 16/30] Added a start position to the subscribe command. Proto3 cannot tell an unset start offset from zero, and there was no way to ask for the earliest record of a truncated stream or the last N records. The position is a new message at a new field number, so the change is additive. --- temporal/api/command/v1/message.proto | 16 ++++++++-------- temporal/api/stream/v1/message.proto | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 890e79e62..256af4403 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -359,13 +359,13 @@ message SubscribeStreamCommandAttributes { // neither exists the Workflow gets a stream of its own by that name, which // is how a reader subscribes before the first record is written. string stream_name_or_id = 1; - // Where to start, as an absolute offset. Any negative value means the head - // of the stream as of registration, and they all mean the same thing. The - // server resolves it and records the result, so replay does not resolve it - // again. - // - // There is no way to ask for the earliest readable offset of a stream that - // has been truncated. Reading it from DescribeStream and passing it here - // races with further truncation. + // Where to start, as an absolute offset. Read only when `start_position` + // is unset. A negative value is refused: the head of the stream is asked + // for with `start_position.tail`. int64 start_offset = 2; + // Where to start. The server resolves it once, when it registers the + // subscription, and records the resolved absolute offset on the subscribed + // event, so replay does not resolve it again. Setting it together with a + // non-zero `start_offset` fails the command. + temporal.api.stream.v1.StreamStartPosition start_position = 3; } diff --git a/temporal/api/stream/v1/message.proto b/temporal/api/stream/v1/message.proto index 868c9fce6..e6a8fab2d 100644 --- a/temporal/api/stream/v1/message.proto +++ b/temporal/api/stream/v1/message.proto @@ -89,6 +89,25 @@ message StreamRange { int64 to_offset = 3; } +// Where a new subscription or read begins. The server resolves it against the +// stream as it stands in the same transaction that registers the reader, so +// the result does not race with appends or truncation, and records the +// resolved absolute offset. +message StreamStartPosition { + oneof position { + // Absolute and inclusive. Refused when below the stream's floor. + int64 offset = 1; + // The last N records the stream holds, or all of them when it holds + // fewer. Counts records of every kind. Must be positive. + int64 last_n = 2; + // The oldest record the stream still holds. Must be true. + bool earliest = 3; + // Only records appended after registration: the stream's head offset. + // Must be true. + bool tail = 4; + } +} + // What a record means to a reader. Kept on the record itself so every store // and every language reads it the same way without a private envelope. enum StreamRecordKind { From 75de7aa04902e7464c708bebb5e8a9296199a142 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Thu, 1 Oct 2026 10:51:42 -0700 Subject: [PATCH 17/30] Added the wake, a coalesced event-less Workflow Task request. A store Temporal does not host needs a way to make a consumer run a task without writing an event per append. Wakes fold by source, so a burst of writes costs one task. --- openapi/openapiv2.json | 152 ++++++++++++++++++ openapi/openapiv3.yaml | 152 ++++++++++++++++++ temporal/api/workflow/v1/message.proto | 23 +++ .../workflowservice/v1/request_response.proto | 27 ++++ temporal/api/workflowservice/v1/service.proto | 16 ++ 5 files changed, 370 insertions(+) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index aba2a2e0f..66cf54554 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -4575,6 +4575,51 @@ ] } }, + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/wake": { + "post": { + "summary": "WakeWorkflowExecution asks a running Workflow Execution to run a Workflow\nTask because a source it consumes has moved. Unlike a Signal it records\nno event, carries no payload, and folds with other wakes for the same\nsource, so a burst of writes costs one task. The Workflow learns the\nsource and its position from the task and reads the source itself.", + "operationId": "WakeWorkflowExecution2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1WakeWorkflowExecutionResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowExecution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceWakeWorkflowExecutionBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, "/api/v1/namespaces/{namespace}/workflows/{workflowId}": { "post": { "summary": "StartWorkflowExecution starts a new workflow execution.", @@ -10158,6 +10203,51 @@ ] } }, + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/wake": { + "post": { + "summary": "WakeWorkflowExecution asks a running Workflow Execution to run a Workflow\nTask because a source it consumes has moved. Unlike a Signal it records\nno event, carries no payload, and folds with other wakes for the same\nsource, so a burst of writes costs one task. The Workflow learns the\nsource and its position from the task and reads the source itself.", + "operationId": "WakeWorkflowExecution", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1WakeWorkflowExecutionResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowExecution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceWakeWorkflowExecutionBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, "/namespaces/{namespace}/workflows/{workflowId}": { "post": { "summary": "StartWorkflowExecution starts a new workflow execution.", @@ -13486,6 +13576,27 @@ }, "description": "Used to validate the compute config without attaching it to a Worker Deployment Version." }, + "WorkflowServiceWakeWorkflowExecutionBody": { + "type": "object", + "properties": { + "workflowExecution": { + "type": "object", + "properties": { + "runId": { + "type": "string" + } + }, + "description": "The Workflow to wake. `run_id` is optional. When set, the wake goes to\nthe current run of the chain that run belongs to, so a sender that\nlearned a run id before a continue-as-new still reaches the consumer,\nand it is refused with NotFound once that chain has ended. When unset,\nthe wake goes to the current run under the Workflow Id, whichever chain." + }, + "wake": { + "$ref": "#/definitions/v1Wake" + }, + "identity": { + "type": "string", + "description": "The identity of the sender, for metrics and logs." + } + } + }, "apiActivityV1CallbackInfo": { "type": "object", "properties": { @@ -18576,6 +18687,14 @@ "$ref": "#/definitions/v1StreamSlice" }, "description": "Stream data attached to this task. Delivered out of band so the payloads\nnever enter History; only the offset ranges are recorded there." + }, + "wakes": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Wake" + }, + "description": "Wakes pending for this execution when the task started, folded by\nsource. Not recorded in History and never re-supplied on replay: the\nWorkflow treats one as a reason to read its source now and records what\nit read itself." } } }, @@ -21066,6 +21185,39 @@ }, "description": "Specifies client's intent to wait for Update results." }, + "v1Wake": { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "What moved, as the sender names it. The server folds wakes by this value\nand never interprets it. For a stream, the provider formats the stream's\nidentity, owner and topic, into it." + }, + "position": { + "type": "string", + "format": "byte", + "description": "Where the source stands after the write that caused this wake, in the\nstore's own terms. Opaque to the server and handed to the Workflow as\nreceived. A later wake's position replaces an earlier one's." + }, + "counter": { + "type": "string", + "format": "int64", + "description": "Orders wakes from one source. Only the source's store can order its\npositions, so the sender derives this from the position and the server\ncompares nothing else. A wake whose counter is not above the one the\nserver already holds is folded." + } + }, + "description": "A wake tells a running Workflow Execution that something it consumes has\nmoved, so it should run a Workflow Task and read from its own cursor. It is\na reason to run, not data: nothing is recorded in History and no record\ntravels with it. A store Temporal does not host sends one after the store\nhas acknowledged a write. Wakes with the same source fold: the server keeps\nthe highest counter it has seen per execution and source, so a repeated,\nreordered or late wake changes nothing, and a burst of writes costs one task." + }, + "v1WakeWorkflowExecutionResponse": { + "type": "object", + "properties": { + "runId": { + "type": "string", + "description": "The run the wake was stored on." + }, + "folded": { + "type": "boolean", + "description": "True when the server already held this counter or a higher one for the\nsource, so the wake changed nothing." + } + } + }, "v1WorkerConfig": { "type": "object", "properties": { diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 770462047..5f2515bf7 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -4678,6 +4678,47 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake: + post: + tags: + - WorkflowService + description: |- + WakeWorkflowExecution asks a running Workflow Execution to run a Workflow + Task because a source it consumes has moved. Unlike a Signal it records + no event, carries no payload, and folds with other wakes for the same + source, so a burst of writes costs one task. The Workflow learns the + source and its position from the task and reads the source itself. + operationId: WakeWorkflowExecution + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WakeWorkflowExecutionRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/WakeWorkflowExecutionResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /api/v1/nexus/endpoints: get: tags: @@ -9701,6 +9742,47 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake: + post: + tags: + - WorkflowService + description: |- + WakeWorkflowExecution asks a running Workflow Execution to run a Workflow + Task because a source it consumes has moved. Unlike a Signal it records + no event, carries no payload, and folds with other wakes for the same + source, so a burst of writes costs one task. The Workflow learns the + source and its position from the task and reads the source itself. + operationId: WakeWorkflowExecution + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WakeWorkflowExecutionRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/WakeWorkflowExecutionResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /system-info: get: tags: @@ -14866,6 +14948,15 @@ components: description: |- Stream data attached to this task. Delivered out of band so the payloads never enter History; only the offset ranges are recorded there. + wakes: + type: array + items: + $ref: '#/components/schemas/Wake' + description: |- + Wakes pending for this execution when the task started, folded by + source. Not recorded in History and never re-supplied on replay: the + Workflow treats one as a reason to read its source now and records what + it read itself. PollerGroupInfo: type: object properties: @@ -19262,6 +19353,67 @@ components: user specified timeout, API call returns even if specified stage is not reached. format: enum description: Specifies client's intent to wait for Update results. + Wake: + type: object + properties: + source: + type: string + description: |- + What moved, as the sender names it. The server folds wakes by this value + and never interprets it. For a stream, the provider formats the stream's + identity, owner and topic, into it. + position: + type: string + description: |- + Where the source stands after the write that caused this wake, in the + store's own terms. Opaque to the server and handed to the Workflow as + received. A later wake's position replaces an earlier one's. + format: bytes + counter: + type: string + description: |- + Orders wakes from one source. Only the source's store can order its + positions, so the sender derives this from the position and the server + compares nothing else. A wake whose counter is not above the one the + server already holds is folded. + description: |- + A wake tells a running Workflow Execution that something it consumes has + moved, so it should run a Workflow Task and read from its own cursor. It is + a reason to run, not data: nothing is recorded in History and no record + travels with it. A store Temporal does not host sends one after the store + has acknowledged a write. Wakes with the same source fold: the server keeps + the highest counter it has seen per execution and source, so a repeated, + reordered or late wake changes nothing, and a burst of writes costs one task. + WakeWorkflowExecutionRequest: + type: object + properties: + namespace: + type: string + workflowExecution: + allOf: + - $ref: '#/components/schemas/WorkflowExecution' + description: |- + The Workflow to wake. `run_id` is optional. When set, the wake goes to + the current run of the chain that run belongs to, so a sender that + learned a run id before a continue-as-new still reaches the consumer, + and it is refused with NotFound once that chain has ended. When unset, + the wake goes to the current run under the Workflow Id, whichever chain. + wake: + $ref: '#/components/schemas/Wake' + identity: + type: string + description: The identity of the sender, for metrics and logs. + WakeWorkflowExecutionResponse: + type: object + properties: + runId: + type: string + description: The run the wake was stored on. + folded: + type: boolean + description: |- + True when the server already held this counter or a higher one for the + source, so the wake changed nothing. WorkerConfig: type: object properties: diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index cf763aa12..e69cef0b0 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -764,3 +764,26 @@ message WorkflowExecutionPauseInfo { // The reason for pausing the workflow execution. string reason = 3; } + +// A wake tells a running Workflow Execution that something it consumes has +// moved, so it should run a Workflow Task and read from its own cursor. It is +// a reason to run, not data: nothing is recorded in History and no record +// travels with it. A store Temporal does not host sends one after the store +// has acknowledged a write. Wakes with the same source fold: the server keeps +// the highest counter it has seen per execution and source, so a repeated, +// reordered or late wake changes nothing, and a burst of writes costs one task. +message Wake { + // What moved, as the sender names it. The server folds wakes by this value + // and never interprets it. For a stream, the provider formats the stream's + // identity, owner and topic, into it. + string source = 1; + // Where the source stands after the write that caused this wake, in the + // store's own terms. Opaque to the server and handed to the Workflow as + // received. A later wake's position replaces an earlier one's. + bytes position = 2; + // Orders wakes from one source. Only the source's store can order its + // positions, so the sender derives this from the position and the server + // compares nothing else. A wake whose counter is not above the one the + // server already holds is folded. + int64 counter = 3; +} diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 93cd0e8b4..17faa8f98 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -389,6 +389,12 @@ message PollWorkflowTaskQueueResponse { // Stream data attached to this task. Delivered out of band so the payloads // never enter History; only the offset ranges are recorded there. repeated temporal.api.stream.v1.StreamSlice stream_slices = 20; + + // Wakes pending for this execution when the task started, folded by + // source. Not recorded in History and never re-supplied on replay: the + // Workflow treats one as a reason to read its source now and records what + // it read itself. + repeated temporal.api.workflow.v1.Wake wakes = 21; } message RespondWorkflowTaskCompletedRequest { @@ -885,6 +891,27 @@ message SignalWorkflowExecutionResponse { temporal.api.common.v1.Link link = 1; } +message WakeWorkflowExecutionRequest { + string namespace = 1; + // The Workflow to wake. `run_id` is optional. When set, the wake goes to + // the current run of the chain that run belongs to, so a sender that + // learned a run id before a continue-as-new still reaches the consumer, + // and it is refused with NotFound once that chain has ended. When unset, + // the wake goes to the current run under the Workflow Id, whichever chain. + temporal.api.common.v1.WorkflowExecution workflow_execution = 2; + temporal.api.workflow.v1.Wake wake = 3; + // The identity of the sender, for metrics and logs. + string identity = 4; +} + +message WakeWorkflowExecutionResponse { + // The run the wake was stored on. + string run_id = 1; + // True when the server already held this counter or a higher one for the + // source, so the wake changed nothing. + bool folded = 2; +} + message SignalWithStartWorkflowExecutionRequest { string namespace = 1; string workflow_id = 2; diff --git a/temporal/api/workflowservice/v1/service.proto b/temporal/api/workflowservice/v1/service.proto index 34f6a73f6..5404601c4 100644 --- a/temporal/api/workflowservice/v1/service.proto +++ b/temporal/api/workflowservice/v1/service.proto @@ -483,6 +483,22 @@ service WorkflowService { }; } + // WakeWorkflowExecution asks a running Workflow Execution to run a Workflow + // Task because a source it consumes has moved. Unlike a Signal it records + // no event, carries no payload, and folds with other wakes for the same + // source, so a burst of writes costs one task. The Workflow learns the + // source and its position from the task and reads the source itself. + rpc WakeWorkflowExecution (WakeWorkflowExecutionRequest) returns (WakeWorkflowExecutionResponse) { + option (google.api.http) = { + post: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake" + body: "*" + additional_bindings { + post: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake" + body: "*" + } + }; + } + // SignalWithStartWorkflowExecution is used to ensure a signal is sent to a workflow, even if // it isn't yet started. // From 086d85dabd985139e39c8236c14825ccedf76fe9 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Thu, 1 Oct 2026 11:08:41 -0700 Subject: [PATCH 18/30] Described the wake's fold rule as pending-and-undelivered only. A wake a task already received cannot be folded against, since only the receiver knows what it read; folding against it stalls a record the watcher read after the task ran. --- temporal/api/workflow/v1/message.proto | 18 ++++++++++-------- .../workflowservice/v1/request_response.proto | 5 +++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index e69cef0b0..8bd444186 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -769,9 +769,10 @@ message WorkflowExecutionPauseInfo { // moved, so it should run a Workflow Task and read from its own cursor. It is // a reason to run, not data: nothing is recorded in History and no record // travels with it. A store Temporal does not host sends one after the store -// has acknowledged a write. Wakes with the same source fold: the server keeps -// the highest counter it has seen per execution and source, so a repeated, -// reordered or late wake changes nothing, and a burst of writes costs one task. +// has acknowledged a write. Wakes for the same source fold while one is +// pending and no task has received it yet, so a burst of writes costs one +// task. Once a task has been handed the wake, a new wake for the source is +// accepted again, because only the receiver knows what it read. message Wake { // What moved, as the sender names it. The server folds wakes by this value // and never interprets it. For a stream, the provider formats the stream's @@ -779,11 +780,12 @@ message Wake { string source = 1; // Where the source stands after the write that caused this wake, in the // store's own terms. Opaque to the server and handed to the Workflow as - // received. A later wake's position replaces an earlier one's. + // received. Among wakes folded together, the position with the highest + // counter is the one delivered. bytes position = 2; - // Orders wakes from one source. Only the source's store can order its - // positions, so the sender derives this from the position and the server - // compares nothing else. A wake whose counter is not above the one the - // server already holds is folded. + // Orders wakes from one source, so that folding keeps the latest position. + // Only the source's store can order its positions, so the sender derives + // this from the position. It is not a durable identity: a wake sent after + // a task received an equal counter is accepted again. int64 counter = 3; } diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 17faa8f98..35f2b7a4c 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -907,8 +907,9 @@ message WakeWorkflowExecutionRequest { message WakeWorkflowExecutionResponse { // The run the wake was stored on. string run_id = 1; - // True when the server already held this counter or a higher one for the - // source, so the wake changed nothing. + // True when a wake for the source was already pending and no task had + // received it yet, so this one changed nothing but possibly the position + // and no new task results from it. bool folded = 2; } From 3f48439512ab16f3c49b4ea598fdf12147267b4c Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Thu, 1 Oct 2026 11:09:26 -0700 Subject: [PATCH 19/30] Regenerated the OpenAPI specs for the wake's fold rule. --- openapi/openapiv2.json | 8 ++++---- openapi/openapiv3.yaml | 23 +++++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 66cf54554..ef19ddbc4 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -21195,15 +21195,15 @@ "position": { "type": "string", "format": "byte", - "description": "Where the source stands after the write that caused this wake, in the\nstore's own terms. Opaque to the server and handed to the Workflow as\nreceived. A later wake's position replaces an earlier one's." + "description": "Where the source stands after the write that caused this wake, in the\nstore's own terms. Opaque to the server and handed to the Workflow as\nreceived. Among wakes folded together, the position with the highest\ncounter is the one delivered." }, "counter": { "type": "string", "format": "int64", - "description": "Orders wakes from one source. Only the source's store can order its\npositions, so the sender derives this from the position and the server\ncompares nothing else. A wake whose counter is not above the one the\nserver already holds is folded." + "description": "Orders wakes from one source, so that folding keeps the latest position.\nOnly the source's store can order its positions, so the sender derives\nthis from the position. It is not a durable identity: a wake sent after\na task received an equal counter is accepted again." } }, - "description": "A wake tells a running Workflow Execution that something it consumes has\nmoved, so it should run a Workflow Task and read from its own cursor. It is\na reason to run, not data: nothing is recorded in History and no record\ntravels with it. A store Temporal does not host sends one after the store\nhas acknowledged a write. Wakes with the same source fold: the server keeps\nthe highest counter it has seen per execution and source, so a repeated,\nreordered or late wake changes nothing, and a burst of writes costs one task." + "description": "A wake tells a running Workflow Execution that something it consumes has\nmoved, so it should run a Workflow Task and read from its own cursor. It is\na reason to run, not data: nothing is recorded in History and no record\ntravels with it. A store Temporal does not host sends one after the store\nhas acknowledged a write. Wakes for the same source fold while one is\npending and no task has received it yet, so a burst of writes costs one\ntask. Once a task has been handed the wake, a new wake for the source is\naccepted again, because only the receiver knows what it read." }, "v1WakeWorkflowExecutionResponse": { "type": "object", @@ -21214,7 +21214,7 @@ }, "folded": { "type": "boolean", - "description": "True when the server already held this counter or a higher one for the\nsource, so the wake changed nothing." + "description": "True when a wake for the source was already pending and no task had\nreceived it yet, so this one changed nothing but possibly the position\nand no new task results from it." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 5f2515bf7..33d052ea1 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -19367,23 +19367,25 @@ components: description: |- Where the source stands after the write that caused this wake, in the store's own terms. Opaque to the server and handed to the Workflow as - received. A later wake's position replaces an earlier one's. + received. Among wakes folded together, the position with the highest + counter is the one delivered. format: bytes counter: type: string description: |- - Orders wakes from one source. Only the source's store can order its - positions, so the sender derives this from the position and the server - compares nothing else. A wake whose counter is not above the one the - server already holds is folded. + Orders wakes from one source, so that folding keeps the latest position. + Only the source's store can order its positions, so the sender derives + this from the position. It is not a durable identity: a wake sent after + a task received an equal counter is accepted again. description: |- A wake tells a running Workflow Execution that something it consumes has moved, so it should run a Workflow Task and read from its own cursor. It is a reason to run, not data: nothing is recorded in History and no record travels with it. A store Temporal does not host sends one after the store - has acknowledged a write. Wakes with the same source fold: the server keeps - the highest counter it has seen per execution and source, so a repeated, - reordered or late wake changes nothing, and a burst of writes costs one task. + has acknowledged a write. Wakes for the same source fold while one is + pending and no task has received it yet, so a burst of writes costs one + task. Once a task has been handed the wake, a new wake for the source is + accepted again, because only the receiver knows what it read. WakeWorkflowExecutionRequest: type: object properties: @@ -19412,8 +19414,9 @@ components: folded: type: boolean description: |- - True when the server already held this counter or a higher one for the - source, so the wake changed nothing. + True when a wake for the source was already pending and no task had + received it yet, so this one changed nothing but possibly the position + and no new task results from it. WorkerConfig: type: object properties: From 2313c6090bc9c66c7ed5806ba3e5d2fec03e2cfb Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Thu, 1 Oct 2026 17:21:03 -0700 Subject: [PATCH 20/30] Added the notification channel: notify, listen, poll and describe. A writer notifies a named channel and the server wakes its listeners, so writers never address a consumer. Notifications a woken Workflow Task carries are recorded on its scheduled event, so replay sees them. The wake is marked as superseded. --- openapi/openapiv2.json | 819 ++++++++++++++++-- openapi/openapiv3.yaml | 591 +++++++++++++ temporal/api/command/v1/message.proto | 9 + temporal/api/enums/v1/command_type.proto | 1 + temporal/api/enums/v1/event_type.proto | 3 + temporal/api/history/v1/message.proto | 14 + temporal/api/notification/v1/message.proto | 55 ++ temporal/api/workflow/v1/message.proto | 4 + .../workflowservice/v1/request_response.proto | 74 ++ temporal/api/workflowservice/v1/service.proto | 69 ++ 10 files changed, 1565 insertions(+), 74 deletions(-) create mode 100644 temporal/api/notification/v1/message.proto diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index ef19ddbc4..65b327f8e 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -1449,6 +1449,244 @@ ] } }, + "/api/v1/namespaces/{namespace}/channels/{channel}": { + "get": { + "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", + "operationId": "DescribeChannel2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DescribeChannelResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/channels/{channel}/listeners": { + "post": { + "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", + "operationId": "RegisterChannelListener2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RegisterChannelListenerResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/channels/{channel}/listeners/{listenerId}": { + "delete": { + "summary": "UnregisterChannelListener removes a listener from a channel.", + "operationId": "UnregisterChannelListener2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1UnregisterChannelListenerResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "listenerId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "identity", + "description": "The identity of the caller, for metrics and logs.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/channels/{channel}/notifications": { + "get": { + "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", + "operationId": "PollChannel2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1PollChannelResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "afterCounter", + "description": "Only notifications with a counter above this one are returned.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "wait", + "description": "How long to wait for a notification when none is retained above\n`after_counter`.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maxNotifications", + "description": "At most this many notifications are returned. Zero means the server's\ndefault.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/channels/{notification.channel}/notify": { + "post": { + "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", + "operationId": "NotifyChannel2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1NotifyChannelResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "notification.channel", + "description": "The channel the writer notified. Listeners register on the same name.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, "/api/v1/namespaces/{namespace}/current-deployment/{deployment.seriesName}": { "post": { "summary": "Sets a deployment as the current deployment for its deployment series. Can optionally update\nthe metadata of the deployment as well.\nDeprecated. Replaced by `SetWorkerDeploymentCurrentVersion`.", @@ -4578,6 +4816,7 @@ "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/wake": { "post": { "summary": "WakeWorkflowExecution asks a running Workflow Execution to run a Workflow\nTask because a source it consumes has moved. Unlike a Signal it records\nno event, carries no payload, and folds with other wakes for the same\nsource, so a burst of writes costs one task. The Workflow learns the\nsource and its position from the task and reads the source itself.", + "description": "Superseded by the notification channel (`NotifyChannel` and the\n`SubscribeNotificationChannel` command). Kept for one round and slated for removal.", "operationId": "WakeWorkflowExecution2", "responses": { "200": { @@ -6886,7 +7125,229 @@ "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RespondActivityTaskCanceledResponse" + "$ref": "#/definitions/v1RespondActivityTaskCanceledResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRespondActivityTaskCanceledBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/archived-workflows": { + "get": { + "summary": "ListArchivedWorkflowExecutions is a visibility API to list archived workflow executions in a specific namespace.", + "operationId": "ListArchivedWorkflowExecutions", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ListArchivedWorkflowExecutionsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "nextPageToken", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + }, + { + "name": "query", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/batch-operations": { + "get": { + "summary": "ListBatchOperations returns a list of batch operations", + "operationId": "ListBatchOperations", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ListBatchOperationsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "Namespace that contains the batch operation", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "pageSize", + "description": "List page size", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "nextPageToken", + "description": "Next page token", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/batch-operations/{jobId}": { + "get": { + "summary": "DescribeBatchOperation returns the information about a batch operation", + "operationId": "DescribeBatchOperation", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DescribeBatchOperationResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "Namespace that contains the batch operation", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "jobId", + "description": "Batch job id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + }, + "post": { + "summary": "StartBatchOperation starts a new batch operation", + "operationId": "StartBatchOperation", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1StartBatchOperationResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "Namespace that contains the batch operation", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "jobId", + "description": "Job ID defines the unique ID for the batch job", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceStartBatchOperationBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/batch-operations/{jobId}/stop": { + "post": { + "summary": "StopBatchOperation stops a batch operation", + "operationId": "StopBatchOperation", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1StopBatchOperationResponse" } }, "default": { @@ -6899,6 +7360,14 @@ "parameters": [ { "name": "namespace", + "description": "Namespace that contains the batch operation", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "jobId", + "description": "Batch job id", "in": "path", "required": true, "type": "string" @@ -6908,7 +7377,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceRespondActivityTaskCanceledBody" + "$ref": "#/definitions/WorkflowServiceStopBatchOperationBody" } } ], @@ -6917,15 +7386,15 @@ ] } }, - "/namespaces/{namespace}/archived-workflows": { + "/namespaces/{namespace}/channels/{channel}": { "get": { - "summary": "ListArchivedWorkflowExecutions is a visibility API to list archived workflow executions in a specific namespace.", - "operationId": "ListArchivedWorkflowExecutions", + "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", + "operationId": "DescribeChannel", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1ListArchivedWorkflowExecutionsResponse" + "$ref": "#/definitions/v1DescribeChannelResponse" } }, "default": { @@ -6943,23 +7412,9 @@ "type": "string" }, { - "name": "pageSize", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "nextPageToken", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "query", - "in": "query", - "required": false, + "name": "channel", + "in": "path", + "required": true, "type": "string" } ], @@ -6968,15 +7423,15 @@ ] } }, - "/namespaces/{namespace}/batch-operations": { - "get": { - "summary": "ListBatchOperations returns a list of batch operations", - "operationId": "ListBatchOperations", + "/namespaces/{namespace}/channels/{channel}/listeners": { + "post": { + "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", + "operationId": "RegisterChannelListener", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1ListBatchOperationsResponse" + "$ref": "#/definitions/v1RegisterChannelListenerResponse" } }, "default": { @@ -6989,26 +7444,23 @@ "parameters": [ { "name": "namespace", - "description": "Namespace that contains the batch operation", "in": "path", "required": true, "type": "string" }, { - "name": "pageSize", - "description": "List page size", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" + "name": "channel", + "in": "path", + "required": true, + "type": "string" }, { - "name": "nextPageToken", - "description": "Next page token", - "in": "query", - "required": false, - "type": "string", - "format": "byte" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" + } } ], "tags": [ @@ -7016,15 +7468,15 @@ ] } }, - "/namespaces/{namespace}/batch-operations/{jobId}": { - "get": { - "summary": "DescribeBatchOperation returns the information about a batch operation", - "operationId": "DescribeBatchOperation", + "/namespaces/{namespace}/channels/{channel}/listeners/{listenerId}": { + "delete": { + "summary": "UnregisterChannelListener removes a listener from a channel.", + "operationId": "UnregisterChannelListener", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1DescribeBatchOperationResponse" + "$ref": "#/definitions/v1UnregisterChannelListenerResponse" } }, "default": { @@ -7037,31 +7489,44 @@ "parameters": [ { "name": "namespace", - "description": "Namespace that contains the batch operation", "in": "path", "required": true, "type": "string" }, { - "name": "jobId", - "description": "Batch job id", + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "listenerId", "in": "path", "required": true, "type": "string" + }, + { + "name": "identity", + "description": "The identity of the caller, for metrics and logs.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ "WorkflowService" ] - }, - "post": { - "summary": "StartBatchOperation starts a new batch operation", - "operationId": "StartBatchOperation", + } + }, + "/namespaces/{namespace}/channels/{channel}/notifications": { + "get": { + "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", + "operationId": "PollChannel", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1StartBatchOperationResponse" + "$ref": "#/definitions/v1PollChannelResponse" } }, "default": { @@ -7074,25 +7539,38 @@ "parameters": [ { "name": "namespace", - "description": "Namespace that contains the batch operation", "in": "path", "required": true, "type": "string" }, { - "name": "jobId", - "description": "Job ID defines the unique ID for the batch job", + "name": "channel", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceStartBatchOperationBody" - } + "name": "afterCounter", + "description": "Only notifications with a counter above this one are returned.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "wait", + "description": "How long to wait for a notification when none is retained above\n`after_counter`.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maxNotifications", + "description": "At most this many notifications are returned. Zero means the server's\ndefault.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" } ], "tags": [ @@ -7100,15 +7578,15 @@ ] } }, - "/namespaces/{namespace}/batch-operations/{jobId}/stop": { + "/namespaces/{namespace}/channels/{notification.channel}/notify": { "post": { - "summary": "StopBatchOperation stops a batch operation", - "operationId": "StopBatchOperation", + "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", + "operationId": "NotifyChannel", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1StopBatchOperationResponse" + "$ref": "#/definitions/v1NotifyChannelResponse" } }, "default": { @@ -7121,14 +7599,13 @@ "parameters": [ { "name": "namespace", - "description": "Namespace that contains the batch operation", "in": "path", "required": true, "type": "string" }, { - "name": "jobId", - "description": "Batch job id", + "name": "notification.channel", + "description": "The channel the writer notified. Listeners register on the same name.", "in": "path", "required": true, "type": "string" @@ -7138,7 +7615,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceStopBatchOperationBody" + "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" } } ], @@ -10206,6 +10683,7 @@ "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/wake": { "post": { "summary": "WakeWorkflowExecution asks a running Workflow Execution to run a Workflow\nTask because a source it consumes has moved. Unlike a Signal it records\nno event, carries no payload, and folds with other wakes for the same\nsource, so a burst of writes costs one task. The Workflow learns the\nsource and its position from the task and reads the source itself.", + "description": "Superseded by the notification channel (`NotifyChannel` and the\n`SubscribeNotificationChannel` command). Kept for one round and slated for removal.", "operationId": "WakeWorkflowExecution", "responses": { "200": { @@ -11799,6 +12277,42 @@ } } }, + "WorkflowServiceNotifyChannelBody": { + "type": "object", + "properties": { + "notification": { + "type": "object", + "properties": { + "position": { + "type": "string", + "format": "byte", + "description": "Where the source stands after the write that caused this notification,\nin the writer's terms. Opaque to the server." + }, + "counter": { + "type": "string", + "format": "int64", + "description": "Orders notifications from one channel's writers. The writer derives it\nfrom the position, since only the source can order its positions. Among\nnotifications folded together, the one with the highest counter is kept." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1Payload" + }, + "description": "Details for the listener, such as which topic moved. Bounded in size and\ncarried as payloads, so a codec applies as to any payload." + } + }, + "description": "A notification tells the listeners of a channel that a source they consume\nhas moved. It is not data: the listener reads the source itself. A channel\nis named by the writer and its listeners; for a stream, the provider formats\nthe stream's identity into the name. Writers never learn who listens. The\nserver folds notifications per listener while one is pending and no task has\nbeen scheduled for it, keeping the one with the highest counter." + }, + "identity": { + "type": "string", + "description": "The identity of the writer, for metrics and logs." + }, + "requestId": { + "type": "string", + "description": "Used to de-dupe a retried notification." + } + } + }, "WorkflowServicePatchScheduleBody": { "type": "object", "properties": { @@ -11987,6 +12501,23 @@ } } }, + "WorkflowServiceRegisterChannelListenerBody": { + "type": "object", + "properties": { + "callback": { + "$ref": "#/definitions/commonV1Callback", + "description": "Invoked with each notification on the channel." + }, + "requestId": { + "type": "string", + "description": "Used to de-dupe a retried registration." + }, + "identity": { + "type": "string", + "description": "The identity of the caller, for metrics and logs." + } + } + }, "WorkflowServiceRequestCancelActivityExecutionBody": { "type": "object", "properties": { @@ -14952,6 +15483,26 @@ } } }, + "v1ChannelListener": { + "type": "object", + "properties": { + "listenerId": { + "type": "string", + "description": "Assigned by the server when the listener registers." + }, + "workflow": { + "$ref": "#/definitions/v1WorkflowListener" + }, + "callback": { + "$ref": "#/definitions/commonV1Callback" + }, + "registeredTime": { + "type": "string", + "format": "date-time" + } + }, + "description": "A listener of a channel: a Workflow Execution woken with a Workflow Task, or\na callback the server invokes with each notification." + }, "v1ChildWorkflowExecutionCanceledEventAttributes": { "type": "object", "properties": { @@ -15781,6 +16332,27 @@ } } }, + "v1DescribeChannelResponse": { + "type": "object", + "properties": { + "listeners": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1ChannelListener" + } + }, + "latest": { + "$ref": "#/definitions/v1Notification", + "description": "The notification with the highest counter the channel retains." + }, + "retainedCount": { + "type": "integer", + "format": "int32", + "description": "How many notifications the channel retains for pollers." + } + } + }, "v1DescribeDeploymentResponse": { "type": "object", "properties": { @@ -16229,10 +16801,11 @@ "EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED", "EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED", "EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED", - "EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED" + "EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED", + "EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED" ], "default": "EVENT_TYPE_UNSPECIFIED", - "description": "- EVENT_TYPE_UNSPECIFIED: Place holder and should never appear in a Workflow execution history\n - EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: Workflow execution has been triggered/started\nIt contains Workflow execution inputs, as well as Workflow timeout configurations\n - EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED: Workflow execution has successfully completed and contains Workflow execution results\n - EVENT_TYPE_WORKFLOW_EXECUTION_FAILED: Workflow execution has unsuccessfully completed and contains the Workflow execution error\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT: Workflow execution has timed out by the Temporal Server\nUsually due to the Workflow having not been completed within timeout settings\n - EVENT_TYPE_WORKFLOW_TASK_SCHEDULED: Workflow Task has been scheduled and the SDK client should now be able to process any new history events\n - EVENT_TYPE_WORKFLOW_TASK_STARTED: Workflow Task has started and the SDK client has picked up the Workflow Task and is processing new history events\n - EVENT_TYPE_WORKFLOW_TASK_COMPLETED: Workflow Task has completed\nThe SDK client picked up the Workflow Task and processed new history events\nSDK client may or may not ask the Temporal Server to do additional work, such as:\nEVENT_TYPE_ACTIVITY_TASK_SCHEDULED\nEVENT_TYPE_TIMER_STARTED\nEVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES\nEVENT_TYPE_MARKER_RECORDED\nEVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED\nEVENT_TYPE_WORKFLOW_EXECUTION_FAILED\nEVENT_TYPE_WORKFLOW_EXECUTION_CANCELED\nEVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW\n - EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: Workflow Task encountered a timeout\nEither an SDK client with a local cache was not available at the time, or it took too long for the SDK client to process the task\n - EVENT_TYPE_WORKFLOW_TASK_FAILED: Workflow Task encountered a failure\nUsually this means that the Workflow was non-deterministic\nHowever, the Workflow reset functionality also uses this event\n - EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: Activity Task was scheduled\nThe SDK client should pick up this activity task and execute\nThis event type contains activity inputs, as well as activity timeout configurations\n - EVENT_TYPE_ACTIVITY_TASK_STARTED: Activity Task has started executing\nThe SDK client has picked up the Activity Task and is processing the Activity invocation\n - EVENT_TYPE_ACTIVITY_TASK_COMPLETED: Activity Task has finished successfully\nThe SDK client has picked up and successfully completed the Activity Task\nThis event type contains Activity execution results\n - EVENT_TYPE_ACTIVITY_TASK_FAILED: Activity Task has finished unsuccessfully\nThe SDK picked up the Activity Task but unsuccessfully completed it\nThis event type contains Activity execution errors\n - EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT: Activity has timed out according to the Temporal Server\nActivity did not complete within the timeout settings\n - EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED: A request to cancel the Activity has occurred\nThe SDK client will be able to confirm cancellation of an Activity during an Activity heartbeat\n - EVENT_TYPE_ACTIVITY_TASK_CANCELED: Activity has been cancelled\n - EVENT_TYPE_TIMER_STARTED: A timer has started\n - EVENT_TYPE_TIMER_FIRED: A timer has fired\n - EVENT_TYPE_TIMER_CANCELED: A time has been cancelled\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED: A request has been made to cancel the Workflow execution\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: SDK client has confirmed the cancellation request and the Workflow execution has been cancelled\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Workflow has requested that the Temporal Server try to cancel another Workflow\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server could not cancel the targeted Workflow\nThis is usually because the target Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED: Temporal Server has successfully requested the cancellation of the target Workflow\n - EVENT_TYPE_MARKER_RECORDED: A marker has been recorded.\nThis event type is transparent to the Temporal Server\nThe Server will only store it and will not try to understand it.\n - EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: Workflow has received a Signal event\nThe event type contains the Signal name, as well as a Signal payload\n - EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: Workflow execution has been forcefully terminated\nThis is usually because the terminate Workflow API was called\n - EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: Workflow has successfully completed and a new Workflow has been started within the same transaction\nContains last Workflow execution results as well as new Workflow execution inputs\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to start a child Workflow\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution cannot be started/triggered\nUsually due to a child Workflow ID collision\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED: Child Workflow execution has successfully started/triggered\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED: Child Workflow execution has successfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution has unsuccessfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED: Child Workflow execution has been cancelled\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT: Child Workflow execution has timed out by the Temporal Server\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED: Child Workflow execution has been terminated\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to Signal the targeted Workflow\nContains the Signal name, as well as a Signal payload\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server cannot Signal the targeted Workflow\nUsually because the Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED: Temporal Server has successfully Signaled the targeted Workflow\n - EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: Workflow search attributes should be updated and synchronized with the visibility store\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: An update was admitted. Note that not all admitted updates result in this\nevent. See UpdateAdmittedEventOrigin for situations in which this event\nis created.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED: An update was accepted (i.e. passed validation, perhaps because no validator was defined)\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REJECTED: This event is never written to history.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED: An update completed\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of the workflow as a whole have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of an already-scheduled activity have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: Workflow properties modified by user workflow code\n - EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: A Nexus operation was scheduled using a ScheduleNexusOperation command.\n - EVENT_TYPE_NEXUS_OPERATION_STARTED: An asynchronous Nexus operation was started by a Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_COMPLETED: A Nexus operation completed successfully.\n - EVENT_TYPE_NEXUS_OPERATION_FAILED: A Nexus operation failed.\n - EVENT_TYPE_NEXUS_OPERATION_CANCELED: A Nexus operation completed as canceled.\n - EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: A Nexus operation timed out.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: A Nexus operation was requested to be canceled using a RequestCancelNexusOperation command.\n - EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: Workflow execution options updated by user.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED: A cancellation request for a Nexus operation was successfully delivered to the Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED: A cancellation request for a Nexus operation resulted in an error.\n - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED: An event that indicates that the workflow execution has been paused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED: An event that indicates that the previously paused workflow execution has been unpaused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED: An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.\n - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: A Workflow subscribed to a stream. Recorded once per subscription, not\nper record: the offsets a task consumed ride WorkflowTaskCompleted and\nthe payloads never enter History at all.\n - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED: A Workflow appended a batch of records to a stream. Recorded per\nbatch, and carrying only the offset range it landed at: the bodies go to\nthe stream's own log, never into History.", + "description": "- EVENT_TYPE_UNSPECIFIED: Place holder and should never appear in a Workflow execution history\n - EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: Workflow execution has been triggered/started\nIt contains Workflow execution inputs, as well as Workflow timeout configurations\n - EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED: Workflow execution has successfully completed and contains Workflow execution results\n - EVENT_TYPE_WORKFLOW_EXECUTION_FAILED: Workflow execution has unsuccessfully completed and contains the Workflow execution error\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT: Workflow execution has timed out by the Temporal Server\nUsually due to the Workflow having not been completed within timeout settings\n - EVENT_TYPE_WORKFLOW_TASK_SCHEDULED: Workflow Task has been scheduled and the SDK client should now be able to process any new history events\n - EVENT_TYPE_WORKFLOW_TASK_STARTED: Workflow Task has started and the SDK client has picked up the Workflow Task and is processing new history events\n - EVENT_TYPE_WORKFLOW_TASK_COMPLETED: Workflow Task has completed\nThe SDK client picked up the Workflow Task and processed new history events\nSDK client may or may not ask the Temporal Server to do additional work, such as:\nEVENT_TYPE_ACTIVITY_TASK_SCHEDULED\nEVENT_TYPE_TIMER_STARTED\nEVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES\nEVENT_TYPE_MARKER_RECORDED\nEVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED\nEVENT_TYPE_WORKFLOW_EXECUTION_FAILED\nEVENT_TYPE_WORKFLOW_EXECUTION_CANCELED\nEVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW\n - EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: Workflow Task encountered a timeout\nEither an SDK client with a local cache was not available at the time, or it took too long for the SDK client to process the task\n - EVENT_TYPE_WORKFLOW_TASK_FAILED: Workflow Task encountered a failure\nUsually this means that the Workflow was non-deterministic\nHowever, the Workflow reset functionality also uses this event\n - EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: Activity Task was scheduled\nThe SDK client should pick up this activity task and execute\nThis event type contains activity inputs, as well as activity timeout configurations\n - EVENT_TYPE_ACTIVITY_TASK_STARTED: Activity Task has started executing\nThe SDK client has picked up the Activity Task and is processing the Activity invocation\n - EVENT_TYPE_ACTIVITY_TASK_COMPLETED: Activity Task has finished successfully\nThe SDK client has picked up and successfully completed the Activity Task\nThis event type contains Activity execution results\n - EVENT_TYPE_ACTIVITY_TASK_FAILED: Activity Task has finished unsuccessfully\nThe SDK picked up the Activity Task but unsuccessfully completed it\nThis event type contains Activity execution errors\n - EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT: Activity has timed out according to the Temporal Server\nActivity did not complete within the timeout settings\n - EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED: A request to cancel the Activity has occurred\nThe SDK client will be able to confirm cancellation of an Activity during an Activity heartbeat\n - EVENT_TYPE_ACTIVITY_TASK_CANCELED: Activity has been cancelled\n - EVENT_TYPE_TIMER_STARTED: A timer has started\n - EVENT_TYPE_TIMER_FIRED: A timer has fired\n - EVENT_TYPE_TIMER_CANCELED: A time has been cancelled\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED: A request has been made to cancel the Workflow execution\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: SDK client has confirmed the cancellation request and the Workflow execution has been cancelled\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Workflow has requested that the Temporal Server try to cancel another Workflow\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server could not cancel the targeted Workflow\nThis is usually because the target Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED: Temporal Server has successfully requested the cancellation of the target Workflow\n - EVENT_TYPE_MARKER_RECORDED: A marker has been recorded.\nThis event type is transparent to the Temporal Server\nThe Server will only store it and will not try to understand it.\n - EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: Workflow has received a Signal event\nThe event type contains the Signal name, as well as a Signal payload\n - EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: Workflow execution has been forcefully terminated\nThis is usually because the terminate Workflow API was called\n - EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: Workflow has successfully completed and a new Workflow has been started within the same transaction\nContains last Workflow execution results as well as new Workflow execution inputs\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to start a child Workflow\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution cannot be started/triggered\nUsually due to a child Workflow ID collision\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED: Child Workflow execution has successfully started/triggered\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED: Child Workflow execution has successfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution has unsuccessfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED: Child Workflow execution has been cancelled\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT: Child Workflow execution has timed out by the Temporal Server\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED: Child Workflow execution has been terminated\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to Signal the targeted Workflow\nContains the Signal name, as well as a Signal payload\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server cannot Signal the targeted Workflow\nUsually because the Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED: Temporal Server has successfully Signaled the targeted Workflow\n - EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: Workflow search attributes should be updated and synchronized with the visibility store\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: An update was admitted. Note that not all admitted updates result in this\nevent. See UpdateAdmittedEventOrigin for situations in which this event\nis created.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED: An update was accepted (i.e. passed validation, perhaps because no validator was defined)\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REJECTED: This event is never written to history.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED: An update completed\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of the workflow as a whole have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of an already-scheduled activity have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: Workflow properties modified by user workflow code\n - EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: A Nexus operation was scheduled using a ScheduleNexusOperation command.\n - EVENT_TYPE_NEXUS_OPERATION_STARTED: An asynchronous Nexus operation was started by a Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_COMPLETED: A Nexus operation completed successfully.\n - EVENT_TYPE_NEXUS_OPERATION_FAILED: A Nexus operation failed.\n - EVENT_TYPE_NEXUS_OPERATION_CANCELED: A Nexus operation completed as canceled.\n - EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: A Nexus operation timed out.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: A Nexus operation was requested to be canceled using a RequestCancelNexusOperation command.\n - EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: Workflow execution options updated by user.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED: A cancellation request for a Nexus operation was successfully delivered to the Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED: A cancellation request for a Nexus operation resulted in an error.\n - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED: An event that indicates that the workflow execution has been paused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED: An event that indicates that the previously paused workflow execution has been unpaused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED: An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.\n - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: A Workflow subscribed to a stream. Recorded once per subscription, not\nper record: the offsets a task consumed ride WorkflowTaskCompleted and\nthe payloads never enter History at all.\n - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED: A Workflow appended a batch of records to a stream. Recorded per\nbatch, and carrying only the offset range it landed at: the bodies go to\nthe stream's own log, never into History.\n - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED: A Workflow became a listener of a notification channel for its run.\nThe notifications themselves ride the WorkflowTaskScheduled event.", "title": "Whenever this list of events is changed do change the function shouldBufferEvent in mutableStateBuilder.go to make sure to do the correct event ordering" }, "v1Execution": { @@ -16905,6 +17478,9 @@ }, "workflowStreamRecordsAppendedEventAttributes": { "$ref": "#/definitions/v1WorkflowStreamRecordsAppendedEventAttributes" + }, + "workflowNotificationChannelSubscribedEventAttributes": { + "$ref": "#/definitions/v1WorkflowNotificationChannelSubscribedEventAttributes" } }, "description": "History events are the method by which Temporal SDKs advance (or recreate) workflow state.\nSee the `EventType` enum for more info about what each event is for." @@ -18204,6 +18780,43 @@ "default": "NEXUS_OPERATION_WAIT_STAGE_UNSPECIFIED", "description": "Stage that can be specified when waiting on a nexus operation.\n\n - NEXUS_OPERATION_WAIT_STAGE_STARTED: Wait for the operation to be started.\n - NEXUS_OPERATION_WAIT_STAGE_CLOSED: Wait for the operation to be in a terminal state, either successful or unsuccessful." }, + "v1Notification": { + "type": "object", + "properties": { + "channel": { + "type": "string", + "description": "The channel the writer notified. Listeners register on the same name." + }, + "position": { + "type": "string", + "format": "byte", + "description": "Where the source stands after the write that caused this notification,\nin the writer's terms. Opaque to the server." + }, + "counter": { + "type": "string", + "format": "int64", + "description": "Orders notifications from one channel's writers. The writer derives it\nfrom the position, since only the source can order its positions. Among\nnotifications folded together, the one with the highest counter is kept." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1Payload" + }, + "description": "Details for the listener, such as which topic moved. Bounded in size and\ncarried as payloads, so a codec applies as to any payload." + } + }, + "description": "A notification tells the listeners of a channel that a source they consume\nhas moved. It is not data: the listener reads the source itself. A channel\nis named by the writer and its listeners; for a stream, the provider formats\nthe stream's identity into the name. Writers never learn who listens. The\nserver folds notifications per listener while one is pending and no task has\nbeen scheduled for it, keeping the one with the highest counter." + }, + "v1NotifyChannelResponse": { + "type": "object", + "properties": { + "listenerCount": { + "type": "integer", + "format": "int32", + "description": "Listeners registered when the notification was accepted. Zero means the\nnotification was retained for pollers and woke nobody." + } + } + }, "v1Outcome": { "type": "object", "properties": { @@ -18542,6 +19155,18 @@ } } }, + "v1PollChannelResponse": { + "type": "object", + "properties": { + "notifications": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Notification" + } + } + } + }, "v1PollNexusOperationExecutionResponse": { "type": "object", "properties": { @@ -18944,6 +19569,14 @@ "v1RecordWorkerHeartbeatResponse": { "type": "object" }, + "v1RegisterChannelListenerResponse": { + "type": "object", + "properties": { + "listenerId": { + "type": "string" + } + } + }, "v1RegisterNamespaceRequest": { "type": "object", "properties": { @@ -20848,6 +21481,9 @@ "type": "object", "description": "Response to a successful UnpauseWorkflowExecution request." }, + "v1UnregisterChannelListenerResponse": { + "type": "object" + }, "v1UpdateActivityExecutionOptionsResponse": { "type": "object", "properties": { @@ -21203,7 +21839,7 @@ "description": "Orders wakes from one source, so that folding keeps the latest position.\nOnly the source's store can order its positions, so the sender derives\nthis from the position. It is not a durable identity: a wake sent after\na task received an equal counter is accepted again." } }, - "description": "A wake tells a running Workflow Execution that something it consumes has\nmoved, so it should run a Workflow Task and read from its own cursor. It is\na reason to run, not data: nothing is recorded in History and no record\ntravels with it. A store Temporal does not host sends one after the store\nhas acknowledged a write. Wakes for the same source fold while one is\npending and no task has received it yet, so a burst of writes costs one\ntask. Once a task has been handed the wake, a new wake for the source is\naccepted again, because only the receiver knows what it read." + "description": "A wake tells a running Workflow Execution that something it consumes has\nmoved, so it should run a Workflow Task and read from its own cursor. It is\na reason to run, not data: nothing is recorded in History and no record\ntravels with it. A store Temporal does not host sends one after the store\nhas acknowledged a write. Wakes for the same source fold while one is\npending and no task has received it yet, so a burst of writes costs one\ntask. Once a task has been handed the wake, a new wake for the source is\naccepted again, because only the receiver knows what it read.\n\nSuperseded by the notification channel (`NotifyChannel` and the\n`SubscribeNotificationChannel` command). Kept for one round and slated for\nremoval." }, "v1WakeWorkflowExecutionResponse": { "type": "object", @@ -22580,6 +23216,33 @@ "default": "WORKFLOW_ID_REUSE_POLICY_UNSPECIFIED", "description": "Defines whether to allow re-using a workflow id from a previously *closed* workflow.\nIf the request is denied, the server returns a `WorkflowExecutionAlreadyStartedFailure` error.\n\nSee `WorkflowIdConflictPolicy` for handling workflow id duplication with a *running* workflow.\n\n - WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE: Allow starting a workflow execution using the same workflow id.\n - WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY: Allow starting a workflow execution using the same workflow id, only when the last\nexecution's final state is one of [terminated, cancelled, timed out, failed].\n - WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE: Do not permit re-use of the workflow id for this workflow. Future start workflow requests\ncould potentially change the policy, allowing re-use of the workflow id.\n - WORKFLOW_ID_REUSE_POLICY_TERMINATE_IF_RUNNING: Terminate the current Workflow if one is already running; otherwise allow reusing the\nWorkflow ID. When using this option, `WorkflowIdConflictPolicy` must be left unspecified.\n\nDeprecated. Instead, set `WorkflowIdReusePolicy` to `ALLOW_DUPLICATE` and\n`WorkflowIdConflictPolicy` to `TERMINATE_EXISTING`. Note that `WorkflowIdConflictPolicy`\nrequires Temporal Server v1.24.0 or later." }, + "v1WorkflowListener": { + "type": "object", + "properties": { + "workflowId": { + "type": "string" + }, + "runId": { + "type": "string", + "description": "The run that subscribed. The server follows a continue-as-new to the\nchain's current run when it delivers." + } + }, + "description": "A Workflow Execution listening on a channel." + }, + "v1WorkflowNotificationChannelSubscribedEventAttributes": { + "type": "object", + "properties": { + "workflowTaskCompletedEventId": { + "type": "string", + "format": "int64", + "description": "The WorkflowTaskCompleted event of the task whose command created this\nsubscription." + }, + "channel": { + "type": "string", + "description": "The channel the Workflow listens on for the rest of this run." + } + } + }, "v1WorkflowPropertiesModifiedEventAttributes": { "type": "object", "properties": { @@ -22947,6 +23610,14 @@ "type": "integer", "format": "int32", "title": "Starting at 1, how many attempts there have been to complete this task" + }, + "notifications": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Notification" + }, + "description": "Notifications for channels this Workflow listens to, folded per channel\nsince the last task was scheduled. In History so a Workflow may act on\nthem deterministically and replay sees the same." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 33d052ea1..67f2f7245 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -1317,6 +1317,220 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/channels/{channel}: + get: + tags: + - WorkflowService + description: |- + DescribeChannel returns the listeners of a channel and its latest + notification. + operationId: DescribeChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DescribeChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/channels/{channel}/listeners: + post: + tags: + - WorkflowService + description: |- + RegisterChannelListener registers a callback as a listener of a channel. A + Workflow registers itself with the `SubscribeNotificationChannel` command + instead. + operationId: RegisterChannelListener + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterChannelListenerRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterChannelListenerResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/channels/{channel}/listeners/{listenerId}: + delete: + tags: + - WorkflowService + description: |- + UnregisterChannelListener removes a listener from a channel. + + (-- api-linter: core::0136::http-method=disabled + aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + operationId: UnregisterChannelListener + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: listenerId + in: path + required: true + schema: + type: string + - name: identity + in: query + description: The identity of the caller, for metrics and logs. + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UnregisterChannelListenerResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/channels/{channel}/notifications: + get: + tags: + - WorkflowService + description: |- + PollChannel is a long poll for clients. It returns the retained + notifications of a channel with a counter above `after_counter`, waiting + up to `wait` for one when none is retained yet. + operationId: PollChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: afterCounter + in: query + description: |- + Only notifications with a counter above this one are returned. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "after" names the exclusive lower bound. --) + schema: + type: string + - name: wait + in: query + description: |- + How long to wait for a notification when none is retained above + `after_counter`. + schema: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s + - name: maxNotifications + in: query + description: |- + At most this many notifications are returned. Zero means the server's + default. + schema: + type: integer + format: int32 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PollChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/channels/{notification.channel}/notify: + post: + tags: + - WorkflowService + description: |- + NotifyChannel tells every listener of a channel that a source they consume + has moved. The writer names no addressee and never learns who listens. The + server wakes each listener: a Workflow with a Workflow Task, a callback by + invoking it. Nothing goes to History except the notifications a woken + Workflow Task carries on its scheduled event. + operationId: NotifyChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: notification.channel + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyChannelRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /api/v1/namespaces/{namespace}/current-deployment/{deployment.series_name}: post: tags: @@ -4688,6 +4902,9 @@ paths: no event, carries no payload, and folds with other wakes for the same source, so a burst of writes costs one task. The Workflow learns the source and its position from the task and reads the source itself. + + Superseded by the notification channel (`NotifyChannel` and the + `SubscribeNotificationChannel` command). Kept for one round and slated for removal. operationId: WakeWorkflowExecution parameters: - name: namespace @@ -6439,6 +6656,220 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /namespaces/{namespace}/channels/{channel}: + get: + tags: + - WorkflowService + description: |- + DescribeChannel returns the listeners of a channel and its latest + notification. + operationId: DescribeChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DescribeChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/channels/{channel}/listeners: + post: + tags: + - WorkflowService + description: |- + RegisterChannelListener registers a callback as a listener of a channel. A + Workflow registers itself with the `SubscribeNotificationChannel` command + instead. + operationId: RegisterChannelListener + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterChannelListenerRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterChannelListenerResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/channels/{channel}/listeners/{listenerId}: + delete: + tags: + - WorkflowService + description: |- + UnregisterChannelListener removes a listener from a channel. + + (-- api-linter: core::0136::http-method=disabled + aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + operationId: UnregisterChannelListener + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: listenerId + in: path + required: true + schema: + type: string + - name: identity + in: query + description: The identity of the caller, for metrics and logs. + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UnregisterChannelListenerResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/channels/{channel}/notifications: + get: + tags: + - WorkflowService + description: |- + PollChannel is a long poll for clients. It returns the retained + notifications of a channel with a counter above `after_counter`, waiting + up to `wait` for one when none is retained yet. + operationId: PollChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: afterCounter + in: query + description: |- + Only notifications with a counter above this one are returned. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "after" names the exclusive lower bound. --) + schema: + type: string + - name: wait + in: query + description: |- + How long to wait for a notification when none is retained above + `after_counter`. + schema: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s + - name: maxNotifications + in: query + description: |- + At most this many notifications are returned. Zero means the server's + default. + schema: + type: integer + format: int32 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PollChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/channels/{notification.channel}/notify: + post: + tags: + - WorkflowService + description: |- + NotifyChannel tells every listener of a channel that a source they consume + has moved. The writer names no addressee and never learns who listens. The + server wakes each listener: a Workflow with a Workflow Task, a callback by + invoking it. Nothing goes to History except the notifications a woken + Workflow Task carries on its scheduled event. + operationId: NotifyChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: notification.channel + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyChannelRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /namespaces/{namespace}/current-deployment/{deployment.series_name}: post: tags: @@ -9752,6 +10183,9 @@ paths: no event, carries no payload, and folds with other wakes for the same source, so a burst of writes costs one task. The Workflow learns the source and its position from the task and reads the source itself. + + Superseded by the notification channel (`NotifyChannel` and the + `SubscribeNotificationChannel` command). Kept for one round and slated for removal. operationId: WakeWorkflowExecution parameters: - name: namespace @@ -11038,6 +11472,22 @@ components: identity: type: string description: The identity of the worker or client that requested the cancellation. + ChannelListener: + type: object + properties: + listenerId: + type: string + description: Assigned by the server when the listener registers. + workflow: + $ref: '#/components/schemas/WorkflowListener' + callback: + $ref: '#/components/schemas/Callback' + registeredTime: + type: string + format: date-time + description: |- + A listener of a channel: a Workflow Execution woken with a Workflow Task, or + a callback the server invokes with each notification. ChildWorkflowExecutionCanceledEventAttributes: type: object properties: @@ -11949,6 +12399,21 @@ components: items: $ref: '#/components/schemas/Execution' description: Executions is the list of workflow OR standalone activity executions to apply the batch operation + DescribeChannelResponse: + type: object + properties: + listeners: + type: array + items: + $ref: '#/components/schemas/ChannelListener' + latest: + allOf: + - $ref: '#/components/schemas/Notification' + description: The notification with the highest counter the channel retains. + retainedCount: + type: integer + description: How many notifications the channel retains for pollers. + format: int32 DescribeDeploymentResponse: type: object properties: @@ -12962,6 +13427,7 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED + - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED type: string format: enum version: @@ -13131,6 +13597,8 @@ components: $ref: '#/components/schemas/WorkflowStreamSubscribedEventAttributes' workflowStreamRecordsAppendedEventAttributes: $ref: '#/components/schemas/WorkflowStreamRecordsAppendedEventAttributes' + workflowNotificationChannelSubscribedEventAttributes: + $ref: '#/components/schemas/WorkflowNotificationChannelSubscribedEventAttributes' description: |- History events are the method by which Temporal SDKs advance (or recreate) workflow state. See the `EventType` enum for more info about what each event is for. @@ -14328,6 +14796,60 @@ components: type: string description: The request ID allocated at schedule time. description: Nexus operation timed out. + Notification: + type: object + properties: + channel: + type: string + description: The channel the writer notified. Listeners register on the same name. + position: + type: string + description: |- + Where the source stands after the write that caused this notification, + in the writer's terms. Opaque to the server. + format: bytes + counter: + type: string + description: |- + Orders notifications from one channel's writers. The writer derives it + from the position, since only the source can order its positions. Among + notifications folded together, the one with the highest counter is kept. + metadata: + type: object + additionalProperties: + $ref: '#/components/schemas/Payload' + description: |- + Details for the listener, such as which topic moved. Bounded in size and + carried as payloads, so a codec applies as to any payload. + description: |- + A notification tells the listeners of a channel that a source they consume + has moved. It is not data: the listener reads the source itself. A channel + is named by the writer and its listeners; for a stream, the provider formats + the stream's identity into the name. Writers never learn who listens. The + server folds notifications per listener while one is pending and no task has + been scheduled for it, keeping the one with the highest counter. + NotifyChannelRequest: + type: object + properties: + namespace: + type: string + notification: + $ref: '#/components/schemas/Notification' + identity: + type: string + description: The identity of the writer, for metrics and logs. + requestId: + type: string + description: Used to de-dupe a retried notification. + NotifyChannelResponse: + type: object + properties: + listenerCount: + type: integer + description: |- + Listeners registered when the notification was accepted. Zero means the + notification was retained for pollers and woke nobody. + format: int32 OnConflictOptions: type: object properties: @@ -14775,6 +15297,13 @@ components: description: The run ID of the activity, useful when run_id was not specified in the request. outcome: $ref: '#/components/schemas/ActivityExecutionOutcome' + PollChannelResponse: + type: object + properties: + notifications: + type: array + items: + $ref: '#/components/schemas/Notification' PollNexusOperationExecutionResponse: type: object properties: @@ -15353,6 +15882,28 @@ components: RecordWorkerHeartbeatResponse: type: object properties: {} + RegisterChannelListenerRequest: + type: object + properties: + namespace: + type: string + channel: + type: string + callback: + allOf: + - $ref: '#/components/schemas/Callback' + description: Invoked with each notification on the channel. + requestId: + type: string + description: Used to de-dupe a retried registration. + identity: + type: string + description: The identity of the caller, for metrics and logs. + RegisterChannelListenerResponse: + type: object + properties: + listenerId: + type: string RegisterNamespaceRequest: type: object properties: @@ -15635,6 +16186,7 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED + - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED type: string description: The event type of the history event generated by the request. format: enum @@ -18590,6 +19142,9 @@ components: type: object properties: {} description: Response to a successful UnpauseWorkflowExecution request. + UnregisterChannelListenerResponse: + type: object + properties: {} UpdateActivityExecutionOptionsRequest: type: object properties: @@ -19386,6 +19941,10 @@ components: pending and no task has received it yet, so a burst of writes costs one task. Once a task has been handed the wake, a new wake for the source is accepted again, because only the receiver knows what it read. + + Superseded by the notification channel (`NotifyChannel` and the + `SubscribeNotificationChannel` command). Kept for one round and slated for + removal. WakeWorkflowExecutionRequest: type: object properties: @@ -20120,6 +20679,7 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED + - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED type: string format: enum description: EventReference is a direct reference to a history event through the event ID. @@ -20193,6 +20753,7 @@ components: - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED + - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED type: string format: enum description: RequestIdReference is a indirect reference to a history event through the request ID. @@ -21178,6 +21739,28 @@ components: description: |- Holds all the information about worker versioning for a particular workflow execution. Experimental. Versioning info is experimental and might change in the future. + WorkflowListener: + type: object + properties: + workflowId: + type: string + runId: + type: string + description: |- + The run that subscribed. The server follows a continue-as-new to the + chain's current run when it delivers. + description: A Workflow Execution listening on a channel. + WorkflowNotificationChannelSubscribedEventAttributes: + type: object + properties: + workflowTaskCompletedEventId: + type: string + description: |- + The WorkflowTaskCompleted event of the task whose command created this + subscription. + channel: + type: string + description: The channel the Workflow listens on for the rest of this run. WorkflowPropertiesModifiedEventAttributes: type: object properties: @@ -21607,6 +22190,14 @@ components: type: integer description: Starting at 1, how many attempts there have been to complete this task format: int32 + notifications: + type: array + items: + $ref: '#/components/schemas/Notification' + description: |- + Notifications for channels this Workflow listens to, folded per channel + since the last task was scheduled. In History so a Workflow may act on + them deterministically and replay sees the same. WorkflowTaskStartedEventAttributes: type: object properties: diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 256af4403..8e17b93f1 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -327,6 +327,7 @@ message Command { RequestCancelNexusOperationCommandAttributes request_cancel_nexus_operation_command_attributes = 19; AppendStreamRecordsCommandAttributes append_stream_records_command_attributes = 20; SubscribeStreamCommandAttributes subscribe_stream_command_attributes = 21; + SubscribeNotificationChannelCommandAttributes subscribe_notification_channel_command_attributes = 22; } } @@ -369,3 +370,11 @@ message SubscribeStreamCommandAttributes { // non-zero `start_offset` fails the command. temporal.api.stream.v1.StreamStartPosition start_position = 3; } + +// Makes the Workflow a listener of a notification channel for this run. The +// next notifications on the channel arrive on the scheduled event of a Workflow +// Task. The subscription ends with the run, and a successor subscribes again. +message SubscribeNotificationChannelCommandAttributes { + // The channel to listen on, as the writers name it. + string channel = 1; +} diff --git a/temporal/api/enums/v1/command_type.proto b/temporal/api/enums/v1/command_type.proto index 967169b19..b8eada084 100644 --- a/temporal/api/enums/v1/command_type.proto +++ b/temporal/api/enums/v1/command_type.proto @@ -31,4 +31,5 @@ enum CommandType { COMMAND_TYPE_REQUEST_CANCEL_NEXUS_OPERATION = 18; COMMAND_TYPE_APPEND_STREAM_RECORDS = 19; COMMAND_TYPE_SUBSCRIBE_STREAM = 20; + COMMAND_TYPE_SUBSCRIBE_NOTIFICATION_CHANNEL = 21; } diff --git a/temporal/api/enums/v1/event_type.proto b/temporal/api/enums/v1/event_type.proto index a386b1db7..f70de73fe 100644 --- a/temporal/api/enums/v1/event_type.proto +++ b/temporal/api/enums/v1/event_type.proto @@ -183,4 +183,7 @@ enum EventType { // batch, and carrying only the offset range it landed at: the bodies go to // the stream's own log, never into History. EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED = 62; + // A Workflow became a listener of a notification channel for its run. + // The notifications themselves ride the WorkflowTaskScheduled event. + EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED = 63; } diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 2f432020d..981e6b4d0 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -18,6 +18,7 @@ import "temporal/api/enums/v1/update.proto"; import "temporal/api/enums/v1/workflow.proto"; import "temporal/api/common/v1/message.proto"; import "temporal/api/stream/v1/message.proto"; +import "temporal/api/notification/v1/message.proto"; import "temporal/api/deployment/v1/message.proto"; import "temporal/api/failure/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; @@ -303,6 +304,10 @@ message WorkflowTaskScheduledEventAttributes { google.protobuf.Duration start_to_close_timeout = 2; // Starting at 1, how many attempts there have been to complete this task int32 attempt = 3; + // Notifications for channels this Workflow listens to, folded per channel + // since the last task was scheduled. In History so a Workflow may act on + // them deterministically and replay sees the same. + repeated temporal.api.notification.v1.Notification notifications = 4; } message WorkflowTaskStartedEventAttributes { @@ -981,6 +986,14 @@ message WorkflowStreamSubscribedEventAttributes { int64 start_offset = 3; } +message WorkflowNotificationChannelSubscribedEventAttributes { + // The WorkflowTaskCompleted event of the task whose command created this + // subscription. + int64 workflow_task_completed_event_id = 1; + // The channel the Workflow listens on for the rest of this run. + string channel = 2; +} + message WorkflowStreamRecordsAppendedEventAttributes { // The WorkflowTaskCompleted event of the task whose command appended this // batch. @@ -1326,6 +1339,7 @@ message HistoryEvent { WorkflowExecutionTimeSkippingTransitionedEventAttributes workflow_execution_time_skipping_transitioned_event_attributes = 65; WorkflowStreamSubscribedEventAttributes workflow_stream_subscribed_event_attributes = 66; WorkflowStreamRecordsAppendedEventAttributes workflow_stream_records_appended_event_attributes = 67; + WorkflowNotificationChannelSubscribedEventAttributes workflow_notification_channel_subscribed_event_attributes = 68; } } diff --git a/temporal/api/notification/v1/message.proto b/temporal/api/notification/v1/message.proto new file mode 100644 index 000000000..63e1b7716 --- /dev/null +++ b/temporal/api/notification/v1/message.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +package temporal.api.notification.v1; + +option go_package = "go.temporal.io/api/notification/v1;notification"; +option java_package = "io.temporal.api.notification.v1"; +option java_multiple_files = true; +option java_outer_classname = "MessageProto"; +option ruby_package = "Temporalio::Api::Notification::V1"; +option csharp_namespace = "Temporalio.Api.Notification.V1"; + +import "google/protobuf/timestamp.proto"; + +import "temporal/api/common/v1/message.proto"; + +// A notification tells the listeners of a channel that a source they consume +// has moved. It is not data: the listener reads the source itself. A channel +// is named by the writer and its listeners; for a stream, the provider formats +// the stream's identity into the name. Writers never learn who listens. The +// server folds notifications per listener while one is pending and no task has +// been scheduled for it, keeping the one with the highest counter. +message Notification { + // The channel the writer notified. Listeners register on the same name. + string channel = 1; + // Where the source stands after the write that caused this notification, + // in the writer's terms. Opaque to the server. + bytes position = 2; + // Orders notifications from one channel's writers. The writer derives it + // from the position, since only the source can order its positions. Among + // notifications folded together, the one with the highest counter is kept. + int64 counter = 3; + // Details for the listener, such as which topic moved. Bounded in size and + // carried as payloads, so a codec applies as to any payload. + map metadata = 4; +} + +// A listener of a channel: a Workflow Execution woken with a Workflow Task, or +// a callback the server invokes with each notification. +message ChannelListener { + // Assigned by the server when the listener registers. + string listener_id = 1; + oneof listener { + WorkflowListener workflow = 2; + temporal.api.common.v1.Callback callback = 3; + } + google.protobuf.Timestamp registered_time = 4; +} + +// A Workflow Execution listening on a channel. +message WorkflowListener { + string workflow_id = 1; + // The run that subscribed. The server follows a continue-as-new to the + // chain's current run when it delivers. + string run_id = 2; +} diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index 8bd444186..c34f291b5 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -773,6 +773,10 @@ message WorkflowExecutionPauseInfo { // pending and no task has received it yet, so a burst of writes costs one // task. Once a task has been handed the wake, a new wake for the source is // accepted again, because only the receiver knows what it read. +// +// Superseded by the notification channel (`NotifyChannel` and the +// `SubscribeNotificationChannel` command). Kept for one round and slated for +// removal. message Wake { // What moved, as the sender names it. The server folds wakes by this value // and never interprets it. For a stream, the provider formats the stream's diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 35f2b7a4c..e661669d4 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -25,6 +25,7 @@ import "temporal/api/enums/v1/nexus.proto"; import "temporal/api/activity/v1/message.proto"; import "temporal/api/common/v1/message.proto"; import "temporal/api/stream/v1/message.proto"; +import "temporal/api/notification/v1/message.proto"; import "temporal/api/history/v1/message.proto"; import "temporal/api/workflow/v1/message.proto"; import "temporal/api/command/v1/message.proto"; @@ -913,6 +914,79 @@ message WakeWorkflowExecutionResponse { bool folded = 2; } +message NotifyChannelRequest { + string namespace = 1; + temporal.api.notification.v1.Notification notification = 2; + // The identity of the writer, for metrics and logs. + string identity = 3; + // Used to de-dupe a retried notification. + string request_id = 4; +} + +message NotifyChannelResponse { + // Listeners registered when the notification was accepted. Zero means the + // notification was retained for pollers and woke nobody. + int32 listener_count = 1; +} + +message RegisterChannelListenerRequest { + string namespace = 1; + string channel = 2; + // Invoked with each notification on the channel. + temporal.api.common.v1.Callback callback = 3; + // Used to de-dupe a retried registration. + string request_id = 4; + // The identity of the caller, for metrics and logs. + string identity = 5; +} + +message RegisterChannelListenerResponse { + string listener_id = 1; +} + +message UnregisterChannelListenerRequest { + string namespace = 1; + string channel = 2; + string listener_id = 3; + // The identity of the caller, for metrics and logs. + string identity = 4; +} + +message UnregisterChannelListenerResponse { +} + +message PollChannelRequest { + string namespace = 1; + string channel = 2; + // Only notifications with a counter above this one are returned. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "after" names the exclusive lower bound. --) + int64 after_counter = 3; + // How long to wait for a notification when none is retained above + // `after_counter`. + google.protobuf.Duration wait = 4; + // At most this many notifications are returned. Zero means the server's + // default. + int32 max_notifications = 5; +} + +message PollChannelResponse { + repeated temporal.api.notification.v1.Notification notifications = 1; +} + +message DescribeChannelRequest { + string namespace = 1; + string channel = 2; +} + +message DescribeChannelResponse { + repeated temporal.api.notification.v1.ChannelListener listeners = 1; + // The notification with the highest counter the channel retains. + temporal.api.notification.v1.Notification latest = 2; + // How many notifications the channel retains for pollers. + int32 retained_count = 3; +} + message SignalWithStartWorkflowExecutionRequest { string namespace = 1; string workflow_id = 2; diff --git a/temporal/api/workflowservice/v1/service.proto b/temporal/api/workflowservice/v1/service.proto index 5404601c4..3a873811a 100644 --- a/temporal/api/workflowservice/v1/service.proto +++ b/temporal/api/workflowservice/v1/service.proto @@ -488,6 +488,9 @@ service WorkflowService { // no event, carries no payload, and folds with other wakes for the same // source, so a burst of writes costs one task. The Workflow learns the // source and its position from the task and reads the source itself. + // + // Superseded by the notification channel (`NotifyChannel` and the + // `SubscribeNotificationChannel` command). Kept for one round and slated for removal. rpc WakeWorkflowExecution (WakeWorkflowExecutionRequest) returns (WakeWorkflowExecutionResponse) { option (google.api.http) = { post: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake" @@ -499,6 +502,72 @@ service WorkflowService { }; } + // NotifyChannel tells every listener of a channel that a source they consume + // has moved. The writer names no addressee and never learns who listens. The + // server wakes each listener: a Workflow with a Workflow Task, a callback by + // invoking it. Nothing goes to History except the notifications a woken + // Workflow Task carries on its scheduled event. + rpc NotifyChannel (NotifyChannelRequest) returns (NotifyChannelResponse) { + option (google.api.http) = { + post: "/namespaces/{namespace}/channels/{notification.channel}/notify" + body: "*" + additional_bindings { + post: "/api/v1/namespaces/{namespace}/channels/{notification.channel}/notify" + body: "*" + } + }; + } + + // RegisterChannelListener registers a callback as a listener of a channel. A + // Workflow registers itself with the `SubscribeNotificationChannel` command + // instead. + rpc RegisterChannelListener (RegisterChannelListenerRequest) returns (RegisterChannelListenerResponse) { + option (google.api.http) = { + post: "/namespaces/{namespace}/channels/{channel}/listeners" + body: "*" + additional_bindings { + post: "/api/v1/namespaces/{namespace}/channels/{channel}/listeners" + body: "*" + } + }; + } + + // UnregisterChannelListener removes a listener from a channel. + // + // (-- api-linter: core::0136::http-method=disabled + // aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + rpc UnregisterChannelListener (UnregisterChannelListenerRequest) returns (UnregisterChannelListenerResponse) { + option (google.api.http) = { + delete: "/namespaces/{namespace}/channels/{channel}/listeners/{listener_id}" + additional_bindings { + delete: "/api/v1/namespaces/{namespace}/channels/{channel}/listeners/{listener_id}" + } + }; + } + + // PollChannel is a long poll for clients. It returns the retained + // notifications of a channel with a counter above `after_counter`, waiting + // up to `wait` for one when none is retained yet. + rpc PollChannel (PollChannelRequest) returns (PollChannelResponse) { + option (google.api.http) = { + get: "/namespaces/{namespace}/channels/{channel}/notifications" + additional_bindings { + get: "/api/v1/namespaces/{namespace}/channels/{channel}/notifications" + } + }; + } + + // DescribeChannel returns the listeners of a channel and its latest + // notification. + rpc DescribeChannel (DescribeChannelRequest) returns (DescribeChannelResponse) { + option (google.api.http) = { + get: "/namespaces/{namespace}/channels/{channel}" + additional_bindings { + get: "/api/v1/namespaces/{namespace}/channels/{channel}" + } + }; + } + // SignalWithStartWorkflowExecution is used to ensure a signal is sent to a workflow, even if // it isn't yet started. // From d0c100d4c8d044ea4990ab42051209db30fe6f30 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Thu, 1 Oct 2026 17:31:47 -0700 Subject: [PATCH 21/30] Wrapped the notification channel lines that ran past 100 characters. --- temporal/api/command/v1/message.proto | 3 ++- temporal/api/history/v1/message.proto | 3 ++- temporal/api/workflowservice/v1/service.proto | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 8e17b93f1..38bdc0b35 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -327,7 +327,8 @@ message Command { RequestCancelNexusOperationCommandAttributes request_cancel_nexus_operation_command_attributes = 19; AppendStreamRecordsCommandAttributes append_stream_records_command_attributes = 20; SubscribeStreamCommandAttributes subscribe_stream_command_attributes = 21; - SubscribeNotificationChannelCommandAttributes subscribe_notification_channel_command_attributes = 22; + SubscribeNotificationChannelCommandAttributes + subscribe_notification_channel_command_attributes = 22; } } diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 981e6b4d0..0ec2ccf5e 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -1339,7 +1339,8 @@ message HistoryEvent { WorkflowExecutionTimeSkippingTransitionedEventAttributes workflow_execution_time_skipping_transitioned_event_attributes = 65; WorkflowStreamSubscribedEventAttributes workflow_stream_subscribed_event_attributes = 66; WorkflowStreamRecordsAppendedEventAttributes workflow_stream_records_appended_event_attributes = 67; - WorkflowNotificationChannelSubscribedEventAttributes workflow_notification_channel_subscribed_event_attributes = 68; + WorkflowNotificationChannelSubscribedEventAttributes + workflow_notification_channel_subscribed_event_attributes = 68; } } diff --git a/temporal/api/workflowservice/v1/service.proto b/temporal/api/workflowservice/v1/service.proto index 3a873811a..e3abf2800 100644 --- a/temporal/api/workflowservice/v1/service.proto +++ b/temporal/api/workflowservice/v1/service.proto @@ -521,7 +521,8 @@ service WorkflowService { // RegisterChannelListener registers a callback as a listener of a channel. A // Workflow registers itself with the `SubscribeNotificationChannel` command // instead. - rpc RegisterChannelListener (RegisterChannelListenerRequest) returns (RegisterChannelListenerResponse) { + rpc RegisterChannelListener (RegisterChannelListenerRequest) + returns (RegisterChannelListenerResponse) { option (google.api.http) = { post: "/namespaces/{namespace}/channels/{channel}/listeners" body: "*" @@ -536,7 +537,8 @@ service WorkflowService { // // (-- api-linter: core::0136::http-method=disabled // aip.dev/not-precedent: Removing a listener is a delete of that listener. --) - rpc UnregisterChannelListener (UnregisterChannelListenerRequest) returns (UnregisterChannelListenerResponse) { + rpc UnregisterChannelListener (UnregisterChannelListenerRequest) + returns (UnregisterChannelListenerResponse) { option (google.api.http) = { delete: "/namespaces/{namespace}/channels/{channel}/listeners/{listener_id}" additional_bindings { From 5fa40260701d3e4e106d39432cf535a7b3f8ddd0 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Thu, 1 Oct 2026 17:45:38 -0700 Subject: [PATCH 22/30] Added the failed cause for a refused SubscribeNotificationChannel command. --- openapi/openapiv2.json | 5 +++-- openapi/openapiv3.yaml | 1 + temporal/api/enums/v1/failed_cause.proto | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 65b327f8e..1ad1a8662 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -23543,10 +23543,11 @@ "WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE", "WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES", "WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES", - "WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE" + "WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE", + "WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES" ], "default": "WORKFLOW_TASK_FAILED_CAUSE_UNSPECIFIED", - "description": "Workflow tasks can fail for various reasons. Note that some of these reasons can only originate\nfrom the server, and some of them can only originate from the SDK/worker.\n\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND: Between starting and completing the workflow task (with a workflow completion command), some\nnew command (like a signal) was processed into workflow history. The outstanding task will be\nfailed with this reason, and a worker must pick up a new task.\n - WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE: The worker wishes to fail the task and have the next one be generated on a normal, not sticky\nqueue. Generally workers should prefer to use the explicit `ResetStickyTaskQueue` RPC call.\n - WORKFLOW_TASK_FAILED_CAUSE_NON_DETERMINISTIC_ERROR: The worker encountered a mismatch while replaying history between what was expected, and\nwhat the workflow code actually did.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_CHILD_WORKFLOWS_LIMIT_EXCEEDED: We send the below error codes to users when their requests would violate a size constraint\nof their workflow. We do this to ensure that the state of their workflow does not become too\nlarge because that can cause severe performance degradation. You can modify the thresholds for\neach of these errors within your dynamic config.\n\nSpawning a new child workflow would cause this workflow to exceed its limit of pending child\nworkflows.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_ACTIVITIES_LIMIT_EXCEEDED: Starting a new activity would cause this workflow to exceed its limit of pending activities\nthat we track.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_SIGNALS_LIMIT_EXCEEDED: A workflow has a buffer of signals that have not yet reached their destination. We return this\nerror when sending a new signal would exceed the capacity of this buffer.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_REQUEST_CANCEL_LIMIT_EXCEEDED: Similarly, we have a buffer of pending requests to cancel other workflows. We return this error\nwhen our capacity for pending cancel requests is already reached.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UPDATE_WORKFLOW_EXECUTION_MESSAGE: Workflow execution update message (update.Acceptance, update.Rejection, or update.Response)\nhas wrong format, or missing required fields.\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_UPDATE: Similar to WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND, but for updates.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid ScheduleNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_NEXUS_OPERATIONS_LIMIT_EXCEEDED: A workflow task completed requesting to schedule a Nexus Operation exceeding the server configured limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid RequestCancelNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_FEATURE_DISABLED: A workflow task completed requesting a feature that's disabled on the server (either system wide or - typically -\nfor the workflow's namespace).\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE: A workflow task failed because a grpc message was too large.\n - WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE: A workflow task failed because payloads were too large.\n - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE: A workflow task failed because an external storage operation failed.\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED: A workflow task is failed because the workflow is paused before the task is started.\n - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE: A workflow task failed because the request exceeded a size limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES: A workflow task completed with an invalid AppendStreamRecords command.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES: A workflow task completed with an invalid SubscribeStream command.\n - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE: A workflow task could not be started because a stream range it consumed and recorded in\nHistory can no longer be served, for example after truncation or because it exceeds the\nreplay bound. Check the workflow task failure message for more information." + "description": "Workflow tasks can fail for various reasons. Note that some of these reasons can only originate\nfrom the server, and some of them can only originate from the SDK/worker.\n\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND: Between starting and completing the workflow task (with a workflow completion command), some\nnew command (like a signal) was processed into workflow history. The outstanding task will be\nfailed with this reason, and a worker must pick up a new task.\n - WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE: The worker wishes to fail the task and have the next one be generated on a normal, not sticky\nqueue. Generally workers should prefer to use the explicit `ResetStickyTaskQueue` RPC call.\n - WORKFLOW_TASK_FAILED_CAUSE_NON_DETERMINISTIC_ERROR: The worker encountered a mismatch while replaying history between what was expected, and\nwhat the workflow code actually did.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_CHILD_WORKFLOWS_LIMIT_EXCEEDED: We send the below error codes to users when their requests would violate a size constraint\nof their workflow. We do this to ensure that the state of their workflow does not become too\nlarge because that can cause severe performance degradation. You can modify the thresholds for\neach of these errors within your dynamic config.\n\nSpawning a new child workflow would cause this workflow to exceed its limit of pending child\nworkflows.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_ACTIVITIES_LIMIT_EXCEEDED: Starting a new activity would cause this workflow to exceed its limit of pending activities\nthat we track.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_SIGNALS_LIMIT_EXCEEDED: A workflow has a buffer of signals that have not yet reached their destination. We return this\nerror when sending a new signal would exceed the capacity of this buffer.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_REQUEST_CANCEL_LIMIT_EXCEEDED: Similarly, we have a buffer of pending requests to cancel other workflows. We return this error\nwhen our capacity for pending cancel requests is already reached.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UPDATE_WORKFLOW_EXECUTION_MESSAGE: Workflow execution update message (update.Acceptance, update.Rejection, or update.Response)\nhas wrong format, or missing required fields.\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_UPDATE: Similar to WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND, but for updates.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid ScheduleNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_NEXUS_OPERATIONS_LIMIT_EXCEEDED: A workflow task completed requesting to schedule a Nexus Operation exceeding the server configured limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid RequestCancelNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_FEATURE_DISABLED: A workflow task completed requesting a feature that's disabled on the server (either system wide or - typically -\nfor the workflow's namespace).\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE: A workflow task failed because a grpc message was too large.\n - WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE: A workflow task failed because payloads were too large.\n - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE: A workflow task failed because an external storage operation failed.\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED: A workflow task is failed because the workflow is paused before the task is started.\n - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE: A workflow task failed because the request exceeded a size limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES: A workflow task completed with an invalid AppendStreamRecords command.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES: A workflow task completed with an invalid SubscribeStream command.\n - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE: A workflow task could not be started because a stream range it consumed and recorded in\nHistory can no longer be served, for example after truncation or because it exceeds the\nreplay bound. Check the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES: A SubscribeNotificationChannel command named an empty or too-long channel, or hit a\nsubscription or listener limit." }, "v1WorkflowTaskFailedEventAttributes": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 67f2f7245..71fccf30d 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -22136,6 +22136,7 @@ components: - WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE + - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES type: string format: enum failure: diff --git a/temporal/api/enums/v1/failed_cause.proto b/temporal/api/enums/v1/failed_cause.proto index 16f876a87..be28778de 100644 --- a/temporal/api/enums/v1/failed_cause.proto +++ b/temporal/api/enums/v1/failed_cause.proto @@ -98,6 +98,9 @@ enum WorkflowTaskFailedCause { // History can no longer be served, for example after truncation or because it exceeds the // replay bound. Check the workflow task failure message for more information. WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE = 43; + // A SubscribeNotificationChannel command named an empty or too-long channel, or hit a + // subscription or listener limit. + WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES = 44; } // Activity tasks can fail for various reasons. Note that some of these reasons can only originate From 9cd8b4005fca87c519118480e07b1b2ce1ad32ac Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Thu, 1 Oct 2026 22:03:42 -0700 Subject: [PATCH 23/30] Removed the point-to-point wake call and message. The notification channel covers what the wake was added for, so the PoC does not need a second path. Field 21 on the poll response stays reserved. --- openapi/openapiv2.json | 154 ---------------- openapi/openapiv3.yaml | 165 ------------------ temporal/api/workflow/v1/message.proto | 29 --- .../workflowservice/v1/request_response.proto | 29 +-- temporal/api/workflowservice/v1/service.proto | 19 -- 5 files changed, 2 insertions(+), 394 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 1ad1a8662..94239c782 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -4813,52 +4813,6 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/wake": { - "post": { - "summary": "WakeWorkflowExecution asks a running Workflow Execution to run a Workflow\nTask because a source it consumes has moved. Unlike a Signal it records\nno event, carries no payload, and folds with other wakes for the same\nsource, so a burst of writes costs one task. The Workflow learns the\nsource and its position from the task and reads the source itself.", - "description": "Superseded by the notification channel (`NotifyChannel` and the\n`SubscribeNotificationChannel` command). Kept for one round and slated for removal.", - "operationId": "WakeWorkflowExecution2", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1WakeWorkflowExecutionResponse" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/rpcStatus" - } - } - }, - "parameters": [ - { - "name": "namespace", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "workflowExecution.workflowId", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceWakeWorkflowExecutionBody" - } - } - ], - "tags": [ - "WorkflowService" - ] - } - }, "/api/v1/namespaces/{namespace}/workflows/{workflowId}": { "post": { "summary": "StartWorkflowExecution starts a new workflow execution.", @@ -10680,52 +10634,6 @@ ] } }, - "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/wake": { - "post": { - "summary": "WakeWorkflowExecution asks a running Workflow Execution to run a Workflow\nTask because a source it consumes has moved. Unlike a Signal it records\nno event, carries no payload, and folds with other wakes for the same\nsource, so a burst of writes costs one task. The Workflow learns the\nsource and its position from the task and reads the source itself.", - "description": "Superseded by the notification channel (`NotifyChannel` and the\n`SubscribeNotificationChannel` command). Kept for one round and slated for removal.", - "operationId": "WakeWorkflowExecution", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1WakeWorkflowExecutionResponse" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/rpcStatus" - } - } - }, - "parameters": [ - { - "name": "namespace", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "workflowExecution.workflowId", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceWakeWorkflowExecutionBody" - } - } - ], - "tags": [ - "WorkflowService" - ] - } - }, "/namespaces/{namespace}/workflows/{workflowId}": { "post": { "summary": "StartWorkflowExecution starts a new workflow execution.", @@ -14107,27 +14015,6 @@ }, "description": "Used to validate the compute config without attaching it to a Worker Deployment Version." }, - "WorkflowServiceWakeWorkflowExecutionBody": { - "type": "object", - "properties": { - "workflowExecution": { - "type": "object", - "properties": { - "runId": { - "type": "string" - } - }, - "description": "The Workflow to wake. `run_id` is optional. When set, the wake goes to\nthe current run of the chain that run belongs to, so a sender that\nlearned a run id before a continue-as-new still reaches the consumer,\nand it is refused with NotFound once that chain has ended. When unset,\nthe wake goes to the current run under the Workflow Id, whichever chain." - }, - "wake": { - "$ref": "#/definitions/v1Wake" - }, - "identity": { - "type": "string", - "description": "The identity of the sender, for metrics and logs." - } - } - }, "apiActivityV1CallbackInfo": { "type": "object", "properties": { @@ -19312,14 +19199,6 @@ "$ref": "#/definitions/v1StreamSlice" }, "description": "Stream data attached to this task. Delivered out of band so the payloads\nnever enter History; only the offset ranges are recorded there." - }, - "wakes": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/v1Wake" - }, - "description": "Wakes pending for this execution when the task started, folded by\nsource. Not recorded in History and never re-supplied on replay: the\nWorkflow treats one as a reason to read its source now and records what\nit read itself." } } }, @@ -21821,39 +21700,6 @@ }, "description": "Specifies client's intent to wait for Update results." }, - "v1Wake": { - "type": "object", - "properties": { - "source": { - "type": "string", - "description": "What moved, as the sender names it. The server folds wakes by this value\nand never interprets it. For a stream, the provider formats the stream's\nidentity, owner and topic, into it." - }, - "position": { - "type": "string", - "format": "byte", - "description": "Where the source stands after the write that caused this wake, in the\nstore's own terms. Opaque to the server and handed to the Workflow as\nreceived. Among wakes folded together, the position with the highest\ncounter is the one delivered." - }, - "counter": { - "type": "string", - "format": "int64", - "description": "Orders wakes from one source, so that folding keeps the latest position.\nOnly the source's store can order its positions, so the sender derives\nthis from the position. It is not a durable identity: a wake sent after\na task received an equal counter is accepted again." - } - }, - "description": "A wake tells a running Workflow Execution that something it consumes has\nmoved, so it should run a Workflow Task and read from its own cursor. It is\na reason to run, not data: nothing is recorded in History and no record\ntravels with it. A store Temporal does not host sends one after the store\nhas acknowledged a write. Wakes for the same source fold while one is\npending and no task has received it yet, so a burst of writes costs one\ntask. Once a task has been handed the wake, a new wake for the source is\naccepted again, because only the receiver knows what it read.\n\nSuperseded by the notification channel (`NotifyChannel` and the\n`SubscribeNotificationChannel` command). Kept for one round and slated for\nremoval." - }, - "v1WakeWorkflowExecutionResponse": { - "type": "object", - "properties": { - "runId": { - "type": "string", - "description": "The run the wake was stored on." - }, - "folded": { - "type": "boolean", - "description": "True when a wake for the source was already pending and no task had\nreceived it yet, so this one changed nothing but possibly the position\nand no new task results from it." - } - } - }, "v1WorkerConfig": { "type": "object", "properties": { diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 71fccf30d..1fd270c6f 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -4892,50 +4892,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake: - post: - tags: - - WorkflowService - description: |- - WakeWorkflowExecution asks a running Workflow Execution to run a Workflow - Task because a source it consumes has moved. Unlike a Signal it records - no event, carries no payload, and folds with other wakes for the same - source, so a burst of writes costs one task. The Workflow learns the - source and its position from the task and reads the source itself. - - Superseded by the notification channel (`NotifyChannel` and the - `SubscribeNotificationChannel` command). Kept for one round and slated for removal. - operationId: WakeWorkflowExecution - parameters: - - name: namespace - in: path - required: true - schema: - type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/WakeWorkflowExecutionRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/WakeWorkflowExecutionResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' /api/v1/nexus/endpoints: get: tags: @@ -10173,50 +10129,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake: - post: - tags: - - WorkflowService - description: |- - WakeWorkflowExecution asks a running Workflow Execution to run a Workflow - Task because a source it consumes has moved. Unlike a Signal it records - no event, carries no payload, and folds with other wakes for the same - source, so a burst of writes costs one task. The Workflow learns the - source and its position from the task and reads the source itself. - - Superseded by the notification channel (`NotifyChannel` and the - `SubscribeNotificationChannel` command). Kept for one round and slated for removal. - operationId: WakeWorkflowExecution - parameters: - - name: namespace - in: path - required: true - schema: - type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/WakeWorkflowExecutionRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/WakeWorkflowExecutionResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' /system-info: get: tags: @@ -15477,15 +15389,6 @@ components: description: |- Stream data attached to this task. Delivered out of band so the payloads never enter History; only the offset ranges are recorded there. - wakes: - type: array - items: - $ref: '#/components/schemas/Wake' - description: |- - Wakes pending for this execution when the task started, folded by - source. Not recorded in History and never re-supplied on replay: the - Workflow treats one as a reason to read its source now and records what - it read itself. PollerGroupInfo: type: object properties: @@ -19908,74 +19811,6 @@ components: user specified timeout, API call returns even if specified stage is not reached. format: enum description: Specifies client's intent to wait for Update results. - Wake: - type: object - properties: - source: - type: string - description: |- - What moved, as the sender names it. The server folds wakes by this value - and never interprets it. For a stream, the provider formats the stream's - identity, owner and topic, into it. - position: - type: string - description: |- - Where the source stands after the write that caused this wake, in the - store's own terms. Opaque to the server and handed to the Workflow as - received. Among wakes folded together, the position with the highest - counter is the one delivered. - format: bytes - counter: - type: string - description: |- - Orders wakes from one source, so that folding keeps the latest position. - Only the source's store can order its positions, so the sender derives - this from the position. It is not a durable identity: a wake sent after - a task received an equal counter is accepted again. - description: |- - A wake tells a running Workflow Execution that something it consumes has - moved, so it should run a Workflow Task and read from its own cursor. It is - a reason to run, not data: nothing is recorded in History and no record - travels with it. A store Temporal does not host sends one after the store - has acknowledged a write. Wakes for the same source fold while one is - pending and no task has received it yet, so a burst of writes costs one - task. Once a task has been handed the wake, a new wake for the source is - accepted again, because only the receiver knows what it read. - - Superseded by the notification channel (`NotifyChannel` and the - `SubscribeNotificationChannel` command). Kept for one round and slated for - removal. - WakeWorkflowExecutionRequest: - type: object - properties: - namespace: - type: string - workflowExecution: - allOf: - - $ref: '#/components/schemas/WorkflowExecution' - description: |- - The Workflow to wake. `run_id` is optional. When set, the wake goes to - the current run of the chain that run belongs to, so a sender that - learned a run id before a continue-as-new still reaches the consumer, - and it is refused with NotFound once that chain has ended. When unset, - the wake goes to the current run under the Workflow Id, whichever chain. - wake: - $ref: '#/components/schemas/Wake' - identity: - type: string - description: The identity of the sender, for metrics and logs. - WakeWorkflowExecutionResponse: - type: object - properties: - runId: - type: string - description: The run the wake was stored on. - folded: - type: boolean - description: |- - True when a wake for the source was already pending and no task had - received it yet, so this one changed nothing but possibly the position - and no new task results from it. WorkerConfig: type: object properties: diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index c34f291b5..cf763aa12 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -764,32 +764,3 @@ message WorkflowExecutionPauseInfo { // The reason for pausing the workflow execution. string reason = 3; } - -// A wake tells a running Workflow Execution that something it consumes has -// moved, so it should run a Workflow Task and read from its own cursor. It is -// a reason to run, not data: nothing is recorded in History and no record -// travels with it. A store Temporal does not host sends one after the store -// has acknowledged a write. Wakes for the same source fold while one is -// pending and no task has received it yet, so a burst of writes costs one -// task. Once a task has been handed the wake, a new wake for the source is -// accepted again, because only the receiver knows what it read. -// -// Superseded by the notification channel (`NotifyChannel` and the -// `SubscribeNotificationChannel` command). Kept for one round and slated for -// removal. -message Wake { - // What moved, as the sender names it. The server folds wakes by this value - // and never interprets it. For a stream, the provider formats the stream's - // identity, owner and topic, into it. - string source = 1; - // Where the source stands after the write that caused this wake, in the - // store's own terms. Opaque to the server and handed to the Workflow as - // received. Among wakes folded together, the position with the highest - // counter is the one delivered. - bytes position = 2; - // Orders wakes from one source, so that folding keeps the latest position. - // Only the source's store can order its positions, so the sender derives - // this from the position. It is not a durable identity: a wake sent after - // a task received an equal counter is accepted again. - int64 counter = 3; -} diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index e661669d4..f7bbe7d33 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -391,11 +391,8 @@ message PollWorkflowTaskQueueResponse { // never enter History; only the offset ranges are recorded there. repeated temporal.api.stream.v1.StreamSlice stream_slices = 20; - // Wakes pending for this execution when the task started, folded by - // source. Not recorded in History and never re-supplied on replay: the - // Workflow treats one as a reason to read its source now and records what - // it read itself. - repeated temporal.api.workflow.v1.Wake wakes = 21; + // Used once by a repeated field this fork has since removed. + reserved 21; } message RespondWorkflowTaskCompletedRequest { @@ -892,28 +889,6 @@ message SignalWorkflowExecutionResponse { temporal.api.common.v1.Link link = 1; } -message WakeWorkflowExecutionRequest { - string namespace = 1; - // The Workflow to wake. `run_id` is optional. When set, the wake goes to - // the current run of the chain that run belongs to, so a sender that - // learned a run id before a continue-as-new still reaches the consumer, - // and it is refused with NotFound once that chain has ended. When unset, - // the wake goes to the current run under the Workflow Id, whichever chain. - temporal.api.common.v1.WorkflowExecution workflow_execution = 2; - temporal.api.workflow.v1.Wake wake = 3; - // The identity of the sender, for metrics and logs. - string identity = 4; -} - -message WakeWorkflowExecutionResponse { - // The run the wake was stored on. - string run_id = 1; - // True when a wake for the source was already pending and no task had - // received it yet, so this one changed nothing but possibly the position - // and no new task results from it. - bool folded = 2; -} - message NotifyChannelRequest { string namespace = 1; temporal.api.notification.v1.Notification notification = 2; diff --git a/temporal/api/workflowservice/v1/service.proto b/temporal/api/workflowservice/v1/service.proto index e3abf2800..1f55b34c0 100644 --- a/temporal/api/workflowservice/v1/service.proto +++ b/temporal/api/workflowservice/v1/service.proto @@ -483,25 +483,6 @@ service WorkflowService { }; } - // WakeWorkflowExecution asks a running Workflow Execution to run a Workflow - // Task because a source it consumes has moved. Unlike a Signal it records - // no event, carries no payload, and folds with other wakes for the same - // source, so a burst of writes costs one task. The Workflow learns the - // source and its position from the task and reads the source itself. - // - // Superseded by the notification channel (`NotifyChannel` and the - // `SubscribeNotificationChannel` command). Kept for one round and slated for removal. - rpc WakeWorkflowExecution (WakeWorkflowExecutionRequest) returns (WakeWorkflowExecutionResponse) { - option (google.api.http) = { - post: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake" - body: "*" - additional_bindings { - post: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/wake" - body: "*" - } - }; - } - // NotifyChannel tells every listener of a channel that a source they consume // has moved. The writer names no addressee and never learns who listens. The // server wakes each listener: a Workflow with a Workflow Task, a callback by From a9e6517177bdc41a73429d2fe64e40679bba389d Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Thu, 1 Oct 2026 22:23:31 -0700 Subject: [PATCH 24/30] Added the channel linked to a workflow to the notification contract. A linked channel lives in one workflow's state and is addressed by workflow id, so the five channel calls take an optional `workflow_execution` and get routes under the workflow's path. `Notification.linked_to` lets a listener that holds both kinds route a notification, and `DescribeChannel` reports the kind and the owner. --- openapi/openapiv2.json | 1119 +++++++++++++---- openapi/openapiv3.yaml | 1016 ++++++++++++--- temporal/api/notification/v1/message.proto | 17 + .../workflowservice/v1/request_response.proto | 26 + temporal/api/workflowservice/v1/service.proto | 34 + 5 files changed, 1794 insertions(+), 418 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 94239c782..6fdd612c3 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -1479,6 +1479,18 @@ "in": "path", "required": true, "type": "string" + }, + { + "name": "workflowExecution.workflowId", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -1574,6 +1586,18 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "workflowExecution.workflowId", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -1634,6 +1658,18 @@ "required": false, "type": "integer", "format": "int32" + }, + { + "name": "workflowExecution.workflowId", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -4522,15 +4558,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/reset": { - "post": { - "summary": "ResetWorkflowExecution will reset an existing workflow execution to a specified\n`WORKFLOW_TASK_COMPLETED` event (exclusive). It will immediately terminate the current\nexecution instance. \"Exclusive\" means the identified completed event itself is not replayed\nin the reset history; the preceding `WORKFLOW_TASK_STARTED` event remains and will be marked as failed\nimmediately, and a new workflow task will be scheduled to retry it.", - "operationId": "ResetWorkflowExecution2", + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}": { + "get": { + "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", + "operationId": "DescribeChannel4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1ResetWorkflowExecutionResponse" + "$ref": "#/definitions/v1DescribeChannelResponse" } }, "default": { @@ -4554,12 +4590,16 @@ "type": "string" }, { - "name": "body", - "in": "body", + "name": "channel", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceResetWorkflowExecutionBody" - } + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -4567,16 +4607,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/signal/{signalName}": { + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/listeners": { "post": { - "summary": "SignalWorkflowExecution is used to send a signal to a running workflow execution.", - "description": "This results in a `WORKFLOW_EXECUTION_SIGNALED` event recorded in the history and a workflow\ntask being created for the execution.", - "operationId": "SignalWorkflowExecution2", + "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", + "operationId": "RegisterChannelListener4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1SignalWorkflowExecutionResponse" + "$ref": "#/definitions/v1RegisterChannelListenerResponse" } }, "default": { @@ -4600,8 +4639,7 @@ "type": "string" }, { - "name": "signalName", - "description": "The workflow author-defined name of the signal to send to the workflow", + "name": "channel", "in": "path", "required": true, "type": "string" @@ -4611,7 +4649,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceSignalWorkflowExecutionBody" + "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" } } ], @@ -4620,15 +4658,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/terminate": { - "post": { - "summary": "TerminateWorkflowExecution terminates an existing workflow execution by recording a\n`WORKFLOW_EXECUTION_TERMINATED` event in the history and immediately terminating the\nexecution instance.", - "operationId": "TerminateWorkflowExecution2", + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/listeners/{listenerId}": { + "delete": { + "summary": "UnregisterChannelListener removes a listener from a channel.", + "operationId": "UnregisterChannelListener4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1TerminateWorkflowExecutionResponse" + "$ref": "#/definitions/v1UnregisterChannelListenerResponse" } }, "default": { @@ -4652,12 +4690,29 @@ "type": "string" }, { - "name": "body", - "in": "body", + "name": "channel", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceTerminateWorkflowExecutionBody" - } + "type": "string" + }, + { + "name": "listenerId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "identity", + "description": "The identity of the caller, for metrics and logs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -4665,14 +4720,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/time-skipping/poll": { + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/notifications": { "get": { - "operationId": "PollWorkflowExecutionTimeSkipping2", + "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", + "operationId": "PollChannel4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1PollWorkflowExecutionTimeSkippingResponse" + "$ref": "#/definitions/v1PollChannelResponse" } }, "default": { @@ -4696,14 +4752,36 @@ "type": "string" }, { - "name": "workflowExecution.runId", + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "afterCounter", + "description": "Only notifications with a counter above this one are returned.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "wait", + "description": "How long to wait for a notification when none is retained above\n`after_counter`.", "in": "query", "required": false, "type": "string" }, { - "name": "fastForwardId", - "description": "Required. Identifies the fast-forward whose completion the caller wants to wait for.\nMust match the `fast_forward_id` set in the execution's TimeSkippingConfig.", + "name": "maxNotifications", + "description": "At most this many notifications are returned. Zero means the server's\ndefault.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "workflowExecution.runId", "in": "query", "required": false, "type": "string" @@ -4714,15 +4792,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/update-options": { + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{notification.channel}/notify": { "post": { - "summary": "UpdateWorkflowExecutionOptions partially updates the WorkflowExecutionOptions of an existing workflow execution.", - "operationId": "UpdateWorkflowExecutionOptions2", + "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", + "operationId": "NotifyChannel4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1UpdateWorkflowExecutionOptionsResponse" + "$ref": "#/definitions/v1NotifyChannelResponse" } }, "default": { @@ -4735,7 +4813,6 @@ "parameters": [ { "name": "namespace", - "description": "The namespace name of the target Workflow.", "in": "path", "required": true, "type": "string" @@ -4746,12 +4823,19 @@ "required": true, "type": "string" }, + { + "name": "notification.channel", + "description": "The channel the writer notified. Listeners register on the same name.", + "in": "path", + "required": true, + "type": "string" + }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceUpdateWorkflowExecutionOptionsBody" + "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" } } ], @@ -4760,15 +4844,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/update/{request.input.name}": { + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/reset": { "post": { - "summary": "Invokes the specified Update function on user Workflow code.", - "operationId": "UpdateWorkflowExecution2", + "summary": "ResetWorkflowExecution will reset an existing workflow execution to a specified\n`WORKFLOW_TASK_COMPLETED` event (exclusive). It will immediately terminate the current\nexecution instance. \"Exclusive\" means the identified completed event itself is not replayed\nin the reset history; the preceding `WORKFLOW_TASK_STARTED` event remains and will be marked as failed\nimmediately, and a new workflow task will be scheduled to retry it.", + "operationId": "ResetWorkflowExecution2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1UpdateWorkflowExecutionResponse" + "$ref": "#/definitions/v1ResetWorkflowExecutionResponse" } }, "default": { @@ -4781,7 +4865,6 @@ "parameters": [ { "name": "namespace", - "description": "The namespace name of the target Workflow.", "in": "path", "required": true, "type": "string" @@ -4792,19 +4875,12 @@ "required": true, "type": "string" }, - { - "name": "request.input.name", - "description": "The name of the Update handler to invoke on the target Workflow.", - "in": "path", - "required": true, - "type": "string" - }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceUpdateWorkflowExecutionBody" + "$ref": "#/definitions/WorkflowServiceResetWorkflowExecutionBody" } } ], @@ -4813,16 +4889,16 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowId}": { + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/signal/{signalName}": { "post": { - "summary": "StartWorkflowExecution starts a new workflow execution.", - "description": "It will create the execution with a `WORKFLOW_EXECUTION_STARTED` event in its history and\nalso schedule the first workflow task. Returns `WorkflowExecutionAlreadyStarted`, if an\ninstance already exists with same workflow id.", - "operationId": "StartWorkflowExecution2", + "summary": "SignalWorkflowExecution is used to send a signal to a running workflow execution.", + "description": "This results in a `WORKFLOW_EXECUTION_SIGNALED` event recorded in the history and a workflow\ntask being created for the execution.", + "operationId": "SignalWorkflowExecution2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1StartWorkflowExecutionResponse" + "$ref": "#/definitions/v1SignalWorkflowExecutionResponse" } }, "default": { @@ -4840,7 +4916,14 @@ "type": "string" }, { - "name": "workflowId", + "name": "workflowExecution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "signalName", + "description": "The workflow author-defined name of the signal to send to the workflow", "in": "path", "required": true, "type": "string" @@ -4850,7 +4933,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceStartWorkflowExecutionBody" + "$ref": "#/definitions/WorkflowServiceSignalWorkflowExecutionBody" } } ], @@ -4859,15 +4942,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/complete": { + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/terminate": { "post": { - "summary": "See `RespondActivityTaskCompleted`. This version allows clients to record completions by\nnamespace/workflow id/activity id instead of task token.", - "operationId": "RespondActivityTaskCompletedById4", + "summary": "TerminateWorkflowExecution terminates an existing workflow execution by recording a\n`WORKFLOW_EXECUTION_TERMINATED` event in the history and immediately terminating the\nexecution instance.", + "operationId": "TerminateWorkflowExecution2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RespondActivityTaskCompletedByIdResponse" + "$ref": "#/definitions/v1TerminateWorkflowExecutionResponse" } }, "default": { @@ -4880,21 +4963,12 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "workflowId", - "description": "Id of the workflow which scheduled this activity, leave empty to target a standalone activity", "in": "path", "required": true, "type": "string" }, { - "name": "activityId", - "description": "Id of the activity to complete", + "name": "workflowExecution.workflowId", "in": "path", "required": true, "type": "string" @@ -4904,7 +4978,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceRespondActivityTaskCompletedByIdBody" + "$ref": "#/definitions/WorkflowServiceTerminateWorkflowExecutionBody" } } ], @@ -4913,15 +4987,14 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/fail": { - "post": { - "summary": "See `RecordActivityTaskFailed`. This version allows clients to record failures by\nnamespace/workflow id/activity id instead of task token.", - "operationId": "RespondActivityTaskFailedById4", + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/time-skipping/poll": { + "get": { + "operationId": "PollWorkflowExecutionTimeSkipping2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RespondActivityTaskFailedByIdResponse" + "$ref": "#/definitions/v1PollWorkflowExecutionTimeSkippingResponse" } }, "default": { @@ -4934,32 +5007,28 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity", "in": "path", "required": true, "type": "string" }, { - "name": "workflowId", - "description": "Id of the workflow which scheduled this activity, leave empty to target a standalone activity", + "name": "workflowExecution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "activityId", - "description": "Id of the activity to fail", - "in": "path", - "required": true, + "name": "workflowExecution.runId", + "in": "query", + "required": false, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceRespondActivityTaskFailedByIdBody" - } + "name": "fastForwardId", + "description": "Required. Identifies the fast-forward whose completion the caller wants to wait for.\nMust match the `fast_forward_id` set in the execution's TimeSkippingConfig.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -4967,15 +5036,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/heartbeat": { + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/update-options": { "post": { - "summary": "See `RecordActivityTaskHeartbeat`. This version allows clients to record heartbeats by\nnamespace/workflow id/activity id instead of task token.", - "operationId": "RecordActivityTaskHeartbeatById4", + "summary": "UpdateWorkflowExecutionOptions partially updates the WorkflowExecutionOptions of an existing workflow execution.", + "operationId": "UpdateWorkflowExecutionOptions2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RecordActivityTaskHeartbeatByIdResponse" + "$ref": "#/definitions/v1UpdateWorkflowExecutionOptionsResponse" } }, "default": { @@ -4988,13 +5057,266 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity", + "description": "The namespace name of the target Workflow.", "in": "path", "required": true, "type": "string" }, { - "name": "workflowId", + "name": "workflowExecution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceUpdateWorkflowExecutionOptionsBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/update/{request.input.name}": { + "post": { + "summary": "Invokes the specified Update function on user Workflow code.", + "operationId": "UpdateWorkflowExecution2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1UpdateWorkflowExecutionResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "The namespace name of the target Workflow.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowExecution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "request.input.name", + "description": "The name of the Update handler to invoke on the target Workflow.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceUpdateWorkflowExecutionBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/workflows/{workflowId}": { + "post": { + "summary": "StartWorkflowExecution starts a new workflow execution.", + "description": "It will create the execution with a `WORKFLOW_EXECUTION_STARTED` event in its history and\nalso schedule the first workflow task. Returns `WorkflowExecutionAlreadyStarted`, if an\ninstance already exists with same workflow id.", + "operationId": "StartWorkflowExecution2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1StartWorkflowExecutionResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceStartWorkflowExecutionBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/complete": { + "post": { + "summary": "See `RespondActivityTaskCompleted`. This version allows clients to record completions by\nnamespace/workflow id/activity id instead of task token.", + "operationId": "RespondActivityTaskCompletedById4", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RespondActivityTaskCompletedByIdResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "Namespace of the workflow which scheduled this activity", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowId", + "description": "Id of the workflow which scheduled this activity, leave empty to target a standalone activity", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "activityId", + "description": "Id of the activity to complete", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRespondActivityTaskCompletedByIdBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/fail": { + "post": { + "summary": "See `RecordActivityTaskFailed`. This version allows clients to record failures by\nnamespace/workflow id/activity id instead of task token.", + "operationId": "RespondActivityTaskFailedById4", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RespondActivityTaskFailedByIdResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "Namespace of the workflow which scheduled this activity", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowId", + "description": "Id of the workflow which scheduled this activity, leave empty to target a standalone activity", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "activityId", + "description": "Id of the activity to fail", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRespondActivityTaskFailedByIdBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/heartbeat": { + "post": { + "summary": "See `RecordActivityTaskHeartbeat`. This version allows clients to record heartbeats by\nnamespace/workflow id/activity id instead of task token.", + "operationId": "RecordActivityTaskHeartbeatById4", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RecordActivityTaskHeartbeatByIdResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "Namespace of the workflow which scheduled this activity", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "workflowId", "description": "Id of the workflow which scheduled this activity, leave empty to target a standalone activity", "in": "path", "required": true, @@ -7370,8 +7692,20 @@ "in": "path", "required": true, "type": "string" - } - ], + }, + { + "name": "workflowExecution.workflowId", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" + } + ], "tags": [ "WorkflowService" ] @@ -7465,6 +7799,18 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "workflowExecution.workflowId", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -7525,6 +7871,18 @@ "required": false, "type": "integer", "format": "int32" + }, + { + "name": "workflowExecution.workflowId", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -9998,17 +10356,298 @@ "format": "int32" }, { - "name": "nextPageToken", - "in": "query", - "required": false, - "type": "string", - "format": "byte" + "name": "nextPageToken", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + }, + { + "name": "query", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/workflows/{execution.workflowId}": { + "get": { + "summary": "DescribeWorkflowExecution returns information about the specified workflow execution.", + "operationId": "DescribeWorkflowExecution", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DescribeWorkflowExecutionResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/workflows/{execution.workflowId}/history": { + "get": { + "summary": "GetWorkflowExecutionHistory returns the history of specified workflow execution. Fails with\n`NotFound` if the specified workflow execution is unknown to the service.", + "operationId": "GetWorkflowExecutionHistory", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1GetWorkflowExecutionHistoryResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maximumPageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "nextPageToken", + "description": "If a `GetWorkflowExecutionHistoryResponse` or a `PollWorkflowTaskQueueResponse` had one of\nthese, it should be passed here to fetch the next page.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + }, + { + "name": "waitNewEvent", + "description": "If set to true, the RPC call will not resolve until there is a new event which matches\nthe `history_event_filter_type`, or a timeout is hit.", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "historyEventFilterType", + "description": "Filter returned events such that they match the specified filter type.\nDefault: HISTORY_EVENT_FILTER_TYPE_ALL_EVENT.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED", + "HISTORY_EVENT_FILTER_TYPE_ALL_EVENT", + "HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT" + ], + "default": "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED" + }, + { + "name": "skipArchival", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/workflows/{execution.workflowId}/history-reverse": { + "get": { + "summary": "GetWorkflowExecutionHistoryReverse returns the history of specified workflow execution in reverse\norder (starting from last event). Fails with`NotFound` if the specified workflow execution is\nunknown to the service.", + "operationId": "GetWorkflowExecutionHistoryReverse", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1GetWorkflowExecutionHistoryReverseResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maximumPageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "nextPageToken", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/workflows/{execution.workflowId}/query/{query.queryType}": { + "post": { + "summary": "QueryWorkflow requests a query be executed for a specified workflow execution.", + "operationId": "QueryWorkflow", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1QueryWorkflowResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.workflowId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "query.queryType", + "description": "The workflow-author-defined identifier of the query. Typically a function name.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceQueryWorkflowBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/workflows/{execution.workflowId}/trigger-rule": { + "post": { + "summary": "TriggerWorkflowRule allows to:\n * trigger existing rule for a specific workflow execution;\n * trigger rule for a specific workflow execution without creating a rule;\nThis is useful for one-off operations.", + "operationId": "TriggerWorkflowRule", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1TriggerWorkflowRuleResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.workflowId", + "in": "path", + "required": true, + "type": "string" }, { - "name": "query", - "in": "query", - "required": false, - "type": "string" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceTriggerWorkflowRuleBody" + } } ], "tags": [ @@ -10016,15 +10655,16 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}": { - "get": { - "summary": "DescribeWorkflowExecution returns information about the specified workflow execution.", - "operationId": "DescribeWorkflowExecution", + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/cancel": { + "post": { + "summary": "RequestCancelWorkflowExecution is called by workers when they want to request cancellation of\na workflow execution.", + "description": "This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the\nworkflow history and a new workflow task created for the workflow. It returns success if the requested\nworkflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist.", + "operationId": "RequestCancelWorkflowExecution", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1DescribeWorkflowExecutionResponse" + "$ref": "#/definitions/v1RequestCancelWorkflowExecutionResponse" } }, "default": { @@ -10042,16 +10682,18 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "workflowExecution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "execution.runId", - "in": "query", - "required": false, - "type": "string" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRequestCancelWorkflowExecutionBody" + } } ], "tags": [ @@ -10059,15 +10701,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}/history": { + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}": { "get": { - "summary": "GetWorkflowExecutionHistory returns the history of specified workflow execution. Fails with\n`NotFound` if the specified workflow execution is unknown to the service.", - "operationId": "GetWorkflowExecutionHistory", + "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", + "operationId": "DescribeChannel3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1GetWorkflowExecutionHistoryResponse" + "$ref": "#/definitions/v1DescribeChannelResponse" } }, "default": { @@ -10085,57 +10727,22 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "workflowExecution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "execution.runId", - "in": "query", - "required": false, + "name": "channel", + "in": "path", + "required": true, "type": "string" }, { - "name": "maximumPageSize", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "nextPageToken", - "description": "If a `GetWorkflowExecutionHistoryResponse` or a `PollWorkflowTaskQueueResponse` had one of\nthese, it should be passed here to fetch the next page.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "waitNewEvent", - "description": "If set to true, the RPC call will not resolve until there is a new event which matches\nthe `history_event_filter_type`, or a timeout is hit.", - "in": "query", - "required": false, - "type": "boolean" - }, - { - "name": "historyEventFilterType", - "description": "Filter returned events such that they match the specified filter type.\nDefault: HISTORY_EVENT_FILTER_TYPE_ALL_EVENT.", - "in": "query", - "required": false, - "type": "string", - "enum": [ - "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED", - "HISTORY_EVENT_FILTER_TYPE_ALL_EVENT", - "HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT" - ], - "default": "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED" - }, - { - "name": "skipArchival", + "name": "workflowExecution.runId", "in": "query", "required": false, - "type": "boolean" + "type": "string" } ], "tags": [ @@ -10143,15 +10750,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}/history-reverse": { - "get": { - "summary": "GetWorkflowExecutionHistoryReverse returns the history of specified workflow execution in reverse\norder (starting from last event). Fails with`NotFound` if the specified workflow execution is\nunknown to the service.", - "operationId": "GetWorkflowExecutionHistoryReverse", + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/listeners": { + "post": { + "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", + "operationId": "RegisterChannelListener3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1GetWorkflowExecutionHistoryReverseResponse" + "$ref": "#/definitions/v1RegisterChannelListenerResponse" } }, "default": { @@ -10169,30 +10776,24 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "workflowExecution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "execution.runId", - "in": "query", - "required": false, + "name": "channel", + "in": "path", + "required": true, "type": "string" }, { - "name": "maximumPageSize", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "nextPageToken", - "in": "query", - "required": false, - "type": "string", - "format": "byte" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" + } } ], "tags": [ @@ -10200,15 +10801,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}/query/{query.queryType}": { - "post": { - "summary": "QueryWorkflow requests a query be executed for a specified workflow execution.", - "operationId": "QueryWorkflow", + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/listeners/{listenerId}": { + "delete": { + "summary": "UnregisterChannelListener removes a listener from a channel.", + "operationId": "UnregisterChannelListener3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1QueryWorkflowResponse" + "$ref": "#/definitions/v1UnregisterChannelListenerResponse" } }, "default": { @@ -10226,25 +10827,35 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "workflowExecution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "query.queryType", - "description": "The workflow-author-defined identifier of the query. Typically a function name.", + "name": "channel", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "listenerId", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceQueryWorkflowBody" - } + "type": "string" + }, + { + "name": "identity", + "description": "The identity of the caller, for metrics and logs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -10252,15 +10863,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}/trigger-rule": { - "post": { - "summary": "TriggerWorkflowRule allows to:\n * trigger existing rule for a specific workflow execution;\n * trigger rule for a specific workflow execution without creating a rule;\nThis is useful for one-off operations.", - "operationId": "TriggerWorkflowRule", + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/notifications": { + "get": { + "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", + "operationId": "PollChannel3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1TriggerWorkflowRuleResponse" + "$ref": "#/definitions/v1PollChannelResponse" } }, "default": { @@ -10278,18 +10889,45 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "workflowExecution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "channel", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceTriggerWorkflowRuleBody" - } + "type": "string" + }, + { + "name": "afterCounter", + "description": "Only notifications with a counter above this one are returned.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "wait", + "description": "How long to wait for a notification when none is retained above\n`after_counter`.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maxNotifications", + "description": "At most this many notifications are returned. Zero means the server's\ndefault.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "workflowExecution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -10297,16 +10935,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/cancel": { + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{notification.channel}/notify": { "post": { - "summary": "RequestCancelWorkflowExecution is called by workers when they want to request cancellation of\na workflow execution.", - "description": "This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the\nworkflow history and a new workflow task created for the workflow. It returns success if the requested\nworkflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist.", - "operationId": "RequestCancelWorkflowExecution", + "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", + "operationId": "NotifyChannel3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RequestCancelWorkflowExecutionResponse" + "$ref": "#/definitions/v1NotifyChannelResponse" } }, "default": { @@ -10329,12 +10966,19 @@ "required": true, "type": "string" }, + { + "name": "notification.channel", + "description": "The channel the writer notified. Listeners register on the same name.", + "in": "path", + "required": true, + "type": "string" + }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceRequestCancelWorkflowExecutionBody" + "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" } } ], @@ -12207,6 +12851,10 @@ "$ref": "#/definitions/v1Payload" }, "description": "Details for the listener, such as which topic moved. Bounded in size and\ncarried as payloads, so a codec applies as to any payload." + }, + "linkedTo": { + "$ref": "#/definitions/v1WorkflowExecution", + "description": "Set for a channel linked to a workflow: the owner and the run that\nreceived the notification. Empty for an independent channel. A listener\nthat holds both kinds routes the notification by it." } }, "description": "A notification tells the listeners of a channel that a source they consume\nhas moved. It is not data: the listener reads the source itself. A channel\nis named by the writer and its listeners; for a stream, the provider formats\nthe stream's identity into the name. Writers never learn who listens. The\nserver folds notifications per listener while one is pending and no task has\nbeen scheduled for it, keeping the one with the highest counter." @@ -12218,6 +12866,15 @@ "requestId": { "type": "string", "description": "Used to de-dupe a retried notification." + }, + "workflowExecution": { + "type": "object", + "properties": { + "runId": { + "type": "string" + } + }, + "description": "When set, the call addresses the channel linked to this workflow; `run_id`\nis optional and resolves to the chain's current run, as a Signal does.\nWhen unset, the call addresses the independent channel of that name." } } }, @@ -12423,6 +13080,15 @@ "identity": { "type": "string", "description": "The identity of the caller, for metrics and logs." + }, + "workflowExecution": { + "type": "object", + "properties": { + "runId": { + "type": "string" + } + }, + "description": "When set, the call addresses the channel linked to this workflow; `run_id`\nis optional and resolves to the chain's current run, as a Signal does.\nWhen unset, the call addresses the independent channel of that name." } } }, @@ -15370,6 +16036,16 @@ } } }, + "v1ChannelKind": { + "type": "string", + "enum": [ + "CHANNEL_KIND_UNSPECIFIED", + "CHANNEL_KIND_INDEPENDENT", + "CHANNEL_KIND_LINKED" + ], + "default": "CHANNEL_KIND_UNSPECIFIED", + "description": "Where a channel lives, which decides how a call addresses it.\n\n - CHANNEL_KIND_INDEPENDENT: Its own execution, keyed by namespace and channel name. Any number of\nworkflows and callbacks listen to it.\n - CHANNEL_KIND_LINKED: Kept in one workflow's state, keyed by namespace, workflow id and\nchannel name. The owning workflow is its listener by construction." + }, "v1ChannelListener": { "type": "object", "properties": { @@ -16237,6 +16913,13 @@ "type": "integer", "format": "int32", "description": "How many notifications the channel retains for pollers." + }, + "kind": { + "$ref": "#/definitions/v1ChannelKind" + }, + "linkedTo": { + "$ref": "#/definitions/v1WorkflowExecution", + "description": "The owner of a linked channel and the run that holds it. Empty for an\nindependent channel." } } }, @@ -18690,6 +19373,10 @@ "$ref": "#/definitions/v1Payload" }, "description": "Details for the listener, such as which topic moved. Bounded in size and\ncarried as payloads, so a codec applies as to any payload." + }, + "linkedTo": { + "$ref": "#/definitions/v1WorkflowExecution", + "description": "Set for a channel linked to a workflow: the owner and the run that\nreceived the notification. Empty for an independent channel. A listener\nthat holds both kinds routes the notification by it." } }, "description": "A notification tells the listeners of a channel that a source they consume\nhas moved. It is not data: the listener reads the source itself. A channel\nis named by the writer and its listeners; for a stream, the provider formats\nthe stream's identity into the name. Writers never learn who listens. The\nserver folds notifications per listener while one is pending and no task has\nbeen scheduled for it, keeping the one with the highest counter." diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 1fd270c6f..815f9b83f 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -1336,6 +1336,14 @@ paths: required: true schema: type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK @@ -1419,6 +1427,14 @@ paths: description: The identity of the caller, for metrics and logs. schema: type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK @@ -1477,6 +1493,14 @@ paths: schema: type: integer format: int32 + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK @@ -4643,17 +4667,14 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/reset: - post: + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}: + get: tags: - WorkflowService description: |- - ResetWorkflowExecution will reset an existing workflow execution to a specified - `WORKFLOW_TASK_COMPLETED` event (exclusive). It will immediately terminate the current - execution instance. "Exclusive" means the identified completed event itself is not replayed - in the reset history; the preceding `WORKFLOW_TASK_STARTED` event remains and will be marked as failed - immediately, and a new workflow task will be scheduled to retry it. - operationId: ResetWorkflowExecution + DescribeChannel returns the listeners of a channel and its latest + notification. + operationId: DescribeChannel parameters: - name: namespace in: path @@ -4665,35 +4686,41 @@ paths: required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ResetWorkflowExecutionRequest' - required: true + - name: channel + in: path + required: true + schema: + type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/ResetWorkflowExecutionResponse' + $ref: '#/components/schemas/DescribeChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/signal/{signalName}: + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners: post: tags: - WorkflowService description: |- - SignalWorkflowExecution is used to send a signal to a running workflow execution. - - This results in a `WORKFLOW_EXECUTION_SIGNALED` event recorded in the history and a workflow - task being created for the execution. - operationId: SignalWorkflowExecution + RegisterChannelListener registers a callback as a listener of a channel. A + Workflow registers itself with the `SubscribeNotificationChannel` command + instead. + operationId: RegisterChannelListener parameters: - name: namespace in: path @@ -4705,9 +4732,8 @@ paths: required: true schema: type: string - - name: signalName + - name: channel in: path - description: The workflow author-defined name of the signal to send to the workflow required: true schema: type: string @@ -4715,7 +4741,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SignalWorkflowExecutionRequest' + $ref: '#/components/schemas/RegisterChannelListenerRequest' required: true responses: "200": @@ -4723,22 +4749,23 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SignalWorkflowExecutionResponse' + $ref: '#/components/schemas/RegisterChannelListenerResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/terminate: - post: + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners/{listenerId}: + delete: tags: - WorkflowService description: |- - TerminateWorkflowExecution terminates an existing workflow execution by recording a - `WORKFLOW_EXECUTION_TERMINATED` event in the history and immediately terminating the - execution instance. - operationId: TerminateWorkflowExecution + UnregisterChannelListener removes a listener from a channel. + + (-- api-linter: core::0136::http-method=disabled + aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + operationId: UnregisterChannelListener parameters: - name: namespace in: path @@ -4750,30 +4777,51 @@ paths: required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TerminateWorkflowExecutionRequest' - required: true + - name: channel + in: path + required: true + schema: + type: string + - name: listenerId + in: path + required: true + schema: + type: string + - name: identity + in: query + description: The identity of the caller, for metrics and logs. + schema: + type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/TerminateWorkflowExecutionResponse' + $ref: '#/components/schemas/UnregisterChannelListenerResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/poll: + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/notifications: get: tags: - WorkflowService - operationId: PollWorkflowExecutionTimeSkipping + description: |- + PollChannel is a long poll for clients. It returns the retained + notifications of a channel with a counter above `after_counter`, waiting + up to `wait` for one when none is retained yet. + operationId: PollChannel parameters: - name: namespace in: path @@ -4785,19 +4833,42 @@ paths: required: true schema: type: string - - name: workflowExecution.workflowId + - name: channel + in: path + required: true + schema: + type: string + - name: afterCounter in: query + description: |- + Only notifications with a counter above this one are returned. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "after" names the exclusive lower bound. --) schema: type: string - - name: workflowExecution.runId + - name: wait in: query + description: |- + How long to wait for a notification when none is retained above + `after_counter`. schema: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ type: string - - name: fastForwardId + description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s + - name: maxNotifications in: query description: |- - Required. Identifies the fast-forward whose completion the caller wants to wait for. - Must match the `fast_forward_id` set in the execution's TimeSkippingConfig. + At most this many notifications are returned. Zero means the server's + default. + schema: + type: integer + format: int32 + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query schema: type: string responses: @@ -4806,23 +4877,27 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PollWorkflowExecutionTimeSkippingResponse' + $ref: '#/components/schemas/PollChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/update-options: + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{notification.channel}/notify: post: tags: - WorkflowService - description: UpdateWorkflowExecutionOptions partially updates the WorkflowExecutionOptions of an existing workflow execution. - operationId: UpdateWorkflowExecutionOptions + description: |- + NotifyChannel tells every listener of a channel that a source they consume + has moved. The writer names no addressee and never learns who listens. The + server wakes each listener: a Workflow with a Workflow Task, a callback by + invoking it. Nothing goes to History except the notifications a woken + Workflow Task carries on its scheduled event. + operationId: NotifyChannel parameters: - name: namespace in: path - description: The namespace name of the target Workflow. required: true schema: type: string @@ -4831,11 +4906,16 @@ paths: required: true schema: type: string + - name: notification.channel + in: path + required: true + schema: + type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/UpdateWorkflowExecutionOptionsRequest' + $ref: '#/components/schemas/NotifyChannelRequest' required: true responses: "200": @@ -4843,23 +4923,27 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateWorkflowExecutionOptionsResponse' + $ref: '#/components/schemas/NotifyChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/update/{request.input.name}: + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/reset: post: tags: - WorkflowService - description: Invokes the specified Update function on user Workflow code. - operationId: UpdateWorkflowExecution + description: |- + ResetWorkflowExecution will reset an existing workflow execution to a specified + `WORKFLOW_TASK_COMPLETED` event (exclusive). It will immediately terminate the current + execution instance. "Exclusive" means the identified completed event itself is not replayed + in the reset history; the preceding `WORKFLOW_TASK_STARTED` event remains and will be marked as failed + immediately, and a new workflow task will be scheduled to retry it. + operationId: ResetWorkflowExecution parameters: - name: namespace in: path - description: The namespace name of the target Workflow. required: true schema: type: string @@ -4868,16 +4952,11 @@ paths: required: true schema: type: string - - name: request.input.name - in: path - required: true - schema: - type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/UpdateWorkflowExecutionRequest' + $ref: '#/components/schemas/ResetWorkflowExecutionRequest' required: true responses: "200": @@ -4885,64 +4964,272 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateWorkflowExecutionResponse' + $ref: '#/components/schemas/ResetWorkflowExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/nexus/endpoints: - get: + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/signal/{signalName}: + post: tags: - - OperatorService + - WorkflowService description: |- - List all Nexus endpoints for the cluster, sorted by ID in ascending order. Set page_token in the request to the - next_page_token field of the previous response to get the next page of results. An empty next_page_token - indicates that there are no more results. During pagination, a newly added service with an ID lexicographically - earlier than the previous page's last endpoint's ID may be missed. - operationId: ListNexusEndpoints + SignalWorkflowExecution is used to send a signal to a running workflow execution. + + This results in a `WORKFLOW_EXECUTION_SIGNALED` event recorded in the history and a workflow + task being created for the execution. + operationId: SignalWorkflowExecution parameters: - - name: pageSize - in: query + - name: namespace + in: path + required: true schema: - type: integer - format: int32 - - name: nextPageToken - in: query - description: |- - To get the next page, pass in `ListNexusEndpointsResponse.next_page_token` from the previous page's - response, the token will be empty if there's no other page. - Note: the last page may be empty if the total number of endpoints registered is a multiple of the page size. + type: string + - name: workflow_execution.workflow_id + in: path + required: true schema: type: string - format: bytes - - name: name - in: query - description: |- - Name of the incoming endpoint to filter on - optional. Specifying this will result in zero or one results. - (-- api-linter: core::203::field-behavior-required=disabled - aip.dev/not-precedent: Not following linter rules. --) + - name: signalName + in: path + description: The workflow author-defined name of the signal to send to the workflow + required: true schema: type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SignalWorkflowExecutionRequest' + required: true responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/ListNexusEndpointsResponse' + $ref: '#/components/schemas/SignalWorkflowExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/terminate: post: tags: - - OperatorService + - WorkflowService description: |- - Create a Nexus endpoint. This will fail if an endpoint with the same name is already registered with a status of + TerminateWorkflowExecution terminates an existing workflow execution by recording a + `WORKFLOW_EXECUTION_TERMINATED` event in the history and immediately terminating the + execution instance. + operationId: TerminateWorkflowExecution + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TerminateWorkflowExecutionRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TerminateWorkflowExecutionResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/poll: + get: + tags: + - WorkflowService + operationId: PollWorkflowExecutionTimeSkipping + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string + - name: fastForwardId + in: query + description: |- + Required. Identifies the fast-forward whose completion the caller wants to wait for. + Must match the `fast_forward_id` set in the execution's TimeSkippingConfig. + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PollWorkflowExecutionTimeSkippingResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/update-options: + post: + tags: + - WorkflowService + description: UpdateWorkflowExecutionOptions partially updates the WorkflowExecutionOptions of an existing workflow execution. + operationId: UpdateWorkflowExecutionOptions + parameters: + - name: namespace + in: path + description: The namespace name of the target Workflow. + required: true + schema: + type: string + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkflowExecutionOptionsRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkflowExecutionOptionsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/update/{request.input.name}: + post: + tags: + - WorkflowService + description: Invokes the specified Update function on user Workflow code. + operationId: UpdateWorkflowExecution + parameters: + - name: namespace + in: path + description: The namespace name of the target Workflow. + required: true + schema: + type: string + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + - name: request.input.name + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkflowExecutionRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkflowExecutionResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/nexus/endpoints: + get: + tags: + - OperatorService + description: |- + List all Nexus endpoints for the cluster, sorted by ID in ascending order. Set page_token in the request to the + next_page_token field of the previous response to get the next page of results. An empty next_page_token + indicates that there are no more results. During pagination, a newly added service with an ID lexicographically + earlier than the previous page's last endpoint's ID may be missed. + operationId: ListNexusEndpoints + parameters: + - name: pageSize + in: query + schema: + type: integer + format: int32 + - name: nextPageToken + in: query + description: |- + To get the next page, pass in `ListNexusEndpointsResponse.next_page_token` from the previous page's + response, the token will be empty if there's no other page. + Note: the last page may be empty if the total number of endpoints registered is a multiple of the page size. + schema: + type: string + format: bytes + - name: name + in: query + description: |- + Name of the incoming endpoint to filter on - optional. Specifying this will result in zero or one results. + (-- api-linter: core::203::field-behavior-required=disabled + aip.dev/not-precedent: Not following linter rules. --) + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListNexusEndpointsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + post: + tags: + - OperatorService + description: |- + Create a Nexus endpoint. This will fail if an endpoint with the same name is already registered with a status of ALREADY_EXISTS. Returns the created endpoint with its initial version. You may use this version for subsequent updates. operationId: CreateNexusEndpoint @@ -6631,6 +6918,14 @@ paths: required: true schema: type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK @@ -6714,6 +7009,14 @@ paths: description: The identity of the caller, for metrics and logs. schema: type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK @@ -6772,6 +7075,14 @@ paths: schema: type: integer format: int32 + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK @@ -9570,7 +9881,252 @@ paths: type: string - name: activityId in: path - description: Id of the activity to confirm is cancelled + description: Id of the activity to confirm is cancelled + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RespondActivityTaskCanceledByIdRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RespondActivityTaskCanceledByIdResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/unpause: + post: + tags: + - WorkflowService + description: |- + UnpauseActivityExecution unpauses the execution of an activity specified by its ID. + This API can be used to target a workflow activity or a standalone activity. + + If activity is not paused, this call will have no effect. + If the activity was paused while waiting for retry, it will be scheduled immediately (* see 'jitter' flag). + Once the activity is unpaused, all timeout timers will be regenerated. + + Returns a `NotFound` error if there is no pending activity with the provided ID + operationId: UnpauseActivityExecution + parameters: + - name: namespace + in: path + description: Namespace of the workflow which scheduled this activity. + required: true + schema: + type: string + - name: workflowId + in: path + description: |- + If provided, targets a workflow activity for the given workflow ID. + If empty, targets a standalone activity. + required: true + schema: + type: string + - name: activityId + in: path + description: The ID of the activity to target. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UnpauseActivityExecutionRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UnpauseActivityExecutionResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/update-options: + post: + tags: + - WorkflowService + description: |- + UpdateActivityExecutionOptions is called by the client to update the options of an activity by its ID. + This API can be used to target a workflow activity or a standalone activity. + operationId: UpdateActivityExecutionOptions + parameters: + - name: namespace + in: path + description: Namespace of the workflow which scheduled this activity + required: true + schema: + type: string + - name: workflowId + in: path + description: |- + If provided, targets a workflow activity for the given workflow ID. + If empty, targets a standalone activity. + required: true + schema: + type: string + - name: activityId + in: path + description: The ID of the activity to target. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateActivityExecutionOptionsRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateActivityExecutionOptionsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflows/{workflowId}/pause: + post: + tags: + - WorkflowService + description: |- + Note: This is an experimental API and the behavior may change in a future release. + PauseWorkflowExecution pauses the workflow execution specified in the request. Pausing a workflow execution results in + - The workflow execution status changes to `PAUSED` and a new WORKFLOW_EXECUTION_PAUSED event is added to the history + - No new workflow tasks or activity tasks are dispatched. + - Any workflow task currently executing on the worker will be allowed to complete. + - Any activity task currently executing will be paused. + - All server-side events will continue to be processed by the server. + - Queries & Updates on a paused workflow will be rejected. + operationId: PauseWorkflowExecution + parameters: + - name: namespace + in: path + description: Namespace of the workflow to pause. + required: true + schema: + type: string + - name: workflowId + in: path + description: ID of the workflow execution to be paused. Required. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PauseWorkflowExecutionRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PauseWorkflowExecutionResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflows/{workflowId}/signal-with-start/{signalName}: + post: + tags: + - WorkflowService + description: |- + SignalWithStartWorkflowExecution is used to ensure a signal is sent to a workflow, even if + it isn't yet started. + + If the workflow is running, a `WORKFLOW_EXECUTION_SIGNALED` event is recorded in the history + and a workflow task is generated. + + If the workflow is not running or not found, then the workflow is created with + `WORKFLOW_EXECUTION_STARTED` and `WORKFLOW_EXECUTION_SIGNALED` events in its history, and a + workflow task is generated. + + (-- api-linter: core::0136::prepositions=disabled + aip.dev/not-precedent: "With" is used to indicate combined operation. --) + operationId: SignalWithStartWorkflowExecution + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: workflowId + in: path + required: true + schema: + type: string + - name: signalName + in: path + description: The workflow author-defined name of the signal to send to the workflow + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SignalWithStartWorkflowExecutionRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SignalWithStartWorkflowExecutionResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflows/{workflowId}/unpause: + post: + tags: + - WorkflowService + description: |- + Note: This is an experimental API and the behavior may change in a future release. + UnpauseWorkflowExecution unpauses a previously paused workflow execution specified in the request. + Unpausing a workflow execution results in + - The workflow execution status changes to `RUNNING` and a new WORKFLOW_EXECUTION_UNPAUSED event is added to the history + - Workflow tasks and activity tasks are resumed. + operationId: UnpauseWorkflowExecution + parameters: + - name: namespace + in: path + description: Namespace of the workflow to unpause. + required: true + schema: + type: string + - name: workflowId + in: path + description: ID of the workflow execution to be paused. Required. required: true schema: type: string @@ -9578,7 +10134,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RespondActivityTaskCanceledByIdRequest' + $ref: '#/components/schemas/UnpauseWorkflowExecutionRequest' required: true responses: "200": @@ -9586,45 +10142,33 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RespondActivityTaskCanceledByIdResponse' + $ref: '#/components/schemas/UnpauseWorkflowExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/unpause: + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/cancel: post: tags: - WorkflowService description: |- - UnpauseActivityExecution unpauses the execution of an activity specified by its ID. - This API can be used to target a workflow activity or a standalone activity. - - If activity is not paused, this call will have no effect. - If the activity was paused while waiting for retry, it will be scheduled immediately (* see 'jitter' flag). - Once the activity is unpaused, all timeout timers will be regenerated. + RequestCancelWorkflowExecution is called by workers when they want to request cancellation of + a workflow execution. - Returns a `NotFound` error if there is no pending activity with the provided ID - operationId: UnpauseActivityExecution + This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the + workflow history and a new workflow task created for the workflow. It returns success if the requested + workflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist. + operationId: RequestCancelWorkflowExecution parameters: - name: namespace in: path - description: Namespace of the workflow which scheduled this activity. - required: true - schema: - type: string - - name: workflowId - in: path - description: |- - If provided, targets a workflow activity for the given workflow ID. - If empty, targets a standalone activity. required: true schema: type: string - - name: activityId + - name: workflow_execution.workflow_id in: path - description: The ID of the activity to target. required: true schema: type: string @@ -9632,7 +10176,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnpauseActivityExecutionRequest' + $ref: '#/components/schemas/RequestCancelWorkflowExecutionRequest' required: true responses: "200": @@ -9640,85 +10184,80 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnpauseActivityExecutionResponse' + $ref: '#/components/schemas/RequestCancelWorkflowExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/update-options: - post: + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}: + get: tags: - WorkflowService description: |- - UpdateActivityExecutionOptions is called by the client to update the options of an activity by its ID. - This API can be used to target a workflow activity or a standalone activity. - operationId: UpdateActivityExecutionOptions + DescribeChannel returns the listeners of a channel and its latest + notification. + operationId: DescribeChannel parameters: - name: namespace in: path - description: Namespace of the workflow which scheduled this activity required: true schema: type: string - - name: workflowId + - name: workflow_execution.workflow_id in: path - description: |- - If provided, targets a workflow activity for the given workflow ID. - If empty, targets a standalone activity. required: true schema: type: string - - name: activityId + - name: channel in: path - description: The ID of the activity to target. required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateActivityExecutionOptionsRequest' - required: true + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/UpdateActivityExecutionOptionsResponse' + $ref: '#/components/schemas/DescribeChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/pause: + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners: post: tags: - WorkflowService description: |- - Note: This is an experimental API and the behavior may change in a future release. - PauseWorkflowExecution pauses the workflow execution specified in the request. Pausing a workflow execution results in - - The workflow execution status changes to `PAUSED` and a new WORKFLOW_EXECUTION_PAUSED event is added to the history - - No new workflow tasks or activity tasks are dispatched. - - Any workflow task currently executing on the worker will be allowed to complete. - - Any activity task currently executing will be paused. - - All server-side events will continue to be processed by the server. - - Queries & Updates on a paused workflow will be rejected. - operationId: PauseWorkflowExecution + RegisterChannelListener registers a callback as a listener of a channel. A + Workflow registers itself with the `SubscribeNotificationChannel` command + instead. + operationId: RegisterChannelListener parameters: - name: namespace in: path - description: Namespace of the workflow to pause. required: true schema: type: string - - name: workflowId + - name: workflow_execution.workflow_id + in: path + required: true + schema: + type: string + - name: channel in: path - description: ID of the workflow execution to be paused. Required. required: true schema: type: string @@ -9726,7 +10265,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PauseWorkflowExecutionRequest' + $ref: '#/components/schemas/RegisterChannelListenerRequest' required: true responses: "200": @@ -9734,122 +10273,152 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PauseWorkflowExecutionResponse' + $ref: '#/components/schemas/RegisterChannelListenerResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/signal-with-start/{signalName}: - post: + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners/{listenerId}: + delete: tags: - WorkflowService description: |- - SignalWithStartWorkflowExecution is used to ensure a signal is sent to a workflow, even if - it isn't yet started. - - If the workflow is running, a `WORKFLOW_EXECUTION_SIGNALED` event is recorded in the history - and a workflow task is generated. - - If the workflow is not running or not found, then the workflow is created with - `WORKFLOW_EXECUTION_STARTED` and `WORKFLOW_EXECUTION_SIGNALED` events in its history, and a - workflow task is generated. + UnregisterChannelListener removes a listener from a channel. - (-- api-linter: core::0136::prepositions=disabled - aip.dev/not-precedent: "With" is used to indicate combined operation. --) - operationId: SignalWithStartWorkflowExecution + (-- api-linter: core::0136::http-method=disabled + aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + operationId: UnregisterChannelListener parameters: - name: namespace in: path required: true schema: type: string - - name: workflowId + - name: workflow_execution.workflow_id in: path required: true schema: type: string - - name: signalName + - name: channel in: path - description: The workflow author-defined name of the signal to send to the workflow required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SignalWithStartWorkflowExecutionRequest' - required: true + - name: listenerId + in: path + required: true + schema: + type: string + - name: identity + in: query + description: The identity of the caller, for metrics and logs. + schema: + type: string + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/SignalWithStartWorkflowExecutionResponse' + $ref: '#/components/schemas/UnregisterChannelListenerResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/unpause: - post: + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/notifications: + get: tags: - WorkflowService description: |- - Note: This is an experimental API and the behavior may change in a future release. - UnpauseWorkflowExecution unpauses a previously paused workflow execution specified in the request. - Unpausing a workflow execution results in - - The workflow execution status changes to `RUNNING` and a new WORKFLOW_EXECUTION_UNPAUSED event is added to the history - - Workflow tasks and activity tasks are resumed. - operationId: UnpauseWorkflowExecution + PollChannel is a long poll for clients. It returns the retained + notifications of a channel with a counter above `after_counter`, waiting + up to `wait` for one when none is retained yet. + operationId: PollChannel parameters: - name: namespace in: path - description: Namespace of the workflow to unpause. required: true schema: type: string - - name: workflowId + - name: workflow_execution.workflow_id in: path - description: ID of the workflow execution to be paused. Required. required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UnpauseWorkflowExecutionRequest' - required: true + - name: channel + in: path + required: true + schema: + type: string + - name: afterCounter + in: query + description: |- + Only notifications with a counter above this one are returned. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "after" names the exclusive lower bound. --) + schema: + type: string + - name: wait + in: query + description: |- + How long to wait for a notification when none is retained above + `after_counter`. + schema: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s + - name: maxNotifications + in: query + description: |- + At most this many notifications are returned. Zero means the server's + default. + schema: + type: integer + format: int32 + - name: workflowExecution.workflowId + in: query + schema: + type: string + - name: workflowExecution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/UnpauseWorkflowExecutionResponse' + $ref: '#/components/schemas/PollChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/cancel: + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{notification.channel}/notify: post: tags: - WorkflowService description: |- - RequestCancelWorkflowExecution is called by workers when they want to request cancellation of - a workflow execution. - - This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the - workflow history and a new workflow task created for the workflow. It returns success if the requested - workflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist. - operationId: RequestCancelWorkflowExecution + NotifyChannel tells every listener of a channel that a source they consume + has moved. The writer names no addressee and never learns who listens. The + server wakes each listener: a Workflow with a Workflow Task, a callback by + invoking it. Nothing goes to History except the notifications a woken + Workflow Task carries on its scheduled event. + operationId: NotifyChannel parameters: - name: namespace in: path @@ -9861,11 +10430,16 @@ paths: required: true schema: type: string + - name: notification.channel + in: path + required: true + schema: + type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/RequestCancelWorkflowExecutionRequest' + $ref: '#/components/schemas/NotifyChannelRequest' required: true responses: "200": @@ -9873,7 +10447,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RequestCancelWorkflowExecutionResponse' + $ref: '#/components/schemas/NotifyChannelResponse' default: description: Default error response content: @@ -12326,6 +12900,21 @@ components: type: integer description: How many notifications the channel retains for pollers. format: int32 + kind: + enum: + - CHANNEL_KIND_UNSPECIFIED + - CHANNEL_KIND_INDEPENDENT + - CHANNEL_KIND_LINKED + type: string + format: enum + linkedTo: + allOf: + - $ref: '#/components/schemas/WorkflowExecution' + description: |- + The owner of a linked channel and the run that holds it. Empty for an + independent channel. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "to" names the owner the channel is linked to. --) DescribeDeploymentResponse: type: object properties: @@ -14733,6 +15322,15 @@ components: description: |- Details for the listener, such as which topic moved. Bounded in size and carried as payloads, so a codec applies as to any payload. + linkedTo: + allOf: + - $ref: '#/components/schemas/WorkflowExecution' + description: |- + Set for a channel linked to a workflow: the owner and the run that + received the notification. Empty for an independent channel. A listener + that holds both kinds routes the notification by it. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "to" names the owner the channel is linked to. --) description: |- A notification tells the listeners of a channel that a source they consume has moved. It is not data: the listener reads the source itself. A channel @@ -14753,6 +15351,13 @@ components: requestId: type: string description: Used to de-dupe a retried notification. + workflowExecution: + allOf: + - $ref: '#/components/schemas/WorkflowExecution' + description: |- + When set, the call addresses the channel linked to this workflow; `run_id` + is optional and resolves to the chain's current run, as a Signal does. + When unset, the call addresses the independent channel of that name. NotifyChannelResponse: type: object properties: @@ -15802,6 +16407,13 @@ components: identity: type: string description: The identity of the caller, for metrics and logs. + workflowExecution: + allOf: + - $ref: '#/components/schemas/WorkflowExecution' + description: |- + When set, the call addresses the channel linked to this workflow; `run_id` + is optional and resolves to the chain's current run, as a Signal does. + When unset, the call addresses the independent channel of that name. RegisterChannelListenerResponse: type: object properties: diff --git a/temporal/api/notification/v1/message.proto b/temporal/api/notification/v1/message.proto index 63e1b7716..e1413efe1 100644 --- a/temporal/api/notification/v1/message.proto +++ b/temporal/api/notification/v1/message.proto @@ -32,6 +32,12 @@ message Notification { // Details for the listener, such as which topic moved. Bounded in size and // carried as payloads, so a codec applies as to any payload. map metadata = 4; + // Set for a channel linked to a workflow: the owner and the run that + // received the notification. Empty for an independent channel. A listener + // that holds both kinds routes the notification by it. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "to" names the owner the channel is linked to. --) + temporal.api.common.v1.WorkflowExecution linked_to = 5; } // A listener of a channel: a Workflow Execution woken with a Workflow Task, or @@ -53,3 +59,14 @@ message WorkflowListener { // chain's current run when it delivers. string run_id = 2; } + +// Where a channel lives, which decides how a call addresses it. +enum ChannelKind { + CHANNEL_KIND_UNSPECIFIED = 0; + // Its own execution, keyed by namespace and channel name. Any number of + // workflows and callbacks listen to it. + CHANNEL_KIND_INDEPENDENT = 1; + // Kept in one workflow's state, keyed by namespace, workflow id and + // channel name. The owning workflow is its listener by construction. + CHANNEL_KIND_LINKED = 2; +} diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index f7bbe7d33..cc046e9bb 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -896,6 +896,10 @@ message NotifyChannelRequest { string identity = 3; // Used to de-dupe a retried notification. string request_id = 4; + // When set, the call addresses the channel linked to this workflow; `run_id` + // is optional and resolves to the chain's current run, as a Signal does. + // When unset, the call addresses the independent channel of that name. + temporal.api.common.v1.WorkflowExecution workflow_execution = 5; } message NotifyChannelResponse { @@ -913,6 +917,10 @@ message RegisterChannelListenerRequest { string request_id = 4; // The identity of the caller, for metrics and logs. string identity = 5; + // When set, the call addresses the channel linked to this workflow; `run_id` + // is optional and resolves to the chain's current run, as a Signal does. + // When unset, the call addresses the independent channel of that name. + temporal.api.common.v1.WorkflowExecution workflow_execution = 6; } message RegisterChannelListenerResponse { @@ -925,6 +933,10 @@ message UnregisterChannelListenerRequest { string listener_id = 3; // The identity of the caller, for metrics and logs. string identity = 4; + // When set, the call addresses the channel linked to this workflow; `run_id` + // is optional and resolves to the chain's current run, as a Signal does. + // When unset, the call addresses the independent channel of that name. + temporal.api.common.v1.WorkflowExecution workflow_execution = 5; } message UnregisterChannelListenerResponse { @@ -943,6 +955,10 @@ message PollChannelRequest { // At most this many notifications are returned. Zero means the server's // default. int32 max_notifications = 5; + // When set, the call addresses the channel linked to this workflow; `run_id` + // is optional and resolves to the chain's current run, as a Signal does. + // When unset, the call addresses the independent channel of that name. + temporal.api.common.v1.WorkflowExecution workflow_execution = 6; } message PollChannelResponse { @@ -952,6 +968,10 @@ message PollChannelResponse { message DescribeChannelRequest { string namespace = 1; string channel = 2; + // When set, the call addresses the channel linked to this workflow; `run_id` + // is optional and resolves to the chain's current run, as a Signal does. + // When unset, the call addresses the independent channel of that name. + temporal.api.common.v1.WorkflowExecution workflow_execution = 3; } message DescribeChannelResponse { @@ -960,6 +980,12 @@ message DescribeChannelResponse { temporal.api.notification.v1.Notification latest = 2; // How many notifications the channel retains for pollers. int32 retained_count = 3; + temporal.api.notification.v1.ChannelKind kind = 4; + // The owner of a linked channel and the run that holds it. Empty for an + // independent channel. + // (-- api-linter: core::0140::prepositions=disabled + // aip.dev/not-precedent: "to" names the owner the channel is linked to. --) + temporal.api.common.v1.WorkflowExecution linked_to = 5; } message SignalWithStartWorkflowExecutionRequest { diff --git a/temporal/api/workflowservice/v1/service.proto b/temporal/api/workflowservice/v1/service.proto index 1f55b34c0..9dbf0317f 100644 --- a/temporal/api/workflowservice/v1/service.proto +++ b/temporal/api/workflowservice/v1/service.proto @@ -496,6 +496,14 @@ service WorkflowService { post: "/api/v1/namespaces/{namespace}/channels/{notification.channel}/notify" body: "*" } + additional_bindings { + post: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{notification.channel}/notify" + body: "*" + } + additional_bindings { + post: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{notification.channel}/notify" + body: "*" + } }; } @@ -511,6 +519,14 @@ service WorkflowService { post: "/api/v1/namespaces/{namespace}/channels/{channel}/listeners" body: "*" } + additional_bindings { + post: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners" + body: "*" + } + additional_bindings { + post: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners" + body: "*" + } }; } @@ -525,6 +541,12 @@ service WorkflowService { additional_bindings { delete: "/api/v1/namespaces/{namespace}/channels/{channel}/listeners/{listener_id}" } + additional_bindings { + delete: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners/{listener_id}" + } + additional_bindings { + delete: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners/{listener_id}" + } }; } @@ -537,6 +559,12 @@ service WorkflowService { additional_bindings { get: "/api/v1/namespaces/{namespace}/channels/{channel}/notifications" } + additional_bindings { + get: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/notifications" + } + additional_bindings { + get: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/notifications" + } }; } @@ -548,6 +576,12 @@ service WorkflowService { additional_bindings { get: "/api/v1/namespaces/{namespace}/channels/{channel}" } + additional_bindings { + get: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}" + } + additional_bindings { + get: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}" + } }; } From 30918a083b1cd1dad8169780d2156269bb100704 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 2 Oct 2026 02:29:24 -0700 Subject: [PATCH 25/30] Added a workflow's channel subscriptions to DescribeWorkflowExecution. The surfaces that show a workflow (the CLI's describe, the UI's workflow page, the SDK description) can list the channels it stands on without one call per channel. `ChannelSubscriptionInfo` covers both kinds: the independent subscriptions a command recorded and the linked channels that hold state. --- openapi/openapiv2.json | 56 +++++++++++++++++++ openapi/openapiv3.yaml | 53 ++++++++++++++++++ temporal/api/workflow/v1/message.proto | 27 +++++++++ .../workflowservice/v1/request_response.proto | 3 + 4 files changed, 139 insertions(+) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 6fdd612c3..34891ed11 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -16066,6 +16066,54 @@ }, "description": "A listener of a channel: a Workflow Execution woken with a Workflow Task, or\na callback the server invokes with each notification." }, + "v1ChannelSubscriptionInfo": { + "type": "object", + "properties": { + "channel": { + "type": "string", + "description": "Channel name." + }, + "kind": { + "$ref": "#/definitions/v1ChannelKind", + "description": "CHANNEL_KIND_INDEPENDENT for a channel the workflow subscribed to with a\nSubscribeNotificationChannel command. CHANNEL_KIND_LINKED for a channel linked to this\nworkflow, which lists it once the channel holds any state." + }, + "subscribedEventId": { + "type": "string", + "format": "int64", + "description": "Independent kind: id of the WorkflowNotificationChannelSubscribed event that recorded the\nsubscription. Zero for the linked kind." + }, + "lastCounter": { + "type": "string", + "format": "int64", + "description": "Highest counter the workflow has accepted from the channel. Zero when none has arrived." + }, + "pendingNotification": { + "$ref": "#/definitions/v1Notification", + "description": "The notification held for the workflow's next Workflow Task, when one is pending." + }, + "scheduledCounter": { + "type": "string", + "format": "int64", + "description": "Counter carried by the scheduled event of a Workflow Task that has not started yet. Zero\notherwise." + }, + "listenerCount": { + "type": "integer", + "format": "int32", + "description": "Linked kind: callback listeners registered on the channel." + }, + "retainedCount": { + "type": "integer", + "format": "int32", + "description": "Linked kind: notifications retained for pollers." + }, + "acceptedCount": { + "type": "string", + "format": "int64", + "description": "Linked kind: notifications the channel has accepted over its life." + } + }, + "description": "A workflow's standing on a notification channel, as reported by DescribeWorkflowExecution." + }, "v1ChildWorkflowExecutionCanceledEventAttributes": { "type": "object", "properties": { @@ -17169,6 +17217,14 @@ }, "workflowExtendedInfo": { "$ref": "#/definitions/v1WorkflowExecutionExtendedInfo" + }, + "channelSubscriptions": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1ChannelSubscriptionInfo" + }, + "description": "The notification channels this run stands on: the independent channels it subscribed to and\nthe channels linked to it that hold any state. Empty when there are none." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 815f9b83f..9d4ee2215 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -11974,6 +11974,52 @@ components: description: |- A listener of a channel: a Workflow Execution woken with a Workflow Task, or a callback the server invokes with each notification. + ChannelSubscriptionInfo: + type: object + properties: + channel: + type: string + description: Channel name. + kind: + enum: + - CHANNEL_KIND_UNSPECIFIED + - CHANNEL_KIND_INDEPENDENT + - CHANNEL_KIND_LINKED + type: string + description: |- + CHANNEL_KIND_INDEPENDENT for a channel the workflow subscribed to with a + SubscribeNotificationChannel command. CHANNEL_KIND_LINKED for a channel linked to this + workflow, which lists it once the channel holds any state. + format: enum + subscribedEventId: + type: string + description: |- + Independent kind: id of the WorkflowNotificationChannelSubscribed event that recorded the + subscription. Zero for the linked kind. + lastCounter: + type: string + description: Highest counter the workflow has accepted from the channel. Zero when none has arrived. + pendingNotification: + allOf: + - $ref: '#/components/schemas/Notification' + description: The notification held for the workflow's next Workflow Task, when one is pending. + scheduledCounter: + type: string + description: |- + Counter carried by the scheduled event of a Workflow Task that has not started yet. Zero + otherwise. + listenerCount: + type: integer + description: 'Linked kind: callback listeners registered on the channel.' + format: int32 + retainedCount: + type: integer + description: 'Linked kind: notifications retained for pollers.' + format: int32 + acceptedCount: + type: string + description: 'Linked kind: notifications the channel has accepted over its life.' + description: A workflow's standing on a notification channel, as reported by DescribeWorkflowExecution. ChildWorkflowExecutionCanceledEventAttributes: type: object properties: @@ -13179,6 +13225,13 @@ components: $ref: '#/components/schemas/PendingNexusOperationInfo' workflowExtendedInfo: $ref: '#/components/schemas/WorkflowExecutionExtendedInfo' + channelSubscriptions: + type: array + items: + $ref: '#/components/schemas/ChannelSubscriptionInfo' + description: |- + The notification channels this run stands on: the independent channels it subscribed to and + the channels linked to it that hold any state. Empty when there are none. DescribeWorkflowRuleResponse: type: object properties: diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index cf763aa12..1cb3e5eba 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -21,6 +21,7 @@ import "temporal/api/enums/v1/workflow.proto"; import "temporal/api/common/v1/message.proto"; import "temporal/api/deployment/v1/message.proto"; import "temporal/api/failure/v1/message.proto"; +import "temporal/api/notification/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; import "temporal/api/sdk/v1/user_metadata.proto"; @@ -586,6 +587,32 @@ message NexusOperationCancellationInfo { string blocked_reason = 7; } +// A workflow's standing on a notification channel, as reported by DescribeWorkflowExecution. +message ChannelSubscriptionInfo { + // Channel name. + string channel = 1; + // CHANNEL_KIND_INDEPENDENT for a channel the workflow subscribed to with a + // SubscribeNotificationChannel command. CHANNEL_KIND_LINKED for a channel linked to this + // workflow, which lists it once the channel holds any state. + temporal.api.notification.v1.ChannelKind kind = 2; + // Independent kind: id of the WorkflowNotificationChannelSubscribed event that recorded the + // subscription. Zero for the linked kind. + int64 subscribed_event_id = 3; + // Highest counter the workflow has accepted from the channel. Zero when none has arrived. + int64 last_counter = 4; + // The notification held for the workflow's next Workflow Task, when one is pending. + temporal.api.notification.v1.Notification pending_notification = 5; + // Counter carried by the scheduled event of a Workflow Task that has not started yet. Zero + // otherwise. + int64 scheduled_counter = 6; + // Linked kind: callback listeners registered on the channel. + int32 listener_count = 7; + // Linked kind: notifications retained for pollers. + int32 retained_count = 8; + // Linked kind: notifications the channel has accepted over its life. + int64 accepted_count = 9; +} + message WorkflowExecutionOptions { // If set, takes precedence over the Versioning Behavior sent by the SDK on Workflow Task completion. VersioningOverride versioning_override = 1; diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index cc046e9bb..acb38e053 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -1320,6 +1320,9 @@ message DescribeWorkflowExecutionResponse { repeated temporal.api.workflow.v1.CallbackInfo callbacks = 6; repeated temporal.api.workflow.v1.PendingNexusOperationInfo pending_nexus_operations = 7; temporal.api.workflow.v1.WorkflowExecutionExtendedInfo workflow_extended_info = 8; + // The notification channels this run stands on: the independent channels it subscribed to and + // the channels linked to it that hold any state. Empty when there are none. + repeated temporal.api.workflow.v1.ChannelSubscriptionInfo channel_subscriptions = 9; } // (-- api-linter: core::0203::optional=disabled From a446be70ee395efa10e01e5bb9ca2ddc0d6b7b17 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 2 Oct 2026 02:37:07 -0700 Subject: [PATCH 26/30] Added the UnsubscribeNotificationChannel command and its event. Until now a subscription ended only with the run. The command removes the run's subscription and its listener registration, and the event records it so replay matches the command. A command for a channel the run is not subscribed to still records its event, since every command must produce one. --- openapi/openapiv2.json | 32 +++++++++++++++++++++--- openapi/openapiv3.yaml | 24 ++++++++++++++++++ temporal/api/command/v1/message.proto | 11 ++++++++ temporal/api/enums/v1/command_type.proto | 1 + temporal/api/enums/v1/event_type.proto | 4 +++ temporal/api/enums/v1/failed_cause.proto | 2 ++ temporal/api/history/v1/message.proto | 14 +++++++++++ 7 files changed, 84 insertions(+), 4 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 34891ed11..ba5a544ad 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -17428,10 +17428,11 @@ "EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED", "EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED", "EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED", - "EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED" + "EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED", + "EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_UNSUBSCRIBED" ], "default": "EVENT_TYPE_UNSPECIFIED", - "description": "- EVENT_TYPE_UNSPECIFIED: Place holder and should never appear in a Workflow execution history\n - EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: Workflow execution has been triggered/started\nIt contains Workflow execution inputs, as well as Workflow timeout configurations\n - EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED: Workflow execution has successfully completed and contains Workflow execution results\n - EVENT_TYPE_WORKFLOW_EXECUTION_FAILED: Workflow execution has unsuccessfully completed and contains the Workflow execution error\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT: Workflow execution has timed out by the Temporal Server\nUsually due to the Workflow having not been completed within timeout settings\n - EVENT_TYPE_WORKFLOW_TASK_SCHEDULED: Workflow Task has been scheduled and the SDK client should now be able to process any new history events\n - EVENT_TYPE_WORKFLOW_TASK_STARTED: Workflow Task has started and the SDK client has picked up the Workflow Task and is processing new history events\n - EVENT_TYPE_WORKFLOW_TASK_COMPLETED: Workflow Task has completed\nThe SDK client picked up the Workflow Task and processed new history events\nSDK client may or may not ask the Temporal Server to do additional work, such as:\nEVENT_TYPE_ACTIVITY_TASK_SCHEDULED\nEVENT_TYPE_TIMER_STARTED\nEVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES\nEVENT_TYPE_MARKER_RECORDED\nEVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED\nEVENT_TYPE_WORKFLOW_EXECUTION_FAILED\nEVENT_TYPE_WORKFLOW_EXECUTION_CANCELED\nEVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW\n - EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: Workflow Task encountered a timeout\nEither an SDK client with a local cache was not available at the time, or it took too long for the SDK client to process the task\n - EVENT_TYPE_WORKFLOW_TASK_FAILED: Workflow Task encountered a failure\nUsually this means that the Workflow was non-deterministic\nHowever, the Workflow reset functionality also uses this event\n - EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: Activity Task was scheduled\nThe SDK client should pick up this activity task and execute\nThis event type contains activity inputs, as well as activity timeout configurations\n - EVENT_TYPE_ACTIVITY_TASK_STARTED: Activity Task has started executing\nThe SDK client has picked up the Activity Task and is processing the Activity invocation\n - EVENT_TYPE_ACTIVITY_TASK_COMPLETED: Activity Task has finished successfully\nThe SDK client has picked up and successfully completed the Activity Task\nThis event type contains Activity execution results\n - EVENT_TYPE_ACTIVITY_TASK_FAILED: Activity Task has finished unsuccessfully\nThe SDK picked up the Activity Task but unsuccessfully completed it\nThis event type contains Activity execution errors\n - EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT: Activity has timed out according to the Temporal Server\nActivity did not complete within the timeout settings\n - EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED: A request to cancel the Activity has occurred\nThe SDK client will be able to confirm cancellation of an Activity during an Activity heartbeat\n - EVENT_TYPE_ACTIVITY_TASK_CANCELED: Activity has been cancelled\n - EVENT_TYPE_TIMER_STARTED: A timer has started\n - EVENT_TYPE_TIMER_FIRED: A timer has fired\n - EVENT_TYPE_TIMER_CANCELED: A time has been cancelled\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED: A request has been made to cancel the Workflow execution\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: SDK client has confirmed the cancellation request and the Workflow execution has been cancelled\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Workflow has requested that the Temporal Server try to cancel another Workflow\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server could not cancel the targeted Workflow\nThis is usually because the target Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED: Temporal Server has successfully requested the cancellation of the target Workflow\n - EVENT_TYPE_MARKER_RECORDED: A marker has been recorded.\nThis event type is transparent to the Temporal Server\nThe Server will only store it and will not try to understand it.\n - EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: Workflow has received a Signal event\nThe event type contains the Signal name, as well as a Signal payload\n - EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: Workflow execution has been forcefully terminated\nThis is usually because the terminate Workflow API was called\n - EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: Workflow has successfully completed and a new Workflow has been started within the same transaction\nContains last Workflow execution results as well as new Workflow execution inputs\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to start a child Workflow\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution cannot be started/triggered\nUsually due to a child Workflow ID collision\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED: Child Workflow execution has successfully started/triggered\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED: Child Workflow execution has successfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution has unsuccessfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED: Child Workflow execution has been cancelled\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT: Child Workflow execution has timed out by the Temporal Server\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED: Child Workflow execution has been terminated\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to Signal the targeted Workflow\nContains the Signal name, as well as a Signal payload\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server cannot Signal the targeted Workflow\nUsually because the Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED: Temporal Server has successfully Signaled the targeted Workflow\n - EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: Workflow search attributes should be updated and synchronized with the visibility store\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: An update was admitted. Note that not all admitted updates result in this\nevent. See UpdateAdmittedEventOrigin for situations in which this event\nis created.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED: An update was accepted (i.e. passed validation, perhaps because no validator was defined)\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REJECTED: This event is never written to history.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED: An update completed\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of the workflow as a whole have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of an already-scheduled activity have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: Workflow properties modified by user workflow code\n - EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: A Nexus operation was scheduled using a ScheduleNexusOperation command.\n - EVENT_TYPE_NEXUS_OPERATION_STARTED: An asynchronous Nexus operation was started by a Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_COMPLETED: A Nexus operation completed successfully.\n - EVENT_TYPE_NEXUS_OPERATION_FAILED: A Nexus operation failed.\n - EVENT_TYPE_NEXUS_OPERATION_CANCELED: A Nexus operation completed as canceled.\n - EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: A Nexus operation timed out.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: A Nexus operation was requested to be canceled using a RequestCancelNexusOperation command.\n - EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: Workflow execution options updated by user.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED: A cancellation request for a Nexus operation was successfully delivered to the Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED: A cancellation request for a Nexus operation resulted in an error.\n - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED: An event that indicates that the workflow execution has been paused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED: An event that indicates that the previously paused workflow execution has been unpaused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED: An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.\n - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: A Workflow subscribed to a stream. Recorded once per subscription, not\nper record: the offsets a task consumed ride WorkflowTaskCompleted and\nthe payloads never enter History at all.\n - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED: A Workflow appended a batch of records to a stream. Recorded per\nbatch, and carrying only the offset range it landed at: the bodies go to\nthe stream's own log, never into History.\n - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED: A Workflow became a listener of a notification channel for its run.\nThe notifications themselves ride the WorkflowTaskScheduled event.", + "description": "- EVENT_TYPE_UNSPECIFIED: Place holder and should never appear in a Workflow execution history\n - EVENT_TYPE_WORKFLOW_EXECUTION_STARTED: Workflow execution has been triggered/started\nIt contains Workflow execution inputs, as well as Workflow timeout configurations\n - EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED: Workflow execution has successfully completed and contains Workflow execution results\n - EVENT_TYPE_WORKFLOW_EXECUTION_FAILED: Workflow execution has unsuccessfully completed and contains the Workflow execution error\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT: Workflow execution has timed out by the Temporal Server\nUsually due to the Workflow having not been completed within timeout settings\n - EVENT_TYPE_WORKFLOW_TASK_SCHEDULED: Workflow Task has been scheduled and the SDK client should now be able to process any new history events\n - EVENT_TYPE_WORKFLOW_TASK_STARTED: Workflow Task has started and the SDK client has picked up the Workflow Task and is processing new history events\n - EVENT_TYPE_WORKFLOW_TASK_COMPLETED: Workflow Task has completed\nThe SDK client picked up the Workflow Task and processed new history events\nSDK client may or may not ask the Temporal Server to do additional work, such as:\nEVENT_TYPE_ACTIVITY_TASK_SCHEDULED\nEVENT_TYPE_TIMER_STARTED\nEVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES\nEVENT_TYPE_MARKER_RECORDED\nEVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED\nEVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED\nEVENT_TYPE_WORKFLOW_EXECUTION_FAILED\nEVENT_TYPE_WORKFLOW_EXECUTION_CANCELED\nEVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW\n - EVENT_TYPE_WORKFLOW_TASK_TIMED_OUT: Workflow Task encountered a timeout\nEither an SDK client with a local cache was not available at the time, or it took too long for the SDK client to process the task\n - EVENT_TYPE_WORKFLOW_TASK_FAILED: Workflow Task encountered a failure\nUsually this means that the Workflow was non-deterministic\nHowever, the Workflow reset functionality also uses this event\n - EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: Activity Task was scheduled\nThe SDK client should pick up this activity task and execute\nThis event type contains activity inputs, as well as activity timeout configurations\n - EVENT_TYPE_ACTIVITY_TASK_STARTED: Activity Task has started executing\nThe SDK client has picked up the Activity Task and is processing the Activity invocation\n - EVENT_TYPE_ACTIVITY_TASK_COMPLETED: Activity Task has finished successfully\nThe SDK client has picked up and successfully completed the Activity Task\nThis event type contains Activity execution results\n - EVENT_TYPE_ACTIVITY_TASK_FAILED: Activity Task has finished unsuccessfully\nThe SDK picked up the Activity Task but unsuccessfully completed it\nThis event type contains Activity execution errors\n - EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT: Activity has timed out according to the Temporal Server\nActivity did not complete within the timeout settings\n - EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED: A request to cancel the Activity has occurred\nThe SDK client will be able to confirm cancellation of an Activity during an Activity heartbeat\n - EVENT_TYPE_ACTIVITY_TASK_CANCELED: Activity has been cancelled\n - EVENT_TYPE_TIMER_STARTED: A timer has started\n - EVENT_TYPE_TIMER_FIRED: A timer has fired\n - EVENT_TYPE_TIMER_CANCELED: A time has been cancelled\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED: A request has been made to cancel the Workflow execution\n - EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED: SDK client has confirmed the cancellation request and the Workflow execution has been cancelled\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Workflow has requested that the Temporal Server try to cancel another Workflow\n - EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server could not cancel the targeted Workflow\nThis is usually because the target Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED: Temporal Server has successfully requested the cancellation of the target Workflow\n - EVENT_TYPE_MARKER_RECORDED: A marker has been recorded.\nThis event type is transparent to the Temporal Server\nThe Server will only store it and will not try to understand it.\n - EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED: Workflow has received a Signal event\nThe event type contains the Signal name, as well as a Signal payload\n - EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED: Workflow execution has been forcefully terminated\nThis is usually because the terminate Workflow API was called\n - EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW: Workflow has successfully completed and a new Workflow has been started within the same transaction\nContains last Workflow execution results as well as new Workflow execution inputs\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to start a child Workflow\n - EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution cannot be started/triggered\nUsually due to a child Workflow ID collision\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED: Child Workflow execution has successfully started/triggered\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED: Child Workflow execution has successfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED: Child Workflow execution has unsuccessfully completed\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED: Child Workflow execution has been cancelled\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT: Child Workflow execution has timed out by the Temporal Server\n - EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED: Child Workflow execution has been terminated\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: Temporal Server will try to Signal the targeted Workflow\nContains the Signal name, as well as a Signal payload\n - EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED: Temporal Server cannot Signal the targeted Workflow\nUsually because the Workflow could not be found\n - EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED: Temporal Server has successfully Signaled the targeted Workflow\n - EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: Workflow search attributes should be updated and synchronized with the visibility store\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ADMITTED: An update was admitted. Note that not all admitted updates result in this\nevent. See UpdateAdmittedEventOrigin for situations in which this event\nis created.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED: An update was accepted (i.e. passed validation, perhaps because no validator was defined)\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_REJECTED: This event is never written to history.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED: An update completed\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of the workflow as a whole have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_ACTIVITY_PROPERTIES_MODIFIED_EXTERNALLY: Some property or properties of an already-scheduled activity have changed by non-workflow code.\nThe distinction of external vs. command-based modification is important so the SDK can\nmaintain determinism when using the command-based approach.\n - EVENT_TYPE_WORKFLOW_PROPERTIES_MODIFIED: Workflow properties modified by user workflow code\n - EVENT_TYPE_NEXUS_OPERATION_SCHEDULED: A Nexus operation was scheduled using a ScheduleNexusOperation command.\n - EVENT_TYPE_NEXUS_OPERATION_STARTED: An asynchronous Nexus operation was started by a Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_COMPLETED: A Nexus operation completed successfully.\n - EVENT_TYPE_NEXUS_OPERATION_FAILED: A Nexus operation failed.\n - EVENT_TYPE_NEXUS_OPERATION_CANCELED: A Nexus operation completed as canceled.\n - EVENT_TYPE_NEXUS_OPERATION_TIMED_OUT: A Nexus operation timed out.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED: A Nexus operation was requested to be canceled using a RequestCancelNexusOperation command.\n - EVENT_TYPE_WORKFLOW_EXECUTION_OPTIONS_UPDATED: Workflow execution options updated by user.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED: A cancellation request for a Nexus operation was successfully delivered to the Nexus handler.\n - EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED: A cancellation request for a Nexus operation resulted in an error.\n - EVENT_TYPE_WORKFLOW_EXECUTION_PAUSED: An event that indicates that the workflow execution has been paused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_UNPAUSED: An event that indicates that the previously paused workflow execution has been unpaused.\n - EVENT_TYPE_WORKFLOW_EXECUTION_TIME_SKIPPING_TRANSITIONED: An event that indicates time skipping advanced time or was disabled automatically after a bound was reached.\n - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED: A Workflow subscribed to a stream. Recorded once per subscription, not\nper record: the offsets a task consumed ride WorkflowTaskCompleted and\nthe payloads never enter History at all.\n - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED: A Workflow appended a batch of records to a stream. Recorded per\nbatch, and carrying only the offset range it landed at: the bodies go to\nthe stream's own log, never into History.\n - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED: A Workflow became a listener of a notification channel for its run.\nThe notifications themselves ride the WorkflowTaskScheduled event.\n - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_UNSUBSCRIBED: A Workflow stopped listening on a notification channel for its run.\nRecorded for every UnsubscribeNotificationChannel command, including one\nnaming a channel the run was not subscribed to.", "title": "Whenever this list of events is changed do change the function shouldBufferEvent in mutableStateBuilder.go to make sure to do the correct event ordering" }, "v1Execution": { @@ -18107,6 +18108,9 @@ }, "workflowNotificationChannelSubscribedEventAttributes": { "$ref": "#/definitions/v1WorkflowNotificationChannelSubscribedEventAttributes" + }, + "workflowNotificationChannelUnsubscribedEventAttributes": { + "$ref": "#/definitions/v1WorkflowNotificationChannelUnsubscribedEventAttributes" } }, "description": "History events are the method by which Temporal SDKs advance (or recreate) workflow state.\nSee the `EventType` enum for more info about what each event is for." @@ -23832,6 +23836,25 @@ } } }, + "v1WorkflowNotificationChannelUnsubscribedEventAttributes": { + "type": "object", + "properties": { + "workflowTaskCompletedEventId": { + "type": "string", + "format": "int64", + "description": "The WorkflowTaskCompleted event of the task whose command ended this\nsubscription." + }, + "channel": { + "type": "string", + "description": "The channel the Workflow stopped listening on." + }, + "subscribedEventId": { + "type": "string", + "format": "int64", + "description": "The WorkflowNotificationChannelSubscribed event that recorded the\nsubscription this command ended. Zero when the run held no subscription\nfor the channel." + } + } + }, "v1WorkflowPropertiesModifiedEventAttributes": { "type": "object", "properties": { @@ -24133,10 +24156,11 @@ "WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES", "WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES", "WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE", - "WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES" + "WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES", + "WORKFLOW_TASK_FAILED_CAUSE_BAD_UNSUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES" ], "default": "WORKFLOW_TASK_FAILED_CAUSE_UNSPECIFIED", - "description": "Workflow tasks can fail for various reasons. Note that some of these reasons can only originate\nfrom the server, and some of them can only originate from the SDK/worker.\n\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND: Between starting and completing the workflow task (with a workflow completion command), some\nnew command (like a signal) was processed into workflow history. The outstanding task will be\nfailed with this reason, and a worker must pick up a new task.\n - WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE: The worker wishes to fail the task and have the next one be generated on a normal, not sticky\nqueue. Generally workers should prefer to use the explicit `ResetStickyTaskQueue` RPC call.\n - WORKFLOW_TASK_FAILED_CAUSE_NON_DETERMINISTIC_ERROR: The worker encountered a mismatch while replaying history between what was expected, and\nwhat the workflow code actually did.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_CHILD_WORKFLOWS_LIMIT_EXCEEDED: We send the below error codes to users when their requests would violate a size constraint\nof their workflow. We do this to ensure that the state of their workflow does not become too\nlarge because that can cause severe performance degradation. You can modify the thresholds for\neach of these errors within your dynamic config.\n\nSpawning a new child workflow would cause this workflow to exceed its limit of pending child\nworkflows.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_ACTIVITIES_LIMIT_EXCEEDED: Starting a new activity would cause this workflow to exceed its limit of pending activities\nthat we track.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_SIGNALS_LIMIT_EXCEEDED: A workflow has a buffer of signals that have not yet reached their destination. We return this\nerror when sending a new signal would exceed the capacity of this buffer.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_REQUEST_CANCEL_LIMIT_EXCEEDED: Similarly, we have a buffer of pending requests to cancel other workflows. We return this error\nwhen our capacity for pending cancel requests is already reached.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UPDATE_WORKFLOW_EXECUTION_MESSAGE: Workflow execution update message (update.Acceptance, update.Rejection, or update.Response)\nhas wrong format, or missing required fields.\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_UPDATE: Similar to WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND, but for updates.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid ScheduleNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_NEXUS_OPERATIONS_LIMIT_EXCEEDED: A workflow task completed requesting to schedule a Nexus Operation exceeding the server configured limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid RequestCancelNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_FEATURE_DISABLED: A workflow task completed requesting a feature that's disabled on the server (either system wide or - typically -\nfor the workflow's namespace).\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE: A workflow task failed because a grpc message was too large.\n - WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE: A workflow task failed because payloads were too large.\n - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE: A workflow task failed because an external storage operation failed.\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED: A workflow task is failed because the workflow is paused before the task is started.\n - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE: A workflow task failed because the request exceeded a size limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES: A workflow task completed with an invalid AppendStreamRecords command.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES: A workflow task completed with an invalid SubscribeStream command.\n - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE: A workflow task could not be started because a stream range it consumed and recorded in\nHistory can no longer be served, for example after truncation or because it exceeds the\nreplay bound. Check the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES: A SubscribeNotificationChannel command named an empty or too-long channel, or hit a\nsubscription or listener limit." + "description": "Workflow tasks can fail for various reasons. Note that some of these reasons can only originate\nfrom the server, and some of them can only originate from the SDK/worker.\n\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND: Between starting and completing the workflow task (with a workflow completion command), some\nnew command (like a signal) was processed into workflow history. The outstanding task will be\nfailed with this reason, and a worker must pick up a new task.\n - WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE: The worker wishes to fail the task and have the next one be generated on a normal, not sticky\nqueue. Generally workers should prefer to use the explicit `ResetStickyTaskQueue` RPC call.\n - WORKFLOW_TASK_FAILED_CAUSE_NON_DETERMINISTIC_ERROR: The worker encountered a mismatch while replaying history between what was expected, and\nwhat the workflow code actually did.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_CHILD_WORKFLOWS_LIMIT_EXCEEDED: We send the below error codes to users when their requests would violate a size constraint\nof their workflow. We do this to ensure that the state of their workflow does not become too\nlarge because that can cause severe performance degradation. You can modify the thresholds for\neach of these errors within your dynamic config.\n\nSpawning a new child workflow would cause this workflow to exceed its limit of pending child\nworkflows.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_ACTIVITIES_LIMIT_EXCEEDED: Starting a new activity would cause this workflow to exceed its limit of pending activities\nthat we track.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_SIGNALS_LIMIT_EXCEEDED: A workflow has a buffer of signals that have not yet reached their destination. We return this\nerror when sending a new signal would exceed the capacity of this buffer.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_REQUEST_CANCEL_LIMIT_EXCEEDED: Similarly, we have a buffer of pending requests to cancel other workflows. We return this error\nwhen our capacity for pending cancel requests is already reached.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UPDATE_WORKFLOW_EXECUTION_MESSAGE: Workflow execution update message (update.Acceptance, update.Rejection, or update.Response)\nhas wrong format, or missing required fields.\n - WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_UPDATE: Similar to WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND, but for updates.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid ScheduleNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_PENDING_NEXUS_OPERATIONS_LIMIT_EXCEEDED: A workflow task completed requesting to schedule a Nexus Operation exceeding the server configured limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_NEXUS_OPERATION_ATTRIBUTES: A workflow task completed with an invalid RequestCancelNexusOperation command.\n - WORKFLOW_TASK_FAILED_CAUSE_FEATURE_DISABLED: A workflow task completed requesting a feature that's disabled on the server (either system wide or - typically -\nfor the workflow's namespace).\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_GRPC_MESSAGE_TOO_LARGE: A workflow task failed because a grpc message was too large.\n - WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE: A workflow task failed because payloads were too large.\n - WORKFLOW_TASK_FAILED_CAUSE_EXTERNAL_STORAGE_FAILURE: A workflow task failed because an external storage operation failed.\nCheck the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_WORKFLOW_PAUSE_REQUESTED_BEFORE_TASK_STARTED: A workflow task is failed because the workflow is paused before the task is started.\n - WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE: A workflow task failed because the request exceeded a size limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_APPEND_STREAM_RECORDS_ATTRIBUTES: A workflow task completed with an invalid AppendStreamRecords command.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES: A workflow task completed with an invalid SubscribeStream command.\n - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE: A workflow task could not be started because a stream range it consumed and recorded in\nHistory can no longer be served, for example after truncation or because it exceeds the\nreplay bound. Check the workflow task failure message for more information.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES: A SubscribeNotificationChannel command named an empty or too-long channel, or hit a\nsubscription or listener limit.\n - WORKFLOW_TASK_FAILED_CAUSE_BAD_UNSUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES: An UnsubscribeNotificationChannel command named an empty or too-long channel." }, "v1WorkflowTaskFailedEventAttributes": { "type": "object", diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 9d4ee2215..746d10ad9 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -13982,6 +13982,7 @@ components: - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED + - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_UNSUBSCRIBED type: string format: enum version: @@ -14153,6 +14154,8 @@ components: $ref: '#/components/schemas/WorkflowStreamRecordsAppendedEventAttributes' workflowNotificationChannelSubscribedEventAttributes: $ref: '#/components/schemas/WorkflowNotificationChannelSubscribedEventAttributes' + workflowNotificationChannelUnsubscribedEventAttributes: + $ref: '#/components/schemas/WorkflowNotificationChannelUnsubscribedEventAttributes' description: |- History events are the method by which Temporal SDKs advance (or recreate) workflow state. See the `EventType` enum for more info about what each event is for. @@ -16755,6 +16758,7 @@ components: - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED + - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_UNSUBSCRIBED type: string description: The event type of the history event generated by the request. format: enum @@ -21180,6 +21184,7 @@ components: - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED + - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_UNSUBSCRIBED type: string format: enum description: EventReference is a direct reference to a history event through the event ID. @@ -21254,6 +21259,7 @@ components: - EVENT_TYPE_WORKFLOW_STREAM_SUBSCRIBED - EVENT_TYPE_WORKFLOW_STREAM_RECORDS_APPENDED - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED + - EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_UNSUBSCRIBED type: string format: enum description: RequestIdReference is a indirect reference to a history event through the request ID. @@ -22261,6 +22267,23 @@ components: channel: type: string description: The channel the Workflow listens on for the rest of this run. + WorkflowNotificationChannelUnsubscribedEventAttributes: + type: object + properties: + workflowTaskCompletedEventId: + type: string + description: |- + The WorkflowTaskCompleted event of the task whose command ended this + subscription. + channel: + type: string + description: The channel the Workflow stopped listening on. + subscribedEventId: + type: string + description: |- + The WorkflowNotificationChannelSubscribed event that recorded the + subscription this command ended. Zero when the run held no subscription + for the channel. WorkflowPropertiesModifiedEventAttributes: type: object properties: @@ -22637,6 +22660,7 @@ components: - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_STREAM_ATTRIBUTES - WORKFLOW_TASK_FAILED_CAUSE_STREAM_RANGE_UNAVAILABLE - WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES + - WORKFLOW_TASK_FAILED_CAUSE_BAD_UNSUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES type: string format: enum failure: diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 38bdc0b35..aadc8f89d 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -329,6 +329,8 @@ message Command { SubscribeStreamCommandAttributes subscribe_stream_command_attributes = 21; SubscribeNotificationChannelCommandAttributes subscribe_notification_channel_command_attributes = 22; + UnsubscribeNotificationChannelCommandAttributes + unsubscribe_notification_channel_command_attributes = 23; } } @@ -379,3 +381,12 @@ message SubscribeNotificationChannelCommandAttributes { // The channel to listen on, as the writers name it. string channel = 1; } + +// Ends the run's subscription to a notification channel. Notifications already +// recorded on a scheduled event still reach that Workflow Task; later ones do +// not. A command naming a channel the run is not subscribed to records its +// event and changes nothing, so replay matches every command to an event. +message UnsubscribeNotificationChannelCommandAttributes { + // The channel to stop listening on, as the writers name it. + string channel = 1; +} diff --git a/temporal/api/enums/v1/command_type.proto b/temporal/api/enums/v1/command_type.proto index b8eada084..629a26528 100644 --- a/temporal/api/enums/v1/command_type.proto +++ b/temporal/api/enums/v1/command_type.proto @@ -32,4 +32,5 @@ enum CommandType { COMMAND_TYPE_APPEND_STREAM_RECORDS = 19; COMMAND_TYPE_SUBSCRIBE_STREAM = 20; COMMAND_TYPE_SUBSCRIBE_NOTIFICATION_CHANNEL = 21; + COMMAND_TYPE_UNSUBSCRIBE_NOTIFICATION_CHANNEL = 22; } diff --git a/temporal/api/enums/v1/event_type.proto b/temporal/api/enums/v1/event_type.proto index f70de73fe..815e1aebe 100644 --- a/temporal/api/enums/v1/event_type.proto +++ b/temporal/api/enums/v1/event_type.proto @@ -186,4 +186,8 @@ enum EventType { // A Workflow became a listener of a notification channel for its run. // The notifications themselves ride the WorkflowTaskScheduled event. EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_SUBSCRIBED = 63; + // A Workflow stopped listening on a notification channel for its run. + // Recorded for every UnsubscribeNotificationChannel command, including one + // naming a channel the run was not subscribed to. + EVENT_TYPE_WORKFLOW_NOTIFICATION_CHANNEL_UNSUBSCRIBED = 64; } diff --git a/temporal/api/enums/v1/failed_cause.proto b/temporal/api/enums/v1/failed_cause.proto index be28778de..9ff4c9a53 100644 --- a/temporal/api/enums/v1/failed_cause.proto +++ b/temporal/api/enums/v1/failed_cause.proto @@ -101,6 +101,8 @@ enum WorkflowTaskFailedCause { // A SubscribeNotificationChannel command named an empty or too-long channel, or hit a // subscription or listener limit. WORKFLOW_TASK_FAILED_CAUSE_BAD_SUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES = 44; + // An UnsubscribeNotificationChannel command named an empty or too-long channel. + WORKFLOW_TASK_FAILED_CAUSE_BAD_UNSUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES = 45; } // Activity tasks can fail for various reasons. Note that some of these reasons can only originate diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 0ec2ccf5e..553683100 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -994,6 +994,18 @@ message WorkflowNotificationChannelSubscribedEventAttributes { string channel = 2; } +message WorkflowNotificationChannelUnsubscribedEventAttributes { + // The WorkflowTaskCompleted event of the task whose command ended this + // subscription. + int64 workflow_task_completed_event_id = 1; + // The channel the Workflow stopped listening on. + string channel = 2; + // The WorkflowNotificationChannelSubscribed event that recorded the + // subscription this command ended. Zero when the run held no subscription + // for the channel. + int64 subscribed_event_id = 3; +} + message WorkflowStreamRecordsAppendedEventAttributes { // The WorkflowTaskCompleted event of the task whose command appended this // batch. @@ -1341,6 +1353,8 @@ message HistoryEvent { WorkflowStreamRecordsAppendedEventAttributes workflow_stream_records_appended_event_attributes = 67; WorkflowNotificationChannelSubscribedEventAttributes workflow_notification_channel_subscribed_event_attributes = 68; + WorkflowNotificationChannelUnsubscribedEventAttributes + workflow_notification_channel_unsubscribed_event_attributes = 69; } } From 07a46d09b6f8b577cabf6ee4d41dce5271dad8b2 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 2 Oct 2026 02:41:32 -0700 Subject: [PATCH 27/30] Put the channel first on the unsubscribe event. The channel and the subscribe event it ended are what a reader of the event looks for, so they lead and the task-completed id follows, as the agreed wire change lists them. --- openapi/openapiv2.json | 10 +++++----- openapi/openapiv3.yaml | 10 +++++----- temporal/api/history/v1/message.proto | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index ba5a544ad..03843564d 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -23839,11 +23839,6 @@ "v1WorkflowNotificationChannelUnsubscribedEventAttributes": { "type": "object", "properties": { - "workflowTaskCompletedEventId": { - "type": "string", - "format": "int64", - "description": "The WorkflowTaskCompleted event of the task whose command ended this\nsubscription." - }, "channel": { "type": "string", "description": "The channel the Workflow stopped listening on." @@ -23852,6 +23847,11 @@ "type": "string", "format": "int64", "description": "The WorkflowNotificationChannelSubscribed event that recorded the\nsubscription this command ended. Zero when the run held no subscription\nfor the channel." + }, + "workflowTaskCompletedEventId": { + "type": "string", + "format": "int64", + "description": "The WorkflowTaskCompleted event of the task whose command ended this\nsubscription." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 746d10ad9..ecb42dc13 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -22270,11 +22270,6 @@ components: WorkflowNotificationChannelUnsubscribedEventAttributes: type: object properties: - workflowTaskCompletedEventId: - type: string - description: |- - The WorkflowTaskCompleted event of the task whose command ended this - subscription. channel: type: string description: The channel the Workflow stopped listening on. @@ -22284,6 +22279,11 @@ components: The WorkflowNotificationChannelSubscribed event that recorded the subscription this command ended. Zero when the run held no subscription for the channel. + workflowTaskCompletedEventId: + type: string + description: |- + The WorkflowTaskCompleted event of the task whose command ended this + subscription. WorkflowPropertiesModifiedEventAttributes: type: object properties: diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 553683100..f2d8c9cf8 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -995,15 +995,15 @@ message WorkflowNotificationChannelSubscribedEventAttributes { } message WorkflowNotificationChannelUnsubscribedEventAttributes { - // The WorkflowTaskCompleted event of the task whose command ended this - // subscription. - int64 workflow_task_completed_event_id = 1; // The channel the Workflow stopped listening on. - string channel = 2; + string channel = 1; // The WorkflowNotificationChannelSubscribed event that recorded the // subscription this command ended. Zero when the run held no subscription // for the channel. - int64 subscribed_event_id = 3; + int64 subscribed_event_id = 2; + // The WorkflowTaskCompleted event of the task whose command ended this + // subscription. + int64 workflow_task_completed_event_id = 3; } message WorkflowStreamRecordsAppendedEventAttributes { From c239d355fbd1bad93c5c3430ea732ca8fb09f693 Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 2 Oct 2026 02:45:09 -0700 Subject: [PATCH 28/30] Restored the file's field order on the unsubscribe event. Every event in this file leads with the task-completed id, the subscribe event included, and a public API review reads the new event next to them. The channel and the subscribe event id follow it. --- openapi/openapiv2.json | 10 +++++----- openapi/openapiv3.yaml | 10 +++++----- temporal/api/history/v1/message.proto | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 03843564d..ba5a544ad 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -23839,6 +23839,11 @@ "v1WorkflowNotificationChannelUnsubscribedEventAttributes": { "type": "object", "properties": { + "workflowTaskCompletedEventId": { + "type": "string", + "format": "int64", + "description": "The WorkflowTaskCompleted event of the task whose command ended this\nsubscription." + }, "channel": { "type": "string", "description": "The channel the Workflow stopped listening on." @@ -23847,11 +23852,6 @@ "type": "string", "format": "int64", "description": "The WorkflowNotificationChannelSubscribed event that recorded the\nsubscription this command ended. Zero when the run held no subscription\nfor the channel." - }, - "workflowTaskCompletedEventId": { - "type": "string", - "format": "int64", - "description": "The WorkflowTaskCompleted event of the task whose command ended this\nsubscription." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index ecb42dc13..746d10ad9 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -22270,6 +22270,11 @@ components: WorkflowNotificationChannelUnsubscribedEventAttributes: type: object properties: + workflowTaskCompletedEventId: + type: string + description: |- + The WorkflowTaskCompleted event of the task whose command ended this + subscription. channel: type: string description: The channel the Workflow stopped listening on. @@ -22279,11 +22284,6 @@ components: The WorkflowNotificationChannelSubscribed event that recorded the subscription this command ended. Zero when the run held no subscription for the channel. - workflowTaskCompletedEventId: - type: string - description: |- - The WorkflowTaskCompleted event of the task whose command ended this - subscription. WorkflowPropertiesModifiedEventAttributes: type: object properties: diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index f2d8c9cf8..553683100 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -995,15 +995,15 @@ message WorkflowNotificationChannelSubscribedEventAttributes { } message WorkflowNotificationChannelUnsubscribedEventAttributes { + // The WorkflowTaskCompleted event of the task whose command ended this + // subscription. + int64 workflow_task_completed_event_id = 1; // The channel the Workflow stopped listening on. - string channel = 1; + string channel = 2; // The WorkflowNotificationChannelSubscribed event that recorded the // subscription this command ended. Zero when the run held no subscription // for the channel. - int64 subscribed_event_id = 2; - // The WorkflowTaskCompleted event of the task whose command ended this - // subscription. - int64 workflow_task_completed_event_id = 3; + int64 subscribed_event_id = 3; } message WorkflowStreamRecordsAppendedEventAttributes { From 4304fd804ce9ebcb1ade8e6e32cd01dc92560d5e Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 2 Oct 2026 17:24:15 -0700 Subject: [PATCH 29/30] Addressed a linked channel by execution rather than by workflow. A standalone activity can own a channel too, so the five channel calls and both `linked_to` fields take `common.v1.Execution` in place of `WorkflowExecution`, with the old numbers reserved. The linked routes bind `execution.business_id` under the workflow and the activity paths, since a binding cannot set the type. The comments on `metadata` and `identity` spell out that one is folded state and the other is for audit only. --- openapi/openapiv2.json | 1890 +++++++++---- openapi/openapiv3.yaml | 2430 +++++++++++------ temporal/api/notification/v1/message.proto | 17 +- .../workflowservice/v1/request_response.proto | 80 +- temporal/api/workflowservice/v1/service.proto | 54 +- 5 files changed, 3046 insertions(+), 1425 deletions(-) diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index ba5a544ad..daf6a3774 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -1021,6 +1021,334 @@ ] } }, + "/api/v1/namespaces/{namespace}/activities/{execution.businessId}/channels/{channel}": { + "get": { + "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", + "operationId": "DescribeChannel6", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DescribeChannelResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/activities/{execution.businessId}/channels/{channel}/listeners": { + "post": { + "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", + "operationId": "RegisterChannelListener6", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RegisterChannelListenerResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/activities/{execution.businessId}/channels/{channel}/listeners/{listenerId}": { + "delete": { + "summary": "UnregisterChannelListener removes a listener from a channel.", + "operationId": "UnregisterChannelListener6", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1UnregisterChannelListenerResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "listenerId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "identity", + "description": "The identity of the caller, for metrics and logs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/activities/{execution.businessId}/channels/{channel}/notifications": { + "get": { + "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", + "operationId": "PollChannel6", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1PollChannelResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "afterCounter", + "description": "Only notifications with a counter above this one are returned.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "wait", + "description": "How long to wait for a notification when none is retained above\n`after_counter`.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maxNotifications", + "description": "At most this many notifications are returned. Zero means the server's\ndefault.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/api/v1/namespaces/{namespace}/activities/{execution.businessId}/channels/{notification.channel}/notify": { + "post": { + "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", + "operationId": "NotifyChannel6", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1NotifyChannelResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "notification.channel", + "description": "The channel the writer notified. Listeners register on the same name.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, "/api/v1/namespaces/{namespace}/activity-complete": { "post": { "summary": "RespondActivityTaskCompleted is called by workers when they successfully complete an activity\ntask.", @@ -1481,13 +1809,27 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.businessId", "in": "query", "required": false, "type": "string" }, { - "name": "workflowExecution.runId", + "name": "execution.runId", "in": "query", "required": false, "type": "string" @@ -1588,13 +1930,27 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.businessId", "in": "query", "required": false, "type": "string" }, { - "name": "workflowExecution.runId", + "name": "execution.runId", "in": "query", "required": false, "type": "string" @@ -1660,13 +2016,27 @@ "format": "int32" }, { - "name": "workflowExecution.workflowId", + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.businessId", "in": "query", "required": false, "type": "string" }, { - "name": "workflowExecution.runId", + "name": "execution.runId", "in": "query", "required": false, "type": "string" @@ -4231,15 +4601,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}": { + "/api/v1/namespaces/{namespace}/workflows/{execution.businessId}/channels/{channel}": { "get": { - "summary": "DescribeWorkflowExecution returns information about the specified workflow execution.", - "operationId": "DescribeWorkflowExecution2", + "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", + "operationId": "DescribeChannel4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1DescribeWorkflowExecutionResponse" + "$ref": "#/definitions/v1DescribeChannelResponse" } }, "default": { @@ -4257,11 +4627,31 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", "in": "path", "required": true, "type": "string" }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, { "name": "execution.runId", "in": "query", @@ -4274,15 +4664,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}/history": { - "get": { - "summary": "GetWorkflowExecutionHistory returns the history of specified workflow execution. Fails with\n`NotFound` if the specified workflow execution is unknown to the service.", - "operationId": "GetWorkflowExecutionHistory2", + "/api/v1/namespaces/{namespace}/workflows/{execution.businessId}/channels/{channel}/listeners": { + "post": { + "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", + "operationId": "RegisterChannelListener4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1GetWorkflowExecutionHistoryResponse" + "$ref": "#/definitions/v1RegisterChannelListenerResponse" } }, "default": { @@ -4300,57 +4690,24 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, { - "name": "execution.runId", - "in": "query", - "required": false, + "name": "channel", + "in": "path", + "required": true, "type": "string" }, { - "name": "maximumPageSize", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "nextPageToken", - "description": "If a `GetWorkflowExecutionHistoryResponse` or a `PollWorkflowTaskQueueResponse` had one of\nthese, it should be passed here to fetch the next page.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "waitNewEvent", - "description": "If set to true, the RPC call will not resolve until there is a new event which matches\nthe `history_event_filter_type`, or a timeout is hit.", - "in": "query", - "required": false, - "type": "boolean" - }, - { - "name": "historyEventFilterType", - "description": "Filter returned events such that they match the specified filter type.\nDefault: HISTORY_EVENT_FILTER_TYPE_ALL_EVENT.", - "in": "query", - "required": false, - "type": "string", - "enum": [ - "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED", - "HISTORY_EVENT_FILTER_TYPE_ALL_EVENT", - "HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT" - ], - "default": "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED" - }, - { - "name": "skipArchival", - "in": "query", - "required": false, - "type": "boolean" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" + } } ], "tags": [ @@ -4358,15 +4715,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}/history-reverse": { - "get": { - "summary": "GetWorkflowExecutionHistoryReverse returns the history of specified workflow execution in reverse\norder (starting from last event). Fails with`NotFound` if the specified workflow execution is\nunknown to the service.", - "operationId": "GetWorkflowExecutionHistoryReverse2", + "/api/v1/namespaces/{namespace}/workflows/{execution.businessId}/channels/{channel}/listeners/{listenerId}": { + "delete": { + "summary": "UnregisterChannelListener removes a listener from a channel.", + "operationId": "UnregisterChannelListener4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1GetWorkflowExecutionHistoryReverseResponse" + "$ref": "#/definitions/v1UnregisterChannelListenerResponse" } }, "default": { @@ -4384,30 +4741,49 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, { - "name": "execution.runId", + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "listenerId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "identity", + "description": "The identity of the caller, for metrics and logs.", "in": "query", "required": false, "type": "string" }, { - "name": "maximumPageSize", + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", "in": "query", "required": false, - "type": "integer", - "format": "int32" + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" }, { - "name": "nextPageToken", + "name": "execution.runId", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" } ], "tags": [ @@ -4415,15 +4791,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}/query/{query.queryType}": { - "post": { - "summary": "QueryWorkflow requests a query be executed for a specified workflow execution.", - "operationId": "QueryWorkflow2", + "/api/v1/namespaces/{namespace}/workflows/{execution.businessId}/channels/{channel}/notifications": { + "get": { + "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", + "operationId": "PollChannel4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1QueryWorkflowResponse" + "$ref": "#/definitions/v1PollChannelResponse" } }, "default": { @@ -4441,25 +4817,59 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, { - "name": "query.queryType", - "description": "The workflow-author-defined identifier of the query. Typically a function name.", + "name": "channel", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceQueryWorkflowBody" - } + "name": "afterCounter", + "description": "Only notifications with a counter above this one are returned.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "wait", + "description": "How long to wait for a notification when none is retained above\n`after_counter`.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maxNotifications", + "description": "At most this many notifications are returned. Zero means the server's\ndefault.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -4467,15 +4877,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}/trigger-rule": { + "/api/v1/namespaces/{namespace}/workflows/{execution.businessId}/channels/{notification.channel}/notify": { "post": { - "summary": "TriggerWorkflowRule allows to:\n * trigger existing rule for a specific workflow execution;\n * trigger rule for a specific workflow execution without creating a rule;\nThis is useful for one-off operations.", - "operationId": "TriggerWorkflowRule2", + "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", + "operationId": "NotifyChannel4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1TriggerWorkflowRuleResponse" + "$ref": "#/definitions/v1NotifyChannelResponse" } }, "default": { @@ -4493,7 +4903,14 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "notification.channel", + "description": "The channel the writer notified. Listeners register on the same name.", "in": "path", "required": true, "type": "string" @@ -4503,7 +4920,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceTriggerWorkflowRuleBody" + "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" } } ], @@ -4512,16 +4929,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/cancel": { - "post": { - "summary": "RequestCancelWorkflowExecution is called by workers when they want to request cancellation of\na workflow execution.", - "description": "This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the\nworkflow history and a new workflow task created for the workflow. It returns success if the requested\nworkflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist.", - "operationId": "RequestCancelWorkflowExecution2", + "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}": { + "get": { + "summary": "DescribeWorkflowExecution returns information about the specified workflow execution.", + "operationId": "DescribeWorkflowExecution2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RequestCancelWorkflowExecutionResponse" + "$ref": "#/definitions/v1DescribeWorkflowExecutionResponse" } }, "default": { @@ -4539,18 +4955,16 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceRequestCancelWorkflowExecutionBody" - } + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -4558,15 +4972,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}": { + "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}/history": { "get": { - "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", - "operationId": "DescribeChannel4", + "summary": "GetWorkflowExecutionHistory returns the history of specified workflow execution. Fails with\n`NotFound` if the specified workflow execution is unknown to the service.", + "operationId": "GetWorkflowExecutionHistory2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1DescribeChannelResponse" + "$ref": "#/definitions/v1GetWorkflowExecutionHistoryResponse" } }, "default": { @@ -4584,22 +4998,57 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "channel", - "in": "path", - "required": true, + "name": "execution.runId", + "in": "query", + "required": false, "type": "string" }, { - "name": "workflowExecution.runId", + "name": "maximumPageSize", "in": "query", "required": false, - "type": "string" + "type": "integer", + "format": "int32" + }, + { + "name": "nextPageToken", + "description": "If a `GetWorkflowExecutionHistoryResponse` or a `PollWorkflowTaskQueueResponse` had one of\nthese, it should be passed here to fetch the next page.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + }, + { + "name": "waitNewEvent", + "description": "If set to true, the RPC call will not resolve until there is a new event which matches\nthe `history_event_filter_type`, or a timeout is hit.", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "historyEventFilterType", + "description": "Filter returned events such that they match the specified filter type.\nDefault: HISTORY_EVENT_FILTER_TYPE_ALL_EVENT.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED", + "HISTORY_EVENT_FILTER_TYPE_ALL_EVENT", + "HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT" + ], + "default": "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED" + }, + { + "name": "skipArchival", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ @@ -4607,15 +5056,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/listeners": { - "post": { - "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", - "operationId": "RegisterChannelListener4", + "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}/history-reverse": { + "get": { + "summary": "GetWorkflowExecutionHistoryReverse returns the history of specified workflow execution in reverse\norder (starting from last event). Fails with`NotFound` if the specified workflow execution is\nunknown to the service.", + "operationId": "GetWorkflowExecutionHistoryReverse2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RegisterChannelListenerResponse" + "$ref": "#/definitions/v1GetWorkflowExecutionHistoryReverseResponse" } }, "default": { @@ -4633,24 +5082,30 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "channel", - "in": "path", - "required": true, + "name": "execution.runId", + "in": "query", + "required": false, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" - } + "name": "maximumPageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "nextPageToken", + "in": "query", + "required": false, + "type": "string", + "format": "byte" } ], "tags": [ @@ -4658,15 +5113,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/listeners/{listenerId}": { - "delete": { - "summary": "UnregisterChannelListener removes a listener from a channel.", - "operationId": "UnregisterChannelListener4", + "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}/query/{query.queryType}": { + "post": { + "summary": "QueryWorkflow requests a query be executed for a specified workflow execution.", + "operationId": "QueryWorkflow2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1UnregisterChannelListenerResponse" + "$ref": "#/definitions/v1QueryWorkflowResponse" } }, "default": { @@ -4684,35 +5139,25 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "channel", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "listenerId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "identity", - "description": "The identity of the caller, for metrics and logs.", - "in": "query", - "required": false, + "name": "query.queryType", + "description": "The workflow-author-defined identifier of the query. Typically a function name.", + "in": "path", + "required": true, "type": "string" }, { - "name": "workflowExecution.runId", - "in": "query", - "required": false, - "type": "string" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceQueryWorkflowBody" + } } ], "tags": [ @@ -4720,15 +5165,15 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/notifications": { - "get": { - "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", - "operationId": "PollChannel4", + "/api/v1/namespaces/{namespace}/workflows/{execution.workflowId}/trigger-rule": { + "post": { + "summary": "TriggerWorkflowRule allows to:\n * trigger existing rule for a specific workflow execution;\n * trigger rule for a specific workflow execution without creating a rule;\nThis is useful for one-off operations.", + "operationId": "TriggerWorkflowRule2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1PollChannelResponse" + "$ref": "#/definitions/v1TriggerWorkflowRuleResponse" } }, "default": { @@ -4746,45 +5191,18 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "channel", - "in": "path", + "name": "body", + "in": "body", "required": true, - "type": "string" - }, - { - "name": "afterCounter", - "description": "Only notifications with a counter above this one are returned.", - "in": "query", - "required": false, - "type": "string", - "format": "int64" - }, - { - "name": "wait", - "description": "How long to wait for a notification when none is retained above\n`after_counter`.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "maxNotifications", - "description": "At most this many notifications are returned. Zero means the server's\ndefault.", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "workflowExecution.runId", - "in": "query", - "required": false, - "type": "string" + "schema": { + "$ref": "#/definitions/WorkflowServiceTriggerWorkflowRuleBody" + } } ], "tags": [ @@ -4792,15 +5210,16 @@ ] } }, - "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{notification.channel}/notify": { + "/api/v1/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/cancel": { "post": { - "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", - "operationId": "NotifyChannel4", + "summary": "RequestCancelWorkflowExecution is called by workers when they want to request cancellation of\na workflow execution.", + "description": "This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the\nworkflow history and a new workflow task created for the workflow. It returns success if the requested\nworkflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist.", + "operationId": "RequestCancelWorkflowExecution2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1NotifyChannelResponse" + "$ref": "#/definitions/v1RequestCancelWorkflowExecutionResponse" } }, "default": { @@ -4823,19 +5242,12 @@ "required": true, "type": "string" }, - { - "name": "notification.channel", - "description": "The channel the writer notified. Listeners register on the same name.", - "in": "path", - "required": true, - "type": "string" - }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" + "$ref": "#/definitions/WorkflowServiceRequestCancelWorkflowExecutionBody" } } ], @@ -6672,16 +7084,202 @@ "tags": [ "WorkflowService" ] - }, + }, + "post": { + "summary": "StartActivityExecution starts a new activity execution.", + "description": "Returns an `ActivityExecutionAlreadyStarted` error if an instance already exists with same activity ID in this namespace\nunless permitted by the specified ID conflict policy.", + "operationId": "StartActivityExecution", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1StartActivityExecutionResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "activityId", + "description": "Identifier for this activity. Required. This identifier should be meaningful in the user's\nown system. It must be unique among activities in the same namespace, subject to the rules\nimposed by id_reuse_policy and id_conflict_policy.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceStartActivityExecutionBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/activities/{activityId}/cancel": { + "post": { + "summary": "RequestCancelActivityExecution requests cancellation of an activity execution.", + "description": "Cancellation is cooperative: this call records the request, but the activity must detect and\nacknowledge it for the activity to reach CANCELED status. The cancellation signal is\ndelivered via `cancel_requested` in the heartbeat response; SDKs surface this via\nlanguage-idiomatic mechanisms (context cancellation, exceptions, abort signals).", + "operationId": "RequestCancelActivityExecution", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RequestCancelActivityExecutionResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "activityId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRequestCancelActivityExecutionBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/activities/{activityId}/complete": { + "post": { + "summary": "See `RespondActivityTaskCompleted`. This version allows clients to record completions by\nnamespace/workflow id/activity id instead of task token.", + "operationId": "RespondActivityTaskCompletedById", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RespondActivityTaskCompletedByIdResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "Namespace of the workflow which scheduled this activity", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "activityId", + "description": "Id of the activity to complete", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRespondActivityTaskCompletedByIdBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/activities/{activityId}/fail": { + "post": { + "summary": "See `RecordActivityTaskFailed`. This version allows clients to record failures by\nnamespace/workflow id/activity id instead of task token.", + "operationId": "RespondActivityTaskFailedById", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1RespondActivityTaskFailedByIdResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "description": "Namespace of the workflow which scheduled this activity", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "activityId", + "description": "Id of the activity to fail", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRespondActivityTaskFailedByIdBody" + } + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/activities/{activityId}/heartbeat": { "post": { - "summary": "StartActivityExecution starts a new activity execution.", - "description": "Returns an `ActivityExecutionAlreadyStarted` error if an instance already exists with same activity ID in this namespace\nunless permitted by the specified ID conflict policy.", - "operationId": "StartActivityExecution", + "summary": "See `RecordActivityTaskHeartbeat`. This version allows clients to record heartbeats by\nnamespace/workflow id/activity id instead of task token.", + "operationId": "RecordActivityTaskHeartbeatById", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1StartActivityExecutionResponse" + "$ref": "#/definitions/v1RecordActivityTaskHeartbeatByIdResponse" } }, "default": { @@ -6694,13 +7292,14 @@ "parameters": [ { "name": "namespace", + "description": "Namespace of the workflow which scheduled this activity", "in": "path", "required": true, "type": "string" }, { "name": "activityId", - "description": "Identifier for this activity. Required. This identifier should be meaningful in the user's\nown system. It must be unique among activities in the same namespace, subject to the rules\nimposed by id_reuse_policy and id_conflict_policy.", + "description": "Id of the activity we're heartbeating", "in": "path", "required": true, "type": "string" @@ -6710,7 +7309,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceStartActivityExecutionBody" + "$ref": "#/definitions/WorkflowServiceRecordActivityTaskHeartbeatByIdBody" } } ], @@ -6719,16 +7318,15 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/cancel": { - "post": { - "summary": "RequestCancelActivityExecution requests cancellation of an activity execution.", - "description": "Cancellation is cooperative: this call records the request, but the activity must detect and\nacknowledge it for the activity to reach CANCELED status. The cancellation signal is\ndelivered via `cancel_requested` in the heartbeat response; SDKs surface this via\nlanguage-idiomatic mechanisms (context cancellation, exceptions, abort signals).", - "operationId": "RequestCancelActivityExecution", + "/namespaces/{namespace}/activities/{activityId}/outcome": { + "get": { + "summary": "PollActivityExecution long-polls for an activity execution to complete and returns the\noutcome (result or failure).", + "operationId": "PollActivityExecution", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RequestCancelActivityExecutionResponse" + "$ref": "#/definitions/v1PollActivityExecutionResponse" } }, "default": { @@ -6752,12 +7350,11 @@ "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceRequestCancelActivityExecutionBody" - } + "name": "runId", + "description": "Activity run ID. If empty the request targets the latest run.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -6765,15 +7362,16 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/complete": { + "/namespaces/{namespace}/activities/{activityId}/pause": { "post": { - "summary": "See `RespondActivityTaskCompleted`. This version allows clients to record completions by\nnamespace/workflow id/activity id instead of task token.", - "operationId": "RespondActivityTaskCompletedById", + "summary": "PauseActivityExecution pauses the execution of an activity specified by its ID.\nThis API can be used to target a workflow activity or a standalone activity", + "description": "Pausing an activity means:\n- If the activity is currently waiting for a retry or is running and subsequently fails,\n it will not be rescheduled until it is unpaused.\n- If the activity is already paused, calling this method will have no effect.\n- If the activity is running and finishes successfully, the activity will be completed.\n- If the activity is running and finishes with failure:\n * if there is no retry left - the activity will be completed.\n * if there are more retries left - the activity will be paused.\nFor long-running activities:\n- activities in paused state will send a cancellation with \"activity_paused\" set to 'true' in response to 'RecordActivityTaskHeartbeat'.\n\nReturns a `NotFound` error if there is no pending activity with the provided ID", + "operationId": "PauseActivityExecution", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RespondActivityTaskCompletedByIdResponse" + "$ref": "#/definitions/v1PauseActivityExecutionResponse" } }, "default": { @@ -6786,14 +7384,14 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity", + "description": "Namespace of the workflow which scheduled this activity.", "in": "path", "required": true, "type": "string" }, { "name": "activityId", - "description": "Id of the activity to complete", + "description": "The ID of the activity to target.", "in": "path", "required": true, "type": "string" @@ -6803,7 +7401,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceRespondActivityTaskCompletedByIdBody" + "$ref": "#/definitions/WorkflowServicePauseActivityExecutionBody" } } ], @@ -6812,15 +7410,16 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/fail": { + "/namespaces/{namespace}/activities/{activityId}/reset": { "post": { - "summary": "See `RecordActivityTaskFailed`. This version allows clients to record failures by\nnamespace/workflow id/activity id instead of task token.", - "operationId": "RespondActivityTaskFailedById", + "summary": "ResetActivityExecution resets the execution of an activity specified by its ID.\nThis API can be used to target a workflow activity or a standalone activity.", + "description": "Resetting an activity means:\n* number of attempts will be reset to 0.\n* activity timeouts will be reset.\n* if the activity is waiting for retry, and it is not paused or 'keep_paused' is not provided:\n it will be scheduled immediately (* see 'jitter' flag)\n\nReturns a `NotFound` error if there is no pending activity with the provided ID or type.", + "operationId": "ResetActivityExecution", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RespondActivityTaskFailedByIdResponse" + "$ref": "#/definitions/v1ResetActivityExecutionResponse" } }, "default": { @@ -6833,14 +7432,14 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity", + "description": "Namespace of the workflow which scheduled this activity.", "in": "path", "required": true, "type": "string" }, { "name": "activityId", - "description": "Id of the activity to fail", + "description": "The ID of the activity to target.", "in": "path", "required": true, "type": "string" @@ -6850,7 +7449,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceRespondActivityTaskFailedByIdBody" + "$ref": "#/definitions/WorkflowServiceResetActivityExecutionBody" } } ], @@ -6859,15 +7458,15 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/heartbeat": { + "/namespaces/{namespace}/activities/{activityId}/resolve-as-canceled": { "post": { - "summary": "See `RecordActivityTaskHeartbeat`. This version allows clients to record heartbeats by\nnamespace/workflow id/activity id instead of task token.", - "operationId": "RecordActivityTaskHeartbeatById", + "summary": "See `RespondActivityTaskCanceled`. This version allows clients to record failures by\nnamespace/workflow id/activity id instead of task token.", + "operationId": "RespondActivityTaskCanceledById", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RecordActivityTaskHeartbeatByIdResponse" + "$ref": "#/definitions/v1RespondActivityTaskCanceledByIdResponse" } }, "default": { @@ -6887,7 +7486,7 @@ }, { "name": "activityId", - "description": "Id of the activity we're heartbeating", + "description": "Id of the activity to confirm is cancelled", "in": "path", "required": true, "type": "string" @@ -6897,7 +7496,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceRecordActivityTaskHeartbeatByIdBody" + "$ref": "#/definitions/WorkflowServiceRespondActivityTaskCanceledByIdBody" } } ], @@ -6906,15 +7505,16 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/outcome": { - "get": { - "summary": "PollActivityExecution long-polls for an activity execution to complete and returns the\noutcome (result or failure).", - "operationId": "PollActivityExecution", + "/namespaces/{namespace}/activities/{activityId}/terminate": { + "post": { + "summary": "TerminateActivityExecution terminates an existing activity execution immediately.", + "description": "Termination does not reach the worker and the activity code cannot react to it. A terminated activity may have a\nrunning attempt.", + "operationId": "TerminateActivityExecution", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1PollActivityExecutionResponse" + "$ref": "#/definitions/v1TerminateActivityExecutionResponse" } }, "default": { @@ -6938,11 +7538,12 @@ "type": "string" }, { - "name": "runId", - "description": "Activity run ID. If empty the request targets the latest run.", - "in": "query", - "required": false, - "type": "string" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceTerminateActivityExecutionBody" + } } ], "tags": [ @@ -6950,16 +7551,16 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/pause": { + "/namespaces/{namespace}/activities/{activityId}/unpause": { "post": { - "summary": "PauseActivityExecution pauses the execution of an activity specified by its ID.\nThis API can be used to target a workflow activity or a standalone activity", - "description": "Pausing an activity means:\n- If the activity is currently waiting for a retry or is running and subsequently fails,\n it will not be rescheduled until it is unpaused.\n- If the activity is already paused, calling this method will have no effect.\n- If the activity is running and finishes successfully, the activity will be completed.\n- If the activity is running and finishes with failure:\n * if there is no retry left - the activity will be completed.\n * if there are more retries left - the activity will be paused.\nFor long-running activities:\n- activities in paused state will send a cancellation with \"activity_paused\" set to 'true' in response to 'RecordActivityTaskHeartbeat'.\n\nReturns a `NotFound` error if there is no pending activity with the provided ID", - "operationId": "PauseActivityExecution", + "summary": "UnpauseActivityExecution unpauses the execution of an activity specified by its ID.\nThis API can be used to target a workflow activity or a standalone activity.", + "description": "If activity is not paused, this call will have no effect.\nIf the activity was paused while waiting for retry, it will be scheduled immediately (* see 'jitter' flag).\nOnce the activity is unpaused, all timeout timers will be regenerated.\n\nReturns a `NotFound` error if there is no pending activity with the provided ID", + "operationId": "UnpauseActivityExecution", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1PauseActivityExecutionResponse" + "$ref": "#/definitions/v1UnpauseActivityExecutionResponse" } }, "default": { @@ -6989,7 +7590,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServicePauseActivityExecutionBody" + "$ref": "#/definitions/WorkflowServiceUnpauseActivityExecutionBody" } } ], @@ -6998,16 +7599,15 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/reset": { + "/namespaces/{namespace}/activities/{activityId}/update-options": { "post": { - "summary": "ResetActivityExecution resets the execution of an activity specified by its ID.\nThis API can be used to target a workflow activity or a standalone activity.", - "description": "Resetting an activity means:\n* number of attempts will be reset to 0.\n* activity timeouts will be reset.\n* if the activity is waiting for retry, and it is not paused or 'keep_paused' is not provided:\n it will be scheduled immediately (* see 'jitter' flag)\n\nReturns a `NotFound` error if there is no pending activity with the provided ID or type.", - "operationId": "ResetActivityExecution", + "summary": "UpdateActivityExecutionOptions is called by the client to update the options of an activity by its ID.\nThis API can be used to target a workflow activity or a standalone activity.", + "operationId": "UpdateActivityExecutionOptions", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1ResetActivityExecutionResponse" + "$ref": "#/definitions/v1UpdateActivityExecutionOptionsResponse" } }, "default": { @@ -7020,7 +7620,7 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity.", + "description": "Namespace of the workflow which scheduled this activity", "in": "path", "required": true, "type": "string" @@ -7037,7 +7637,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceResetActivityExecutionBody" + "$ref": "#/definitions/WorkflowServiceUpdateActivityExecutionOptionsBody" } } ], @@ -7046,15 +7646,15 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/resolve-as-canceled": { - "post": { - "summary": "See `RespondActivityTaskCanceled`. This version allows clients to record failures by\nnamespace/workflow id/activity id instead of task token.", - "operationId": "RespondActivityTaskCanceledById", + "/namespaces/{namespace}/activities/{execution.businessId}/channels/{channel}": { + "get": { + "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", + "operationId": "DescribeChannel5", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RespondActivityTaskCanceledByIdResponse" + "$ref": "#/definitions/v1DescribeChannelResponse" } }, "default": { @@ -7067,25 +7667,41 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity", "in": "path", "required": true, "type": "string" }, { - "name": "activityId", - "description": "Id of the activity to confirm is cancelled", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "channel", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceRespondActivityTaskCanceledByIdBody" - } + "type": "string" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -7093,16 +7709,15 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/terminate": { + "/namespaces/{namespace}/activities/{execution.businessId}/channels/{channel}/listeners": { "post": { - "summary": "TerminateActivityExecution terminates an existing activity execution immediately.", - "description": "Termination does not reach the worker and the activity code cannot react to it. A terminated activity may have a\nrunning attempt.", - "operationId": "TerminateActivityExecution", + "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", + "operationId": "RegisterChannelListener5", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1TerminateActivityExecutionResponse" + "$ref": "#/definitions/v1RegisterChannelListenerResponse" } }, "default": { @@ -7120,7 +7735,13 @@ "type": "string" }, { - "name": "activityId", + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", "in": "path", "required": true, "type": "string" @@ -7130,7 +7751,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceTerminateActivityExecutionBody" + "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" } } ], @@ -7139,16 +7760,91 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/unpause": { - "post": { - "summary": "UnpauseActivityExecution unpauses the execution of an activity specified by its ID.\nThis API can be used to target a workflow activity or a standalone activity.", - "description": "If activity is not paused, this call will have no effect.\nIf the activity was paused while waiting for retry, it will be scheduled immediately (* see 'jitter' flag).\nOnce the activity is unpaused, all timeout timers will be regenerated.\n\nReturns a `NotFound` error if there is no pending activity with the provided ID", - "operationId": "UnpauseActivityExecution", + "/namespaces/{namespace}/activities/{execution.businessId}/channels/{channel}/listeners/{listenerId}": { + "delete": { + "summary": "UnregisterChannelListener removes a listener from a channel.", + "operationId": "UnregisterChannelListener5", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1UnpauseActivityExecutionResponse" + "$ref": "#/definitions/v1UnregisterChannelListenerResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "listenerId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "identity", + "description": "The identity of the caller, for metrics and logs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "WorkflowService" + ] + } + }, + "/namespaces/{namespace}/activities/{execution.businessId}/channels/{channel}/notifications": { + "get": { + "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", + "operationId": "PollChannel5", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1PollChannelResponse" } }, "default": { @@ -7161,25 +7857,64 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity.", "in": "path", "required": true, "type": "string" }, { - "name": "activityId", - "description": "The ID of the activity to target.", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "channel", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceUnpauseActivityExecutionBody" - } + "type": "string" + }, + { + "name": "afterCounter", + "description": "Only notifications with a counter above this one are returned.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "wait", + "description": "How long to wait for a notification when none is retained above\n`after_counter`.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maxNotifications", + "description": "At most this many notifications are returned. Zero means the server's\ndefault.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -7187,15 +7922,15 @@ ] } }, - "/namespaces/{namespace}/activities/{activityId}/update-options": { + "/namespaces/{namespace}/activities/{execution.businessId}/channels/{notification.channel}/notify": { "post": { - "summary": "UpdateActivityExecutionOptions is called by the client to update the options of an activity by its ID.\nThis API can be used to target a workflow activity or a standalone activity.", - "operationId": "UpdateActivityExecutionOptions", + "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", + "operationId": "NotifyChannel5", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1UpdateActivityExecutionOptionsResponse" + "$ref": "#/definitions/v1NotifyChannelResponse" } }, "default": { @@ -7208,14 +7943,19 @@ "parameters": [ { "name": "namespace", - "description": "Namespace of the workflow which scheduled this activity", "in": "path", "required": true, "type": "string" }, { - "name": "activityId", - "description": "The ID of the activity to target.", + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "notification.channel", + "description": "The channel the writer notified. Listeners register on the same name.", "in": "path", "required": true, "type": "string" @@ -7225,7 +7965,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceUpdateActivityExecutionOptionsBody" + "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" } } ], @@ -7694,13 +8434,27 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.businessId", "in": "query", "required": false, "type": "string" }, { - "name": "workflowExecution.runId", + "name": "execution.runId", "in": "query", "required": false, "type": "string" @@ -7801,13 +8555,27 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.businessId", "in": "query", "required": false, "type": "string" }, { - "name": "workflowExecution.runId", + "name": "execution.runId", "in": "query", "required": false, "type": "string" @@ -7873,13 +8641,27 @@ "format": "int32" }, { - "name": "workflowExecution.workflowId", + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.businessId", "in": "query", "required": false, "type": "string" }, { - "name": "workflowExecution.runId", + "name": "execution.runId", "in": "query", "required": false, "type": "string" @@ -10374,15 +11156,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}": { + "/namespaces/{namespace}/workflows/{execution.businessId}/channels/{channel}": { "get": { - "summary": "DescribeWorkflowExecution returns information about the specified workflow execution.", - "operationId": "DescribeWorkflowExecution", + "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", + "operationId": "DescribeChannel3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1DescribeWorkflowExecutionResponse" + "$ref": "#/definitions/v1DescribeChannelResponse" } }, "default": { @@ -10400,11 +11182,31 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, + { + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, { "name": "execution.runId", "in": "query", @@ -10417,15 +11219,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}/history": { - "get": { - "summary": "GetWorkflowExecutionHistory returns the history of specified workflow execution. Fails with\n`NotFound` if the specified workflow execution is unknown to the service.", - "operationId": "GetWorkflowExecutionHistory", + "/namespaces/{namespace}/workflows/{execution.businessId}/channels/{channel}/listeners": { + "post": { + "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", + "operationId": "RegisterChannelListener3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1GetWorkflowExecutionHistoryResponse" + "$ref": "#/definitions/v1RegisterChannelListenerResponse" } }, "default": { @@ -10443,57 +11245,24 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, { - "name": "execution.runId", - "in": "query", - "required": false, + "name": "channel", + "in": "path", + "required": true, "type": "string" }, { - "name": "maximumPageSize", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "nextPageToken", - "description": "If a `GetWorkflowExecutionHistoryResponse` or a `PollWorkflowTaskQueueResponse` had one of\nthese, it should be passed here to fetch the next page.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "waitNewEvent", - "description": "If set to true, the RPC call will not resolve until there is a new event which matches\nthe `history_event_filter_type`, or a timeout is hit.", - "in": "query", - "required": false, - "type": "boolean" - }, - { - "name": "historyEventFilterType", - "description": "Filter returned events such that they match the specified filter type.\nDefault: HISTORY_EVENT_FILTER_TYPE_ALL_EVENT.", - "in": "query", - "required": false, - "type": "string", - "enum": [ - "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED", - "HISTORY_EVENT_FILTER_TYPE_ALL_EVENT", - "HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT" - ], - "default": "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED" - }, - { - "name": "skipArchival", - "in": "query", - "required": false, - "type": "boolean" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" + } } ], "tags": [ @@ -10501,15 +11270,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}/history-reverse": { - "get": { - "summary": "GetWorkflowExecutionHistoryReverse returns the history of specified workflow execution in reverse\norder (starting from last event). Fails with`NotFound` if the specified workflow execution is\nunknown to the service.", - "operationId": "GetWorkflowExecutionHistoryReverse", + "/namespaces/{namespace}/workflows/{execution.businessId}/channels/{channel}/listeners/{listenerId}": { + "delete": { + "summary": "UnregisterChannelListener removes a listener from a channel.", + "operationId": "UnregisterChannelListener3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1GetWorkflowExecutionHistoryReverseResponse" + "$ref": "#/definitions/v1UnregisterChannelListenerResponse" } }, "default": { @@ -10527,30 +11296,49 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, { - "name": "execution.runId", + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "listenerId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "identity", + "description": "The identity of the caller, for metrics and logs.", "in": "query", "required": false, "type": "string" }, { - "name": "maximumPageSize", + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", "in": "query", "required": false, - "type": "integer", - "format": "int32" + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" }, { - "name": "nextPageToken", + "name": "execution.runId", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" } ], "tags": [ @@ -10558,15 +11346,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}/query/{query.queryType}": { - "post": { - "summary": "QueryWorkflow requests a query be executed for a specified workflow execution.", - "operationId": "QueryWorkflow", + "/namespaces/{namespace}/workflows/{execution.businessId}/channels/{channel}/notifications": { + "get": { + "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", + "operationId": "PollChannel3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1QueryWorkflowResponse" + "$ref": "#/definitions/v1PollChannelResponse" } }, "default": { @@ -10584,25 +11372,59 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", "in": "path", "required": true, "type": "string" }, { - "name": "query.queryType", - "description": "The workflow-author-defined identifier of the query. Typically a function name.", - "in": "path", - "required": true, + "name": "channel", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "afterCounter", + "description": "Only notifications with a counter above this one are returned.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "wait", + "description": "How long to wait for a notification when none is retained above\n`after_counter`.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "maxNotifications", + "description": "At most this many notifications are returned. Zero means the server's\ndefault.", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "execution.type", + "description": " - EXECUTION_TYPE_WORKFLOW: A workflow execution archetype.\n - EXECUTION_TYPE_ACTIVITY: An activity execution archetype. This is reserved for standalone activities.\n - EXECUTION_TYPE_NEXUS_OPERATION: A Nexus operation execution archetype. This is reserved for standalone Nexus operations.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "EXECUTION_TYPE_UNSPECIFIED", + "EXECUTION_TYPE_WORKFLOW", + "EXECUTION_TYPE_ACTIVITY", + "EXECUTION_TYPE_NEXUS_OPERATION" + ], + "default": "EXECUTION_TYPE_UNSPECIFIED" + }, + { + "name": "execution.runId", + "in": "query", + "required": false, "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceQueryWorkflowBody" - } } ], "tags": [ @@ -10610,15 +11432,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{execution.workflowId}/trigger-rule": { + "/namespaces/{namespace}/workflows/{execution.businessId}/channels/{notification.channel}/notify": { "post": { - "summary": "TriggerWorkflowRule allows to:\n * trigger existing rule for a specific workflow execution;\n * trigger rule for a specific workflow execution without creating a rule;\nThis is useful for one-off operations.", - "operationId": "TriggerWorkflowRule", + "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", + "operationId": "NotifyChannel3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1TriggerWorkflowRuleResponse" + "$ref": "#/definitions/v1NotifyChannelResponse" } }, "default": { @@ -10636,7 +11458,14 @@ "type": "string" }, { - "name": "execution.workflowId", + "name": "execution.businessId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "notification.channel", + "description": "The channel the writer notified. Listeners register on the same name.", "in": "path", "required": true, "type": "string" @@ -10646,7 +11475,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceTriggerWorkflowRuleBody" + "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" } } ], @@ -10655,16 +11484,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/cancel": { - "post": { - "summary": "RequestCancelWorkflowExecution is called by workers when they want to request cancellation of\na workflow execution.", - "description": "This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the\nworkflow history and a new workflow task created for the workflow. It returns success if the requested\nworkflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist.", - "operationId": "RequestCancelWorkflowExecution", + "/namespaces/{namespace}/workflows/{execution.workflowId}": { + "get": { + "summary": "DescribeWorkflowExecution returns information about the specified workflow execution.", + "operationId": "DescribeWorkflowExecution", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RequestCancelWorkflowExecutionResponse" + "$ref": "#/definitions/v1DescribeWorkflowExecutionResponse" } }, "default": { @@ -10682,18 +11510,16 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceRequestCancelWorkflowExecutionBody" - } + "name": "execution.runId", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -10701,15 +11527,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}": { + "/namespaces/{namespace}/workflows/{execution.workflowId}/history": { "get": { - "summary": "DescribeChannel returns the listeners of a channel and its latest\nnotification.", - "operationId": "DescribeChannel3", + "summary": "GetWorkflowExecutionHistory returns the history of specified workflow execution. Fails with\n`NotFound` if the specified workflow execution is unknown to the service.", + "operationId": "GetWorkflowExecutionHistory", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1DescribeChannelResponse" + "$ref": "#/definitions/v1GetWorkflowExecutionHistoryResponse" } }, "default": { @@ -10727,22 +11553,57 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "channel", - "in": "path", - "required": true, + "name": "execution.runId", + "in": "query", + "required": false, "type": "string" }, { - "name": "workflowExecution.runId", + "name": "maximumPageSize", "in": "query", "required": false, - "type": "string" + "type": "integer", + "format": "int32" + }, + { + "name": "nextPageToken", + "description": "If a `GetWorkflowExecutionHistoryResponse` or a `PollWorkflowTaskQueueResponse` had one of\nthese, it should be passed here to fetch the next page.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + }, + { + "name": "waitNewEvent", + "description": "If set to true, the RPC call will not resolve until there is a new event which matches\nthe `history_event_filter_type`, or a timeout is hit.", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "historyEventFilterType", + "description": "Filter returned events such that they match the specified filter type.\nDefault: HISTORY_EVENT_FILTER_TYPE_ALL_EVENT.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED", + "HISTORY_EVENT_FILTER_TYPE_ALL_EVENT", + "HISTORY_EVENT_FILTER_TYPE_CLOSE_EVENT" + ], + "default": "HISTORY_EVENT_FILTER_TYPE_UNSPECIFIED" + }, + { + "name": "skipArchival", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ @@ -10750,15 +11611,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/listeners": { - "post": { - "summary": "RegisterChannelListener registers a callback as a listener of a channel. A\nWorkflow registers itself with the `SubscribeNotificationChannel` command\ninstead.", - "operationId": "RegisterChannelListener3", + "/namespaces/{namespace}/workflows/{execution.workflowId}/history-reverse": { + "get": { + "summary": "GetWorkflowExecutionHistoryReverse returns the history of specified workflow execution in reverse\norder (starting from last event). Fails with`NotFound` if the specified workflow execution is\nunknown to the service.", + "operationId": "GetWorkflowExecutionHistoryReverse", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1RegisterChannelListenerResponse" + "$ref": "#/definitions/v1GetWorkflowExecutionHistoryReverseResponse" } }, "default": { @@ -10776,24 +11637,30 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "channel", - "in": "path", - "required": true, + "name": "execution.runId", + "in": "query", + "required": false, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/WorkflowServiceRegisterChannelListenerBody" - } + "name": "maximumPageSize", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "nextPageToken", + "in": "query", + "required": false, + "type": "string", + "format": "byte" } ], "tags": [ @@ -10801,15 +11668,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/listeners/{listenerId}": { - "delete": { - "summary": "UnregisterChannelListener removes a listener from a channel.", - "operationId": "UnregisterChannelListener3", + "/namespaces/{namespace}/workflows/{execution.workflowId}/query/{query.queryType}": { + "post": { + "summary": "QueryWorkflow requests a query be executed for a specified workflow execution.", + "operationId": "QueryWorkflow", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1UnregisterChannelListenerResponse" + "$ref": "#/definitions/v1QueryWorkflowResponse" } }, "default": { @@ -10827,35 +11694,25 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "channel", + "name": "query.queryType", + "description": "The workflow-author-defined identifier of the query. Typically a function name.", "in": "path", "required": true, "type": "string" }, { - "name": "listenerId", - "in": "path", + "name": "body", + "in": "body", "required": true, - "type": "string" - }, - { - "name": "identity", - "description": "The identity of the caller, for metrics and logs.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "workflowExecution.runId", - "in": "query", - "required": false, - "type": "string" + "schema": { + "$ref": "#/definitions/WorkflowServiceQueryWorkflowBody" + } } ], "tags": [ @@ -10863,15 +11720,15 @@ ] } }, - "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{channel}/notifications": { - "get": { - "summary": "PollChannel is a long poll for clients. It returns the retained\nnotifications of a channel with a counter above `after_counter`, waiting\nup to `wait` for one when none is retained yet.", - "operationId": "PollChannel3", + "/namespaces/{namespace}/workflows/{execution.workflowId}/trigger-rule": { + "post": { + "summary": "TriggerWorkflowRule allows to:\n * trigger existing rule for a specific workflow execution;\n * trigger rule for a specific workflow execution without creating a rule;\nThis is useful for one-off operations.", + "operationId": "TriggerWorkflowRule", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1PollChannelResponse" + "$ref": "#/definitions/v1TriggerWorkflowRuleResponse" } }, "default": { @@ -10889,45 +11746,18 @@ "type": "string" }, { - "name": "workflowExecution.workflowId", + "name": "execution.workflowId", "in": "path", "required": true, "type": "string" }, { - "name": "channel", - "in": "path", + "name": "body", + "in": "body", "required": true, - "type": "string" - }, - { - "name": "afterCounter", - "description": "Only notifications with a counter above this one are returned.", - "in": "query", - "required": false, - "type": "string", - "format": "int64" - }, - { - "name": "wait", - "description": "How long to wait for a notification when none is retained above\n`after_counter`.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "maxNotifications", - "description": "At most this many notifications are returned. Zero means the server's\ndefault.", - "in": "query", - "required": false, - "type": "integer", - "format": "int32" - }, - { - "name": "workflowExecution.runId", - "in": "query", - "required": false, - "type": "string" + "schema": { + "$ref": "#/definitions/WorkflowServiceTriggerWorkflowRuleBody" + } } ], "tags": [ @@ -10935,15 +11765,16 @@ ] } }, - "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/channels/{notification.channel}/notify": { + "/namespaces/{namespace}/workflows/{workflowExecution.workflowId}/cancel": { "post": { - "summary": "NotifyChannel tells every listener of a channel that a source they consume\nhas moved. The writer names no addressee and never learns who listens. The\nserver wakes each listener: a Workflow with a Workflow Task, a callback by\ninvoking it. Nothing goes to History except the notifications a woken\nWorkflow Task carries on its scheduled event.", - "operationId": "NotifyChannel3", + "summary": "RequestCancelWorkflowExecution is called by workers when they want to request cancellation of\na workflow execution.", + "description": "This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the\nworkflow history and a new workflow task created for the workflow. It returns success if the requested\nworkflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist.", + "operationId": "RequestCancelWorkflowExecution", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v1NotifyChannelResponse" + "$ref": "#/definitions/v1RequestCancelWorkflowExecutionResponse" } }, "default": { @@ -10966,19 +11797,12 @@ "required": true, "type": "string" }, - { - "name": "notification.channel", - "description": "The channel the writer notified. Listeners register on the same name.", - "in": "path", - "required": true, - "type": "string" - }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/WorkflowServiceNotifyChannelBody" + "$ref": "#/definitions/WorkflowServiceRequestCancelWorkflowExecutionBody" } } ], @@ -12850,31 +13674,34 @@ "additionalProperties": { "$ref": "#/definitions/v1Payload" }, - "description": "Details for the listener, such as which topic moved. Bounded in size and\ncarried as payloads, so a codec applies as to any payload." + "description": "Details for the listener, such as which topic moved. Bounded in size and\ncarried as payloads, so a codec applies as to any payload. This is state,\nnot a log: a fold keeps the latest notification only, so a writer puts\nhere what is true at `position`, such as which topic moved or a close\nflag, never something a consumer must see once per write." }, "linkedTo": { - "$ref": "#/definitions/v1WorkflowExecution", - "description": "Set for a channel linked to a workflow: the owner and the run that\nreceived the notification. Empty for an independent channel. A listener\nthat holds both kinds routes the notification by it." + "$ref": "#/definitions/v1Execution", + "description": "Set for a channel linked to an execution: the owner and the run that\nreceived the notification. Empty for an independent channel. A listener\nthat holds both kinds routes the notification by it." } }, "description": "A notification tells the listeners of a channel that a source they consume\nhas moved. It is not data: the listener reads the source itself. A channel\nis named by the writer and its listeners; for a stream, the provider formats\nthe stream's identity into the name. Writers never learn who listens. The\nserver folds notifications per listener while one is pending and no task has\nbeen scheduled for it, keeping the one with the highest counter." }, "identity": { "type": "string", - "description": "The identity of the writer, for metrics and logs." + "description": "The identity of the caller, for audit, metrics and logs. It is not copied\ninto the notification. A writer that wants the consumer to see who wrote\nputs that in the notification's `metadata`." }, "requestId": { "type": "string", "description": "Used to de-dupe a retried notification." }, - "workflowExecution": { + "execution": { "type": "object", "properties": { + "type": { + "$ref": "#/definitions/v1ExecutionType" + }, "runId": { "type": "string" } }, - "description": "When set, the call addresses the channel linked to this workflow; `run_id`\nis optional and resolves to the chain's current run, as a Signal does.\nWhen unset, the call addresses the independent channel of that name." + "description": "When set, the call addresses the channel linked to this execution.\n`run_id` is optional and resolves to the current run of a workflow chain,\nas a Signal does. When unset, the call addresses the independent channel\nof that name." } } }, @@ -13081,14 +13908,17 @@ "type": "string", "description": "The identity of the caller, for metrics and logs." }, - "workflowExecution": { + "execution": { "type": "object", "properties": { + "type": { + "$ref": "#/definitions/v1ExecutionType" + }, "runId": { "type": "string" } }, - "description": "When set, the call addresses the channel linked to this workflow; `run_id`\nis optional and resolves to the chain's current run, as a Signal does.\nWhen unset, the call addresses the independent channel of that name." + "description": "When set, the call addresses the channel linked to this execution.\n`run_id` is optional and resolves to the current run of a workflow chain,\nas a Signal does. When unset, the call addresses the independent channel\nof that name." } } }, @@ -16044,7 +16874,7 @@ "CHANNEL_KIND_LINKED" ], "default": "CHANNEL_KIND_UNSPECIFIED", - "description": "Where a channel lives, which decides how a call addresses it.\n\n - CHANNEL_KIND_INDEPENDENT: Its own execution, keyed by namespace and channel name. Any number of\nworkflows and callbacks listen to it.\n - CHANNEL_KIND_LINKED: Kept in one workflow's state, keyed by namespace, workflow id and\nchannel name. The owning workflow is its listener by construction." + "description": "Where a channel lives, which decides how a call addresses it.\n\n - CHANNEL_KIND_INDEPENDENT: Its own execution, keyed by namespace and channel name. Any number of\nworkflows and callbacks listen to it.\n - CHANNEL_KIND_LINKED: Kept in one execution's state, keyed by namespace, execution and\nchannel name. The owning execution is its listener by construction." }, "v1ChannelListener": { "type": "object", @@ -16966,7 +17796,7 @@ "$ref": "#/definitions/v1ChannelKind" }, "linkedTo": { - "$ref": "#/definitions/v1WorkflowExecution", + "$ref": "#/definitions/v1Execution", "description": "The owner of a linked channel and the run that holds it. Empty for an\nindependent channel." } } @@ -19432,11 +20262,11 @@ "additionalProperties": { "$ref": "#/definitions/v1Payload" }, - "description": "Details for the listener, such as which topic moved. Bounded in size and\ncarried as payloads, so a codec applies as to any payload." + "description": "Details for the listener, such as which topic moved. Bounded in size and\ncarried as payloads, so a codec applies as to any payload. This is state,\nnot a log: a fold keeps the latest notification only, so a writer puts\nhere what is true at `position`, such as which topic moved or a close\nflag, never something a consumer must see once per write." }, "linkedTo": { - "$ref": "#/definitions/v1WorkflowExecution", - "description": "Set for a channel linked to a workflow: the owner and the run that\nreceived the notification. Empty for an independent channel. A listener\nthat holds both kinds routes the notification by it." + "$ref": "#/definitions/v1Execution", + "description": "Set for a channel linked to an execution: the owner and the run that\nreceived the notification. Empty for an independent channel. A listener\nthat holds both kinds routes the notification by it." } }, "description": "A notification tells the listeners of a channel that a source they consume\nhas moved. It is not data: the listener reads the source itself. A channel\nis named by the writer and its listeners; for a stream, the provider formats\nthe stream's identity into the name. Writers never learn who listens. The\nserver folds notifications per listener while one is pending and no task has\nbeen scheduled for it, keeping the one with the highest counter." diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index 746d10ad9..d960c88e1 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -950,6 +950,299 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}: + get: + tags: + - WorkflowService + description: |- + DescribeChannel returns the listeners of a channel and its latest + notification. + operationId: DescribeChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DescribeChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/listeners: + post: + tags: + - WorkflowService + description: |- + RegisterChannelListener registers a callback as a listener of a channel. A + Workflow registers itself with the `SubscribeNotificationChannel` command + instead. + operationId: RegisterChannelListener + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterChannelListenerRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterChannelListenerResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/listeners/{listenerId}: + delete: + tags: + - WorkflowService + description: |- + UnregisterChannelListener removes a listener from a channel. + + (-- api-linter: core::0136::http-method=disabled + aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + operationId: UnregisterChannelListener + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: listenerId + in: path + required: true + schema: + type: string + - name: identity + in: query + description: The identity of the caller, for metrics and logs. + schema: + type: string + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UnregisterChannelListenerResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/notifications: + get: + tags: + - WorkflowService + description: |- + PollChannel is a long poll for clients. It returns the retained + notifications of a channel with a counter above `after_counter`, waiting + up to `wait` for one when none is retained yet. + operationId: PollChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: afterCounter + in: query + description: |- + Only notifications with a counter above this one are returned. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "after" names the exclusive lower bound. --) + schema: + type: string + - name: wait + in: query + description: |- + How long to wait for a notification when none is retained above + `after_counter`. + schema: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s + - name: maxNotifications + in: query + description: |- + At most this many notifications are returned. Zero means the server's + default. + schema: + type: integer + format: int32 + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PollChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{notification.channel}/notify: + post: + tags: + - WorkflowService + description: |- + NotifyChannel tells every listener of a channel that a source they consume + has moved. The writer names no addressee and never learns who listens. The + server wakes each listener: a Workflow with a Workflow Task, a callback by + invoking it. Nothing goes to History except the notifications a woken + Workflow Task carries on its scheduled event. + operationId: NotifyChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: notification.channel + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyChannelRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /api/v1/namespaces/{namespace}/activity-complete: post: tags: @@ -1336,11 +1629,21 @@ paths: required: true schema: type: string - - name: workflowExecution.workflowId + - name: execution.type in: query schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION type: string - - name: workflowExecution.runId + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId in: query schema: type: string @@ -1427,11 +1730,21 @@ paths: description: The identity of the caller, for metrics and logs. schema: type: string - - name: workflowExecution.workflowId + - name: execution.type in: query schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION type: string - - name: workflowExecution.runId + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId in: query schema: type: string @@ -1493,11 +1806,21 @@ paths: schema: type: integer format: int32 - - name: workflowExecution.workflowId + - name: execution.type in: query schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION type: string - - name: workflowExecution.runId + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId in: query schema: type: string @@ -3494,7 +3817,168 @@ paths: parameters: - name: namespace in: path - description: Namespace this worker belongs to. + description: Namespace this worker belongs to. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/FetchWorkerConfigRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/FetchWorkerConfigResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workers/heartbeat: + post: + tags: + - WorkflowService + description: WorkerHeartbeat receive heartbeat request from the worker. + operationId: RecordWorkerHeartbeat + parameters: + - name: namespace + in: path + description: Namespace this worker belongs to. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RecordWorkerHeartbeatRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RecordWorkerHeartbeatResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workers/update-config: + post: + tags: + - WorkflowService + description: |- + UpdateWorkerConfig updates the worker configuration of one or more workers. + Can be used to partially update the worker configuration. + Can be used to update the configuration of multiple workers. + operationId: UpdateWorkerConfig + parameters: + - name: namespace + in: path + description: Namespace this worker belongs to. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkerConfigRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkerConfigResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflow-count: + get: + tags: + - WorkflowService + description: CountWorkflowExecutions is a visibility API to count of workflow executions in a specific namespace. + operationId: CountWorkflowExecutions + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: query + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CountWorkflowExecutionsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflow-rules: + get: + tags: + - WorkflowService + description: Return all namespace workflow rules + operationId: ListWorkflowRules + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: nextPageToken + in: query + schema: + type: string + format: bytes + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListWorkflowRulesResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + post: + tags: + - WorkflowService + description: |- + Create a new workflow rule. The rules are used to control the workflow execution. + The rule will be applied to all running and new workflows in the namespace. + If the rule with such ID already exist this call will fail + Note: the rules are part of namespace configuration and will be stored in the namespace config. + Namespace config is eventually consistent. + operationId: CreateWorkflowRule + parameters: + - name: namespace + in: path required: true schema: type: string @@ -3502,7 +3986,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/FetchWorkerConfigRequest' + $ref: '#/components/schemas/CreateWorkflowRuleRequest' required: true responses: "200": @@ -3510,92 +3994,98 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/FetchWorkerConfigResponse' + $ref: '#/components/schemas/CreateWorkflowRuleResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workers/heartbeat: - post: + /api/v1/namespaces/{namespace}/workflow-rules/{ruleId}: + get: tags: - WorkflowService - description: WorkerHeartbeat receive heartbeat request from the worker. - operationId: RecordWorkerHeartbeat + description: |- + DescribeWorkflowRule return the rule specification for existing rule id. + If there is no rule with such id - NOT FOUND error will be returned. + operationId: DescribeWorkflowRule parameters: - name: namespace in: path - description: Namespace this worker belongs to. required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RecordWorkerHeartbeatRequest' - required: true + - name: ruleId + in: path + description: User-specified ID of the rule to read. Unique within the namespace. + required: true + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/RecordWorkerHeartbeatResponse' + $ref: '#/components/schemas/DescribeWorkflowRuleResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workers/update-config: - post: + delete: tags: - WorkflowService - description: |- - UpdateWorkerConfig updates the worker configuration of one or more workers. - Can be used to partially update the worker configuration. - Can be used to update the configuration of multiple workers. - operationId: UpdateWorkerConfig + description: Delete rule by rule id + operationId: DeleteWorkflowRule parameters: - name: namespace in: path - description: Namespace this worker belongs to. required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateWorkerConfigRequest' - required: true + - name: ruleId + in: path + description: ID of the rule to delete. Unique within the namespace. + required: true + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/UpdateWorkerConfigResponse' + $ref: '#/components/schemas/DeleteWorkflowRuleResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflow-count: + /api/v1/namespaces/{namespace}/workflows: get: tags: - WorkflowService - description: CountWorkflowExecutions is a visibility API to count of workflow executions in a specific namespace. - operationId: CountWorkflowExecutions + description: ListWorkflowExecutions is a visibility API to list workflow executions in a specific namespace. + operationId: ListWorkflowExecutions parameters: - name: namespace in: path required: true schema: type: string + - name: pageSize + in: query + schema: + type: integer + format: int32 + - name: nextPageToken + in: query + schema: + type: string + format: bytes - name: query in: query schema: @@ -3606,64 +4096,98 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CountWorkflowExecutionsResponse' + $ref: '#/components/schemas/ListWorkflowExecutionsResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflow-rules: + /api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}: get: tags: - WorkflowService - description: Return all namespace workflow rules - operationId: ListWorkflowRules + description: |- + DescribeChannel returns the listeners of a channel and its latest + notification. + operationId: DescribeChannel parameters: - name: namespace in: path required: true schema: type: string - - name: nextPageToken + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId in: query schema: type: string - format: bytes responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/ListWorkflowRulesResponse' + $ref: '#/components/schemas/DescribeChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/listeners: post: tags: - WorkflowService description: |- - Create a new workflow rule. The rules are used to control the workflow execution. - The rule will be applied to all running and new workflows in the namespace. - If the rule with such ID already exist this call will fail - Note: the rules are part of namespace configuration and will be stored in the namespace config. - Namespace config is eventually consistent. - operationId: CreateWorkflowRule + RegisterChannelListener registers a callback as a listener of a channel. A + Workflow registers itself with the `SubscribeNotificationChannel` command + instead. + operationId: RegisterChannelListener parameters: - name: namespace in: path required: true schema: type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/CreateWorkflowRuleRequest' + $ref: '#/components/schemas/RegisterChannelListenerRequest' required: true responses: "200": @@ -3671,109 +4195,201 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateWorkflowRuleResponse' + $ref: '#/components/schemas/RegisterChannelListenerResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflow-rules/{ruleId}: - get: + /api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/listeners/{listenerId}: + delete: tags: - WorkflowService description: |- - DescribeWorkflowRule return the rule specification for existing rule id. - If there is no rule with such id - NOT FOUND error will be returned. - operationId: DescribeWorkflowRule + UnregisterChannelListener removes a listener from a channel. + + (-- api-linter: core::0136::http-method=disabled + aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + operationId: UnregisterChannelListener parameters: - name: namespace in: path required: true schema: type: string - - name: ruleId + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: listenerId in: path - description: User-specified ID of the rule to read. Unique within the namespace. required: true schema: type: string + - name: identity + in: query + description: The identity of the caller, for metrics and logs. + schema: + type: string + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/DescribeWorkflowRuleResponse' + $ref: '#/components/schemas/UnregisterChannelListenerResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - delete: + /api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/notifications: + get: tags: - WorkflowService - description: Delete rule by rule id - operationId: DeleteWorkflowRule + description: |- + PollChannel is a long poll for clients. It returns the retained + notifications of a channel with a counter above `after_counter`, waiting + up to `wait` for one when none is retained yet. + operationId: PollChannel parameters: - name: namespace in: path required: true schema: type: string - - name: ruleId + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel in: path - description: ID of the rule to delete. Unique within the namespace. required: true schema: type: string + - name: afterCounter + in: query + description: |- + Only notifications with a counter above this one are returned. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "after" names the exclusive lower bound. --) + schema: + type: string + - name: wait + in: query + description: |- + How long to wait for a notification when none is retained above + `after_counter`. + schema: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s + - name: maxNotifications + in: query + description: |- + At most this many notifications are returned. Zero means the server's + default. + schema: + type: integer + format: int32 + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/DeleteWorkflowRuleResponse' + $ref: '#/components/schemas/PollChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows: - get: + /api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{notification.channel}/notify: + post: tags: - WorkflowService - description: ListWorkflowExecutions is a visibility API to list workflow executions in a specific namespace. - operationId: ListWorkflowExecutions + description: |- + NotifyChannel tells every listener of a channel that a source they consume + has moved. The writer names no addressee and never learns who listens. The + server wakes each listener: a Workflow with a Workflow Task, a callback by + invoking it. Nothing goes to History except the notifications a woken + Workflow Task carries on its scheduled event. + operationId: NotifyChannel parameters: - name: namespace in: path required: true schema: type: string - - name: pageSize - in: query - schema: - type: integer - format: int32 - - name: nextPageToken - in: query + - name: execution.business_id + in: path + required: true schema: type: string - format: bytes - - name: query - in: query + - name: notification.channel + in: path + required: true schema: type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyChannelRequest' + required: true responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/ListWorkflowExecutionsResponse' + $ref: '#/components/schemas/NotifyChannelResponse' default: description: Default error response content: @@ -4618,283 +5234,25 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnpauseWorkflowExecutionResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/cancel: - post: - tags: - - WorkflowService - description: |- - RequestCancelWorkflowExecution is called by workers when they want to request cancellation of - a workflow execution. - - This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the - workflow history and a new workflow task created for the workflow. It returns success if the requested - workflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist. - operationId: RequestCancelWorkflowExecution - parameters: - - name: namespace - in: path - required: true - schema: - type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RequestCancelWorkflowExecutionRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/RequestCancelWorkflowExecutionResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}: - get: - tags: - - WorkflowService - description: |- - DescribeChannel returns the listeners of a channel and its latest - notification. - operationId: DescribeChannel - parameters: - - name: namespace - in: path - required: true - schema: - type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - - name: channel - in: path - required: true - schema: - type: string - - name: workflowExecution.workflowId - in: query - schema: - type: string - - name: workflowExecution.runId - in: query - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/DescribeChannelResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners: - post: - tags: - - WorkflowService - description: |- - RegisterChannelListener registers a callback as a listener of a channel. A - Workflow registers itself with the `SubscribeNotificationChannel` command - instead. - operationId: RegisterChannelListener - parameters: - - name: namespace - in: path - required: true - schema: - type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - - name: channel - in: path - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RegisterChannelListenerRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/RegisterChannelListenerResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners/{listenerId}: - delete: - tags: - - WorkflowService - description: |- - UnregisterChannelListener removes a listener from a channel. - - (-- api-linter: core::0136::http-method=disabled - aip.dev/not-precedent: Removing a listener is a delete of that listener. --) - operationId: UnregisterChannelListener - parameters: - - name: namespace - in: path - required: true - schema: - type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - - name: channel - in: path - required: true - schema: - type: string - - name: listenerId - in: path - required: true - schema: - type: string - - name: identity - in: query - description: The identity of the caller, for metrics and logs. - schema: - type: string - - name: workflowExecution.workflowId - in: query - schema: - type: string - - name: workflowExecution.runId - in: query - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/UnregisterChannelListenerResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/notifications: - get: - tags: - - WorkflowService - description: |- - PollChannel is a long poll for clients. It returns the retained - notifications of a channel with a counter above `after_counter`, waiting - up to `wait` for one when none is retained yet. - operationId: PollChannel - parameters: - - name: namespace - in: path - required: true - schema: - type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - - name: channel - in: path - required: true - schema: - type: string - - name: afterCounter - in: query - description: |- - Only notifications with a counter above this one are returned. - (-- api-linter: core::0140::prepositions=disabled - aip.dev/not-precedent: "after" names the exclusive lower bound. --) - schema: - type: string - - name: wait - in: query - description: |- - How long to wait for a notification when none is retained above - `after_counter`. - schema: - pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ - type: string - description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s - - name: maxNotifications - in: query - description: |- - At most this many notifications are returned. Zero means the server's - default. - schema: - type: integer - format: int32 - - name: workflowExecution.workflowId - in: query - schema: - type: string - - name: workflowExecution.runId - in: query - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PollChannelResponse' + $ref: '#/components/schemas/UnpauseWorkflowExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{notification.channel}/notify: + /api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/cancel: post: tags: - WorkflowService description: |- - NotifyChannel tells every listener of a channel that a source they consume - has moved. The writer names no addressee and never learns who listens. The - server wakes each listener: a Workflow with a Workflow Task, a callback by - invoking it. Nothing goes to History except the notifications a woken - Workflow Task carries on its scheduled event. - operationId: NotifyChannel + RequestCancelWorkflowExecution is called by workers when they want to request cancellation of + a workflow execution. + + This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the + workflow history and a new workflow task created for the workflow. It returns success if the requested + workflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist. + operationId: RequestCancelWorkflowExecution parameters: - name: namespace in: path @@ -4906,16 +5264,11 @@ paths: required: true schema: type: string - - name: notification.channel - in: path - required: true - schema: - type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/NotifyChannelRequest' + $ref: '#/components/schemas/RequestCancelWorkflowExecutionRequest' required: true responses: "200": @@ -4923,7 +5276,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/NotifyChannelResponse' + $ref: '#/components/schemas/RequestCancelWorkflowExecutionResponse' default: description: Default error response content: @@ -6467,49 +6820,342 @@ paths: required: true schema: type: string - - name: activityId + - name: activityId + in: path + description: The ID of the activity to target. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UnpauseActivityExecutionRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UnpauseActivityExecutionResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/activities/{activityId}/update-options: + post: + tags: + - WorkflowService + description: |- + UpdateActivityExecutionOptions is called by the client to update the options of an activity by its ID. + This API can be used to target a workflow activity or a standalone activity. + operationId: UpdateActivityExecutionOptions + parameters: + - name: namespace + in: path + description: Namespace of the workflow which scheduled this activity + required: true + schema: + type: string + - name: activityId + in: path + description: The ID of the activity to target. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateActivityExecutionOptionsRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateActivityExecutionOptionsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}: + get: + tags: + - WorkflowService + description: |- + DescribeChannel returns the listeners of a channel and its latest + notification. + operationId: DescribeChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DescribeChannelResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/listeners: + post: + tags: + - WorkflowService + description: |- + RegisterChannelListener registers a callback as a listener of a channel. A + Workflow registers itself with the `SubscribeNotificationChannel` command + instead. + operationId: RegisterChannelListener + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterChannelListenerRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterChannelListenerResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/listeners/{listenerId}: + delete: + tags: + - WorkflowService + description: |- + UnregisterChannelListener removes a listener from a channel. + + (-- api-linter: core::0136::http-method=disabled + aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + operationId: UnregisterChannelListener + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: listenerId + in: path + required: true + schema: + type: string + - name: identity + in: query + description: The identity of the caller, for metrics and logs. + schema: + type: string + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UnregisterChannelListenerResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/notifications: + get: + tags: + - WorkflowService + description: |- + PollChannel is a long poll for clients. It returns the retained + notifications of a channel with a counter above `after_counter`, waiting + up to `wait` for one when none is retained yet. + operationId: PollChannel + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel in: path - description: The ID of the activity to target. required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UnpauseActivityExecutionRequest' - required: true + - name: afterCounter + in: query + description: |- + Only notifications with a counter above this one are returned. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "after" names the exclusive lower bound. --) + schema: + type: string + - name: wait + in: query + description: |- + How long to wait for a notification when none is retained above + `after_counter`. + schema: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s + - name: maxNotifications + in: query + description: |- + At most this many notifications are returned. Zero means the server's + default. + schema: + type: integer + format: int32 + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/UnpauseActivityExecutionResponse' + $ref: '#/components/schemas/PollChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/activities/{activityId}/update-options: + /namespaces/{namespace}/activities/{execution.business_id}/channels/{notification.channel}/notify: post: tags: - WorkflowService description: |- - UpdateActivityExecutionOptions is called by the client to update the options of an activity by its ID. - This API can be used to target a workflow activity or a standalone activity. - operationId: UpdateActivityExecutionOptions + NotifyChannel tells every listener of a channel that a source they consume + has moved. The writer names no addressee and never learns who listens. The + server wakes each listener: a Workflow with a Workflow Task, a callback by + invoking it. Nothing goes to History except the notifications a woken + Workflow Task carries on its scheduled event. + operationId: NotifyChannel parameters: - name: namespace in: path - description: Namespace of the workflow which scheduled this activity required: true schema: type: string - - name: activityId + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: notification.channel in: path - description: The ID of the activity to target. required: true schema: type: string @@ -6517,7 +7163,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateActivityExecutionOptionsRequest' + $ref: '#/components/schemas/NotifyChannelRequest' required: true responses: "200": @@ -6525,7 +7171,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateActivityExecutionOptionsResponse' + $ref: '#/components/schemas/NotifyChannelResponse' default: description: Default error response content: @@ -6918,11 +7564,21 @@ paths: required: true schema: type: string - - name: workflowExecution.workflowId + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId in: query schema: type: string - - name: workflowExecution.runId + - name: execution.runId in: query schema: type: string @@ -7009,11 +7665,21 @@ paths: description: The identity of the caller, for metrics and logs. schema: type: string - - name: workflowExecution.workflowId + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId in: query schema: type: string - - name: workflowExecution.runId + - name: execution.runId in: query schema: type: string @@ -7075,11 +7741,21 @@ paths: schema: type: integer format: int32 - - name: workflowExecution.workflowId + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId in: query schema: type: string - - name: workflowExecution.runId + - name: execution.runId in: query schema: type: string @@ -9018,7 +9694,168 @@ paths: parameters: - name: namespace in: path - description: Namespace this worker belongs to. + description: Namespace this worker belongs to. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/FetchWorkerConfigRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/FetchWorkerConfigResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workers/heartbeat: + post: + tags: + - WorkflowService + description: WorkerHeartbeat receive heartbeat request from the worker. + operationId: RecordWorkerHeartbeat + parameters: + - name: namespace + in: path + description: Namespace this worker belongs to. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RecordWorkerHeartbeatRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RecordWorkerHeartbeatResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workers/update-config: + post: + tags: + - WorkflowService + description: |- + UpdateWorkerConfig updates the worker configuration of one or more workers. + Can be used to partially update the worker configuration. + Can be used to update the configuration of multiple workers. + operationId: UpdateWorkerConfig + parameters: + - name: namespace + in: path + description: Namespace this worker belongs to. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkerConfigRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkerConfigResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflow-count: + get: + tags: + - WorkflowService + description: CountWorkflowExecutions is a visibility API to count of workflow executions in a specific namespace. + operationId: CountWorkflowExecutions + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: query + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CountWorkflowExecutionsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflow-rules: + get: + tags: + - WorkflowService + description: Return all namespace workflow rules + operationId: ListWorkflowRules + parameters: + - name: namespace + in: path + required: true + schema: + type: string + - name: nextPageToken + in: query + schema: + type: string + format: bytes + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListWorkflowRulesResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + post: + tags: + - WorkflowService + description: |- + Create a new workflow rule. The rules are used to control the workflow execution. + The rule will be applied to all running and new workflows in the namespace. + If the rule with such ID already exist this call will fail + Note: the rules are part of namespace configuration and will be stored in the namespace config. + Namespace config is eventually consistent. + operationId: CreateWorkflowRule + parameters: + - name: namespace + in: path required: true schema: type: string @@ -9026,7 +9863,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/FetchWorkerConfigRequest' + $ref: '#/components/schemas/CreateWorkflowRuleRequest' required: true responses: "200": @@ -9034,92 +9871,98 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/FetchWorkerConfigResponse' + $ref: '#/components/schemas/CreateWorkflowRuleResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workers/heartbeat: - post: + /namespaces/{namespace}/workflow-rules/{ruleId}: + get: tags: - WorkflowService - description: WorkerHeartbeat receive heartbeat request from the worker. - operationId: RecordWorkerHeartbeat + description: |- + DescribeWorkflowRule return the rule specification for existing rule id. + If there is no rule with such id - NOT FOUND error will be returned. + operationId: DescribeWorkflowRule parameters: - name: namespace in: path - description: Namespace this worker belongs to. required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RecordWorkerHeartbeatRequest' - required: true + - name: ruleId + in: path + description: User-specified ID of the rule to read. Unique within the namespace. + required: true + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/RecordWorkerHeartbeatResponse' + $ref: '#/components/schemas/DescribeWorkflowRuleResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workers/update-config: - post: + delete: tags: - WorkflowService - description: |- - UpdateWorkerConfig updates the worker configuration of one or more workers. - Can be used to partially update the worker configuration. - Can be used to update the configuration of multiple workers. - operationId: UpdateWorkerConfig + description: Delete rule by rule id + operationId: DeleteWorkflowRule parameters: - name: namespace in: path - description: Namespace this worker belongs to. required: true schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateWorkerConfigRequest' - required: true + - name: ruleId + in: path + description: ID of the rule to delete. Unique within the namespace. + required: true + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/UpdateWorkerConfigResponse' + $ref: '#/components/schemas/DeleteWorkflowRuleResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflow-count: + /namespaces/{namespace}/workflows: get: tags: - WorkflowService - description: CountWorkflowExecutions is a visibility API to count of workflow executions in a specific namespace. - operationId: CountWorkflowExecutions + description: ListWorkflowExecutions is a visibility API to list workflow executions in a specific namespace. + operationId: ListWorkflowExecutions parameters: - name: namespace in: path required: true schema: type: string + - name: pageSize + in: query + schema: + type: integer + format: int32 + - name: nextPageToken + in: query + schema: + type: string + format: bytes - name: query in: query schema: @@ -9130,64 +9973,98 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CountWorkflowExecutionsResponse' + $ref: '#/components/schemas/ListWorkflowExecutionsResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflow-rules: + /namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}: get: tags: - WorkflowService - description: Return all namespace workflow rules - operationId: ListWorkflowRules + description: |- + DescribeChannel returns the listeners of a channel and its latest + notification. + operationId: DescribeChannel parameters: - name: namespace in: path required: true schema: type: string - - name: nextPageToken + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId in: query schema: type: string - format: bytes responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/ListWorkflowRulesResponse' + $ref: '#/components/schemas/DescribeChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' + /namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/listeners: post: tags: - WorkflowService description: |- - Create a new workflow rule. The rules are used to control the workflow execution. - The rule will be applied to all running and new workflows in the namespace. - If the rule with such ID already exist this call will fail - Note: the rules are part of namespace configuration and will be stored in the namespace config. - Namespace config is eventually consistent. - operationId: CreateWorkflowRule + RegisterChannelListener registers a callback as a listener of a channel. A + Workflow registers itself with the `SubscribeNotificationChannel` command + instead. + operationId: RegisterChannelListener parameters: - name: namespace in: path required: true schema: type: string + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/CreateWorkflowRuleRequest' + $ref: '#/components/schemas/RegisterChannelListenerRequest' required: true responses: "200": @@ -9195,109 +10072,201 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateWorkflowRuleResponse' + $ref: '#/components/schemas/RegisterChannelListenerResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflow-rules/{ruleId}: - get: + /namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/listeners/{listenerId}: + delete: tags: - WorkflowService description: |- - DescribeWorkflowRule return the rule specification for existing rule id. - If there is no rule with such id - NOT FOUND error will be returned. - operationId: DescribeWorkflowRule + UnregisterChannelListener removes a listener from a channel. + + (-- api-linter: core::0136::http-method=disabled + aip.dev/not-precedent: Removing a listener is a delete of that listener. --) + operationId: UnregisterChannelListener parameters: - name: namespace in: path required: true schema: type: string - - name: ruleId + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel + in: path + required: true + schema: + type: string + - name: listenerId in: path - description: User-specified ID of the rule to read. Unique within the namespace. required: true schema: type: string + - name: identity + in: query + description: The identity of the caller, for metrics and logs. + schema: + type: string + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/DescribeWorkflowRuleResponse' + $ref: '#/components/schemas/UnregisterChannelListenerResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - delete: + /namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/notifications: + get: tags: - WorkflowService - description: Delete rule by rule id - operationId: DeleteWorkflowRule + description: |- + PollChannel is a long poll for clients. It returns the retained + notifications of a channel with a counter above `after_counter`, waiting + up to `wait` for one when none is retained yet. + operationId: PollChannel parameters: - name: namespace in: path required: true schema: type: string - - name: ruleId + - name: execution.business_id + in: path + required: true + schema: + type: string + - name: channel in: path - description: ID of the rule to delete. Unique within the namespace. required: true schema: type: string + - name: afterCounter + in: query + description: |- + Only notifications with a counter above this one are returned. + (-- api-linter: core::0140::prepositions=disabled + aip.dev/not-precedent: "after" names the exclusive lower bound. --) + schema: + type: string + - name: wait + in: query + description: |- + How long to wait for a notification when none is retained above + `after_counter`. + schema: + pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ + type: string + description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s + - name: maxNotifications + in: query + description: |- + At most this many notifications are returned. Zero means the server's + default. + schema: + type: integer + format: int32 + - name: execution.type + in: query + schema: + enum: + - EXECUTION_TYPE_UNSPECIFIED + - EXECUTION_TYPE_WORKFLOW + - EXECUTION_TYPE_ACTIVITY + - EXECUTION_TYPE_NEXUS_OPERATION + type: string + format: enum + - name: execution.businessId + in: query + schema: + type: string + - name: execution.runId + in: query + schema: + type: string responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/DeleteWorkflowRuleResponse' + $ref: '#/components/schemas/PollChannelResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows: - get: + /namespaces/{namespace}/workflows/{execution.business_id}/channels/{notification.channel}/notify: + post: tags: - WorkflowService - description: ListWorkflowExecutions is a visibility API to list workflow executions in a specific namespace. - operationId: ListWorkflowExecutions + description: |- + NotifyChannel tells every listener of a channel that a source they consume + has moved. The writer names no addressee and never learns who listens. The + server wakes each listener: a Workflow with a Workflow Task, a callback by + invoking it. Nothing goes to History except the notifications a woken + Workflow Task carries on its scheduled event. + operationId: NotifyChannel parameters: - name: namespace in: path required: true schema: type: string - - name: pageSize - in: query - schema: - type: integer - format: int32 - - name: nextPageToken - in: query + - name: execution.business_id + in: path + required: true schema: type: string - format: bytes - - name: query - in: query + - name: notification.channel + in: path + required: true schema: type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyChannelRequest' + required: true responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/ListWorkflowExecutionsResponse' + $ref: '#/components/schemas/NotifyChannelResponse' default: description: Default error response content: @@ -9862,271 +10831,26 @@ paths: description: |- See `RespondActivityTaskCanceled`. This version allows clients to record failures by namespace/workflow id/activity id instead of task token. - - (-- api-linter: core::0136::prepositions=disabled - aip.dev/not-precedent: "By" is used to indicate request type. --) - operationId: RespondActivityTaskCanceledById - parameters: - - name: namespace - in: path - description: Namespace of the workflow which scheduled this activity - required: true - schema: - type: string - - name: workflowId - in: path - description: Id of the workflow which scheduled this activity, leave empty to target a standalone activity - required: true - schema: - type: string - - name: activityId - in: path - description: Id of the activity to confirm is cancelled - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RespondActivityTaskCanceledByIdRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/RespondActivityTaskCanceledByIdResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/unpause: - post: - tags: - - WorkflowService - description: |- - UnpauseActivityExecution unpauses the execution of an activity specified by its ID. - This API can be used to target a workflow activity or a standalone activity. - - If activity is not paused, this call will have no effect. - If the activity was paused while waiting for retry, it will be scheduled immediately (* see 'jitter' flag). - Once the activity is unpaused, all timeout timers will be regenerated. - - Returns a `NotFound` error if there is no pending activity with the provided ID - operationId: UnpauseActivityExecution - parameters: - - name: namespace - in: path - description: Namespace of the workflow which scheduled this activity. - required: true - schema: - type: string - - name: workflowId - in: path - description: |- - If provided, targets a workflow activity for the given workflow ID. - If empty, targets a standalone activity. - required: true - schema: - type: string - - name: activityId - in: path - description: The ID of the activity to target. - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UnpauseActivityExecutionRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/UnpauseActivityExecutionResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/update-options: - post: - tags: - - WorkflowService - description: |- - UpdateActivityExecutionOptions is called by the client to update the options of an activity by its ID. - This API can be used to target a workflow activity or a standalone activity. - operationId: UpdateActivityExecutionOptions - parameters: - - name: namespace - in: path - description: Namespace of the workflow which scheduled this activity - required: true - schema: - type: string - - name: workflowId - in: path - description: |- - If provided, targets a workflow activity for the given workflow ID. - If empty, targets a standalone activity. - required: true - schema: - type: string - - name: activityId - in: path - description: The ID of the activity to target. - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateActivityExecutionOptionsRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateActivityExecutionOptionsResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/pause: - post: - tags: - - WorkflowService - description: |- - Note: This is an experimental API and the behavior may change in a future release. - PauseWorkflowExecution pauses the workflow execution specified in the request. Pausing a workflow execution results in - - The workflow execution status changes to `PAUSED` and a new WORKFLOW_EXECUTION_PAUSED event is added to the history - - No new workflow tasks or activity tasks are dispatched. - - Any workflow task currently executing on the worker will be allowed to complete. - - Any activity task currently executing will be paused. - - All server-side events will continue to be processed by the server. - - Queries & Updates on a paused workflow will be rejected. - operationId: PauseWorkflowExecution - parameters: - - name: namespace - in: path - description: Namespace of the workflow to pause. - required: true - schema: - type: string - - name: workflowId - in: path - description: ID of the workflow execution to be paused. Required. - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PauseWorkflowExecutionRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PauseWorkflowExecutionResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/signal-with-start/{signalName}: - post: - tags: - - WorkflowService - description: |- - SignalWithStartWorkflowExecution is used to ensure a signal is sent to a workflow, even if - it isn't yet started. - - If the workflow is running, a `WORKFLOW_EXECUTION_SIGNALED` event is recorded in the history - and a workflow task is generated. - - If the workflow is not running or not found, then the workflow is created with - `WORKFLOW_EXECUTION_STARTED` and `WORKFLOW_EXECUTION_SIGNALED` events in its history, and a - workflow task is generated. - - (-- api-linter: core::0136::prepositions=disabled - aip.dev/not-precedent: "With" is used to indicate combined operation. --) - operationId: SignalWithStartWorkflowExecution - parameters: - - name: namespace - in: path - required: true - schema: - type: string - - name: workflowId - in: path - required: true - schema: - type: string - - name: signalName - in: path - description: The workflow author-defined name of the signal to send to the workflow - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SignalWithStartWorkflowExecutionRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/SignalWithStartWorkflowExecutionResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflowId}/unpause: - post: - tags: - - WorkflowService - description: |- - Note: This is an experimental API and the behavior may change in a future release. - UnpauseWorkflowExecution unpauses a previously paused workflow execution specified in the request. - Unpausing a workflow execution results in - - The workflow execution status changes to `RUNNING` and a new WORKFLOW_EXECUTION_UNPAUSED event is added to the history - - Workflow tasks and activity tasks are resumed. - operationId: UnpauseWorkflowExecution + + (-- api-linter: core::0136::prepositions=disabled + aip.dev/not-precedent: "By" is used to indicate request type. --) + operationId: RespondActivityTaskCanceledById parameters: - name: namespace in: path - description: Namespace of the workflow to unpause. + description: Namespace of the workflow which scheduled this activity required: true schema: type: string - name: workflowId in: path - description: ID of the workflow execution to be paused. Required. + description: Id of the workflow which scheduled this activity, leave empty to target a standalone activity + required: true + schema: + type: string + - name: activityId + in: path + description: Id of the activity to confirm is cancelled required: true schema: type: string @@ -10134,7 +10858,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnpauseWorkflowExecutionRequest' + $ref: '#/components/schemas/RespondActivityTaskCanceledByIdRequest' required: true responses: "200": @@ -10142,33 +10866,45 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UnpauseWorkflowExecutionResponse' + $ref: '#/components/schemas/RespondActivityTaskCanceledByIdResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/cancel: + /namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/unpause: post: tags: - WorkflowService description: |- - RequestCancelWorkflowExecution is called by workers when they want to request cancellation of - a workflow execution. + UnpauseActivityExecution unpauses the execution of an activity specified by its ID. + This API can be used to target a workflow activity or a standalone activity. - This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the - workflow history and a new workflow task created for the workflow. It returns success if the requested - workflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist. - operationId: RequestCancelWorkflowExecution + If activity is not paused, this call will have no effect. + If the activity was paused while waiting for retry, it will be scheduled immediately (* see 'jitter' flag). + Once the activity is unpaused, all timeout timers will be regenerated. + + Returns a `NotFound` error if there is no pending activity with the provided ID + operationId: UnpauseActivityExecution parameters: - name: namespace in: path + description: Namespace of the workflow which scheduled this activity. required: true schema: type: string - - name: workflow_execution.workflow_id + - name: workflowId + in: path + description: |- + If provided, targets a workflow activity for the given workflow ID. + If empty, targets a standalone activity. + required: true + schema: + type: string + - name: activityId in: path + description: The ID of the activity to target. required: true schema: type: string @@ -10176,7 +10912,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RequestCancelWorkflowExecutionRequest' + $ref: '#/components/schemas/UnpauseActivityExecutionRequest' required: true responses: "200": @@ -10184,80 +10920,85 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RequestCancelWorkflowExecutionResponse' + $ref: '#/components/schemas/UnpauseActivityExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}: - get: + /namespaces/{namespace}/workflows/{workflowId}/activities/{activityId}/update-options: + post: tags: - WorkflowService description: |- - DescribeChannel returns the listeners of a channel and its latest - notification. - operationId: DescribeChannel + UpdateActivityExecutionOptions is called by the client to update the options of an activity by its ID. + This API can be used to target a workflow activity or a standalone activity. + operationId: UpdateActivityExecutionOptions parameters: - name: namespace in: path + description: Namespace of the workflow which scheduled this activity required: true schema: type: string - - name: workflow_execution.workflow_id + - name: workflowId in: path + description: |- + If provided, targets a workflow activity for the given workflow ID. + If empty, targets a standalone activity. required: true schema: type: string - - name: channel + - name: activityId in: path + description: The ID of the activity to target. required: true schema: type: string - - name: workflowExecution.workflowId - in: query - schema: - type: string - - name: workflowExecution.runId - in: query - schema: - type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateActivityExecutionOptionsRequest' + required: true responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/DescribeChannelResponse' + $ref: '#/components/schemas/UpdateActivityExecutionOptionsResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners: + /namespaces/{namespace}/workflows/{workflowId}/pause: post: tags: - WorkflowService description: |- - RegisterChannelListener registers a callback as a listener of a channel. A - Workflow registers itself with the `SubscribeNotificationChannel` command - instead. - operationId: RegisterChannelListener + Note: This is an experimental API and the behavior may change in a future release. + PauseWorkflowExecution pauses the workflow execution specified in the request. Pausing a workflow execution results in + - The workflow execution status changes to `PAUSED` and a new WORKFLOW_EXECUTION_PAUSED event is added to the history + - No new workflow tasks or activity tasks are dispatched. + - Any workflow task currently executing on the worker will be allowed to complete. + - Any activity task currently executing will be paused. + - All server-side events will continue to be processed by the server. + - Queries & Updates on a paused workflow will be rejected. + operationId: PauseWorkflowExecution parameters: - name: namespace in: path + description: Namespace of the workflow to pause. required: true schema: type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - - name: channel + - name: workflowId in: path + description: ID of the workflow execution to be paused. Required. required: true schema: type: string @@ -10265,7 +11006,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterChannelListenerRequest' + $ref: '#/components/schemas/PauseWorkflowExecutionRequest' required: true responses: "200": @@ -10273,152 +11014,122 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterChannelListenerResponse' + $ref: '#/components/schemas/PauseWorkflowExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners/{listenerId}: - delete: + /namespaces/{namespace}/workflows/{workflowId}/signal-with-start/{signalName}: + post: tags: - WorkflowService description: |- - UnregisterChannelListener removes a listener from a channel. + SignalWithStartWorkflowExecution is used to ensure a signal is sent to a workflow, even if + it isn't yet started. - (-- api-linter: core::0136::http-method=disabled - aip.dev/not-precedent: Removing a listener is a delete of that listener. --) - operationId: UnregisterChannelListener + If the workflow is running, a `WORKFLOW_EXECUTION_SIGNALED` event is recorded in the history + and a workflow task is generated. + + If the workflow is not running or not found, then the workflow is created with + `WORKFLOW_EXECUTION_STARTED` and `WORKFLOW_EXECUTION_SIGNALED` events in its history, and a + workflow task is generated. + + (-- api-linter: core::0136::prepositions=disabled + aip.dev/not-precedent: "With" is used to indicate combined operation. --) + operationId: SignalWithStartWorkflowExecution parameters: - name: namespace in: path required: true schema: type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - - name: channel + - name: workflowId in: path required: true schema: type: string - - name: listenerId + - name: signalName in: path + description: The workflow author-defined name of the signal to send to the workflow required: true schema: type: string - - name: identity - in: query - description: The identity of the caller, for metrics and logs. - schema: - type: string - - name: workflowExecution.workflowId - in: query - schema: - type: string - - name: workflowExecution.runId - in: query - schema: - type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SignalWithStartWorkflowExecutionRequest' + required: true responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/UnregisterChannelListenerResponse' + $ref: '#/components/schemas/SignalWithStartWorkflowExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/notifications: - get: + /namespaces/{namespace}/workflows/{workflowId}/unpause: + post: tags: - WorkflowService description: |- - PollChannel is a long poll for clients. It returns the retained - notifications of a channel with a counter above `after_counter`, waiting - up to `wait` for one when none is retained yet. - operationId: PollChannel + Note: This is an experimental API and the behavior may change in a future release. + UnpauseWorkflowExecution unpauses a previously paused workflow execution specified in the request. + Unpausing a workflow execution results in + - The workflow execution status changes to `RUNNING` and a new WORKFLOW_EXECUTION_UNPAUSED event is added to the history + - Workflow tasks and activity tasks are resumed. + operationId: UnpauseWorkflowExecution parameters: - name: namespace in: path + description: Namespace of the workflow to unpause. required: true schema: type: string - - name: workflow_execution.workflow_id - in: path - required: true - schema: - type: string - - name: channel + - name: workflowId in: path + description: ID of the workflow execution to be paused. Required. required: true schema: type: string - - name: afterCounter - in: query - description: |- - Only notifications with a counter above this one are returned. - (-- api-linter: core::0140::prepositions=disabled - aip.dev/not-precedent: "after" names the exclusive lower bound. --) - schema: - type: string - - name: wait - in: query - description: |- - How long to wait for a notification when none is retained above - `after_counter`. - schema: - pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ - type: string - description: Represents a a duration between -315,576,000,000s and 315,576,000,000s (around 10000 years). Precision is in nanoseconds. 1 nanosecond is represented as 0.000000001s - - name: maxNotifications - in: query - description: |- - At most this many notifications are returned. Zero means the server's - default. - schema: - type: integer - format: int32 - - name: workflowExecution.workflowId - in: query - schema: - type: string - - name: workflowExecution.runId - in: query - schema: - type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UnpauseWorkflowExecutionRequest' + required: true responses: "200": description: OK content: application/json: schema: - $ref: '#/components/schemas/PollChannelResponse' + $ref: '#/components/schemas/UnpauseWorkflowExecutionResponse' default: description: Default error response content: application/json: schema: $ref: '#/components/schemas/Status' - /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{notification.channel}/notify: + /namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/cancel: post: tags: - WorkflowService description: |- - NotifyChannel tells every listener of a channel that a source they consume - has moved. The writer names no addressee and never learns who listens. The - server wakes each listener: a Workflow with a Workflow Task, a callback by - invoking it. Nothing goes to History except the notifications a woken - Workflow Task carries on its scheduled event. - operationId: NotifyChannel + RequestCancelWorkflowExecution is called by workers when they want to request cancellation of + a workflow execution. + + This results in a new `WORKFLOW_EXECUTION_CANCEL_REQUESTED` event being written to the + workflow history and a new workflow task created for the workflow. It returns success if the requested + workflow is already closed. It fails with 'NotFound' if the requested workflow doesn't exist. + operationId: RequestCancelWorkflowExecution parameters: - name: namespace in: path @@ -10430,16 +11141,11 @@ paths: required: true schema: type: string - - name: notification.channel - in: path - required: true - schema: - type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/NotifyChannelRequest' + $ref: '#/components/schemas/RequestCancelWorkflowExecutionRequest' required: true responses: "200": @@ -10447,7 +11153,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/NotifyChannelResponse' + $ref: '#/components/schemas/RequestCancelWorkflowExecutionResponse' default: description: Default error response content: @@ -12955,7 +13661,7 @@ components: format: enum linkedTo: allOf: - - $ref: '#/components/schemas/WorkflowExecution' + - $ref: '#/components/schemas/Execution' description: |- The owner of a linked channel and the run that holds it. Empty for an independent channel. @@ -15377,12 +16083,15 @@ components: $ref: '#/components/schemas/Payload' description: |- Details for the listener, such as which topic moved. Bounded in size and - carried as payloads, so a codec applies as to any payload. + carried as payloads, so a codec applies as to any payload. This is state, + not a log: a fold keeps the latest notification only, so a writer puts + here what is true at `position`, such as which topic moved or a close + flag, never something a consumer must see once per write. linkedTo: allOf: - - $ref: '#/components/schemas/WorkflowExecution' + - $ref: '#/components/schemas/Execution' description: |- - Set for a channel linked to a workflow: the owner and the run that + Set for a channel linked to an execution: the owner and the run that received the notification. Empty for an independent channel. A listener that holds both kinds routes the notification by it. (-- api-linter: core::0140::prepositions=disabled @@ -15403,17 +16112,21 @@ components: $ref: '#/components/schemas/Notification' identity: type: string - description: The identity of the writer, for metrics and logs. + description: |- + The identity of the caller, for audit, metrics and logs. It is not copied + into the notification. A writer that wants the consumer to see who wrote + puts that in the notification's `metadata`. requestId: type: string description: Used to de-dupe a retried notification. - workflowExecution: + execution: allOf: - - $ref: '#/components/schemas/WorkflowExecution' + - $ref: '#/components/schemas/Execution' description: |- - When set, the call addresses the channel linked to this workflow; `run_id` - is optional and resolves to the chain's current run, as a Signal does. - When unset, the call addresses the independent channel of that name. + When set, the call addresses the channel linked to this execution. + `run_id` is optional and resolves to the current run of a workflow chain, + as a Signal does. When unset, the call addresses the independent channel + of that name. NotifyChannelResponse: type: object properties: @@ -16463,13 +17176,14 @@ components: identity: type: string description: The identity of the caller, for metrics and logs. - workflowExecution: + execution: allOf: - - $ref: '#/components/schemas/WorkflowExecution' + - $ref: '#/components/schemas/Execution' description: |- - When set, the call addresses the channel linked to this workflow; `run_id` - is optional and resolves to the chain's current run, as a Signal does. - When unset, the call addresses the independent channel of that name. + When set, the call addresses the channel linked to this execution. + `run_id` is optional and resolves to the current run of a workflow chain, + as a Signal does. When unset, the call addresses the independent channel + of that name. RegisterChannelListenerResponse: type: object properties: diff --git a/temporal/api/notification/v1/message.proto b/temporal/api/notification/v1/message.proto index e1413efe1..85519f697 100644 --- a/temporal/api/notification/v1/message.proto +++ b/temporal/api/notification/v1/message.proto @@ -30,14 +30,21 @@ message Notification { // notifications folded together, the one with the highest counter is kept. int64 counter = 3; // Details for the listener, such as which topic moved. Bounded in size and - // carried as payloads, so a codec applies as to any payload. + // carried as payloads, so a codec applies as to any payload. This is state, + // not a log: a fold keeps the latest notification only, so a writer puts + // here what is true at `position`, such as which topic moved or a close + // flag, never something a consumer must see once per write. map metadata = 4; - // Set for a channel linked to a workflow: the owner and the run that + + // Was temporal.api.common.v1.WorkflowExecution linked_to. + reserved 5; + + // Set for a channel linked to an execution: the owner and the run that // received the notification. Empty for an independent channel. A listener // that holds both kinds routes the notification by it. // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" names the owner the channel is linked to. --) - temporal.api.common.v1.WorkflowExecution linked_to = 5; + temporal.api.common.v1.Execution linked_to = 6; } // A listener of a channel: a Workflow Execution woken with a Workflow Task, or @@ -66,7 +73,7 @@ enum ChannelKind { // Its own execution, keyed by namespace and channel name. Any number of // workflows and callbacks listen to it. CHANNEL_KIND_INDEPENDENT = 1; - // Kept in one workflow's state, keyed by namespace, workflow id and - // channel name. The owning workflow is its listener by construction. + // Kept in one execution's state, keyed by namespace, execution and + // channel name. The owning execution is its listener by construction. CHANNEL_KIND_LINKED = 2; } diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index acb38e053..5f934bad9 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -892,14 +892,22 @@ message SignalWorkflowExecutionResponse { message NotifyChannelRequest { string namespace = 1; temporal.api.notification.v1.Notification notification = 2; - // The identity of the writer, for metrics and logs. + // The identity of the caller, for audit, metrics and logs. It is not copied + // into the notification. A writer that wants the consumer to see who wrote + // puts that in the notification's `metadata`. string identity = 3; // Used to de-dupe a retried notification. string request_id = 4; - // When set, the call addresses the channel linked to this workflow; `run_id` - // is optional and resolves to the chain's current run, as a Signal does. - // When unset, the call addresses the independent channel of that name. - temporal.api.common.v1.WorkflowExecution workflow_execution = 5; + + // Was temporal.api.common.v1.WorkflowExecution workflow_execution. + reserved 5; + reserved "workflow_execution"; + + // When set, the call addresses the channel linked to this execution. + // `run_id` is optional and resolves to the current run of a workflow chain, + // as a Signal does. When unset, the call addresses the independent channel + // of that name. + temporal.api.common.v1.Execution execution = 6; } message NotifyChannelResponse { @@ -917,10 +925,16 @@ message RegisterChannelListenerRequest { string request_id = 4; // The identity of the caller, for metrics and logs. string identity = 5; - // When set, the call addresses the channel linked to this workflow; `run_id` - // is optional and resolves to the chain's current run, as a Signal does. - // When unset, the call addresses the independent channel of that name. - temporal.api.common.v1.WorkflowExecution workflow_execution = 6; + + // Was temporal.api.common.v1.WorkflowExecution workflow_execution. + reserved 6; + reserved "workflow_execution"; + + // When set, the call addresses the channel linked to this execution. + // `run_id` is optional and resolves to the current run of a workflow chain, + // as a Signal does. When unset, the call addresses the independent channel + // of that name. + temporal.api.common.v1.Execution execution = 7; } message RegisterChannelListenerResponse { @@ -933,10 +947,16 @@ message UnregisterChannelListenerRequest { string listener_id = 3; // The identity of the caller, for metrics and logs. string identity = 4; - // When set, the call addresses the channel linked to this workflow; `run_id` - // is optional and resolves to the chain's current run, as a Signal does. - // When unset, the call addresses the independent channel of that name. - temporal.api.common.v1.WorkflowExecution workflow_execution = 5; + + // Was temporal.api.common.v1.WorkflowExecution workflow_execution. + reserved 5; + reserved "workflow_execution"; + + // When set, the call addresses the channel linked to this execution. + // `run_id` is optional and resolves to the current run of a workflow chain, + // as a Signal does. When unset, the call addresses the independent channel + // of that name. + temporal.api.common.v1.Execution execution = 6; } message UnregisterChannelListenerResponse { @@ -955,10 +975,16 @@ message PollChannelRequest { // At most this many notifications are returned. Zero means the server's // default. int32 max_notifications = 5; - // When set, the call addresses the channel linked to this workflow; `run_id` - // is optional and resolves to the chain's current run, as a Signal does. - // When unset, the call addresses the independent channel of that name. - temporal.api.common.v1.WorkflowExecution workflow_execution = 6; + + // Was temporal.api.common.v1.WorkflowExecution workflow_execution. + reserved 6; + reserved "workflow_execution"; + + // When set, the call addresses the channel linked to this execution. + // `run_id` is optional and resolves to the current run of a workflow chain, + // as a Signal does. When unset, the call addresses the independent channel + // of that name. + temporal.api.common.v1.Execution execution = 7; } message PollChannelResponse { @@ -968,10 +994,16 @@ message PollChannelResponse { message DescribeChannelRequest { string namespace = 1; string channel = 2; - // When set, the call addresses the channel linked to this workflow; `run_id` - // is optional and resolves to the chain's current run, as a Signal does. - // When unset, the call addresses the independent channel of that name. - temporal.api.common.v1.WorkflowExecution workflow_execution = 3; + + // Was temporal.api.common.v1.WorkflowExecution workflow_execution. + reserved 3; + reserved "workflow_execution"; + + // When set, the call addresses the channel linked to this execution. + // `run_id` is optional and resolves to the current run of a workflow chain, + // as a Signal does. When unset, the call addresses the independent channel + // of that name. + temporal.api.common.v1.Execution execution = 4; } message DescribeChannelResponse { @@ -981,11 +1013,15 @@ message DescribeChannelResponse { // How many notifications the channel retains for pollers. int32 retained_count = 3; temporal.api.notification.v1.ChannelKind kind = 4; + + // Was temporal.api.common.v1.WorkflowExecution linked_to. + reserved 5; + // The owner of a linked channel and the run that holds it. Empty for an // independent channel. // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" names the owner the channel is linked to. --) - temporal.api.common.v1.WorkflowExecution linked_to = 5; + temporal.api.common.v1.Execution linked_to = 6; } message SignalWithStartWorkflowExecutionRequest { diff --git a/temporal/api/workflowservice/v1/service.proto b/temporal/api/workflowservice/v1/service.proto index 9dbf0317f..b2ec8a5e9 100644 --- a/temporal/api/workflowservice/v1/service.proto +++ b/temporal/api/workflowservice/v1/service.proto @@ -497,11 +497,19 @@ service WorkflowService { body: "*" } additional_bindings { - post: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{notification.channel}/notify" + post: "/namespaces/{namespace}/workflows/{execution.business_id}/channels/{notification.channel}/notify" body: "*" } additional_bindings { - post: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{notification.channel}/notify" + post: "/api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{notification.channel}/notify" + body: "*" + } + additional_bindings { + post: "/namespaces/{namespace}/activities/{execution.business_id}/channels/{notification.channel}/notify" + body: "*" + } + additional_bindings { + post: "/api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{notification.channel}/notify" body: "*" } }; @@ -520,11 +528,19 @@ service WorkflowService { body: "*" } additional_bindings { - post: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners" + post: "/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/listeners" + body: "*" + } + additional_bindings { + post: "/api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/listeners" + body: "*" + } + additional_bindings { + post: "/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/listeners" body: "*" } additional_bindings { - post: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners" + post: "/api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/listeners" body: "*" } }; @@ -542,10 +558,16 @@ service WorkflowService { delete: "/api/v1/namespaces/{namespace}/channels/{channel}/listeners/{listener_id}" } additional_bindings { - delete: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners/{listener_id}" + delete: "/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/listeners/{listener_id}" } additional_bindings { - delete: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/listeners/{listener_id}" + delete: "/api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/listeners/{listener_id}" + } + additional_bindings { + delete: "/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/listeners/{listener_id}" + } + additional_bindings { + delete: "/api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/listeners/{listener_id}" } }; } @@ -560,10 +582,16 @@ service WorkflowService { get: "/api/v1/namespaces/{namespace}/channels/{channel}/notifications" } additional_bindings { - get: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/notifications" + get: "/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/notifications" + } + additional_bindings { + get: "/api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}/notifications" } additional_bindings { - get: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}/notifications" + get: "/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/notifications" + } + additional_bindings { + get: "/api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}/notifications" } }; } @@ -577,10 +605,16 @@ service WorkflowService { get: "/api/v1/namespaces/{namespace}/channels/{channel}" } additional_bindings { - get: "/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}" + get: "/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}" + } + additional_bindings { + get: "/api/v1/namespaces/{namespace}/workflows/{execution.business_id}/channels/{channel}" + } + additional_bindings { + get: "/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}" } additional_bindings { - get: "/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/channels/{channel}" + get: "/api/v1/namespaces/{namespace}/activities/{execution.business_id}/channels/{channel}" } }; } From 071feb8ad14429adcb0c97a8f741147a3b326d2d Mon Sep 17 00:00:00 2001 From: Mohammad Dashti Date: Fri, 2 Oct 2026 19:15:34 -0700 Subject: [PATCH 30/30] Reused the old numbers for the execution fields. The earlier shape was never released, so the numbers are free and nothing needs reserving. --- temporal/api/notification/v1/message.proto | 6 +-- .../workflowservice/v1/request_response.proto | 41 +++---------------- 2 files changed, 7 insertions(+), 40 deletions(-) diff --git a/temporal/api/notification/v1/message.proto b/temporal/api/notification/v1/message.proto index 85519f697..584619582 100644 --- a/temporal/api/notification/v1/message.proto +++ b/temporal/api/notification/v1/message.proto @@ -35,16 +35,12 @@ message Notification { // here what is true at `position`, such as which topic moved or a close // flag, never something a consumer must see once per write. map metadata = 4; - - // Was temporal.api.common.v1.WorkflowExecution linked_to. - reserved 5; - // Set for a channel linked to an execution: the owner and the run that // received the notification. Empty for an independent channel. A listener // that holds both kinds routes the notification by it. // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" names the owner the channel is linked to. --) - temporal.api.common.v1.Execution linked_to = 6; + temporal.api.common.v1.Execution linked_to = 5; } // A listener of a channel: a Workflow Execution woken with a Workflow Task, or diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 5f934bad9..d69732a71 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -898,16 +898,11 @@ message NotifyChannelRequest { string identity = 3; // Used to de-dupe a retried notification. string request_id = 4; - - // Was temporal.api.common.v1.WorkflowExecution workflow_execution. - reserved 5; - reserved "workflow_execution"; - // When set, the call addresses the channel linked to this execution. // `run_id` is optional and resolves to the current run of a workflow chain, // as a Signal does. When unset, the call addresses the independent channel // of that name. - temporal.api.common.v1.Execution execution = 6; + temporal.api.common.v1.Execution execution = 5; } message NotifyChannelResponse { @@ -925,16 +920,11 @@ message RegisterChannelListenerRequest { string request_id = 4; // The identity of the caller, for metrics and logs. string identity = 5; - - // Was temporal.api.common.v1.WorkflowExecution workflow_execution. - reserved 6; - reserved "workflow_execution"; - // When set, the call addresses the channel linked to this execution. // `run_id` is optional and resolves to the current run of a workflow chain, // as a Signal does. When unset, the call addresses the independent channel // of that name. - temporal.api.common.v1.Execution execution = 7; + temporal.api.common.v1.Execution execution = 6; } message RegisterChannelListenerResponse { @@ -947,16 +937,11 @@ message UnregisterChannelListenerRequest { string listener_id = 3; // The identity of the caller, for metrics and logs. string identity = 4; - - // Was temporal.api.common.v1.WorkflowExecution workflow_execution. - reserved 5; - reserved "workflow_execution"; - // When set, the call addresses the channel linked to this execution. // `run_id` is optional and resolves to the current run of a workflow chain, // as a Signal does. When unset, the call addresses the independent channel // of that name. - temporal.api.common.v1.Execution execution = 6; + temporal.api.common.v1.Execution execution = 5; } message UnregisterChannelListenerResponse { @@ -975,16 +960,11 @@ message PollChannelRequest { // At most this many notifications are returned. Zero means the server's // default. int32 max_notifications = 5; - - // Was temporal.api.common.v1.WorkflowExecution workflow_execution. - reserved 6; - reserved "workflow_execution"; - // When set, the call addresses the channel linked to this execution. // `run_id` is optional and resolves to the current run of a workflow chain, // as a Signal does. When unset, the call addresses the independent channel // of that name. - temporal.api.common.v1.Execution execution = 7; + temporal.api.common.v1.Execution execution = 6; } message PollChannelResponse { @@ -994,16 +974,11 @@ message PollChannelResponse { message DescribeChannelRequest { string namespace = 1; string channel = 2; - - // Was temporal.api.common.v1.WorkflowExecution workflow_execution. - reserved 3; - reserved "workflow_execution"; - // When set, the call addresses the channel linked to this execution. // `run_id` is optional and resolves to the current run of a workflow chain, // as a Signal does. When unset, the call addresses the independent channel // of that name. - temporal.api.common.v1.Execution execution = 4; + temporal.api.common.v1.Execution execution = 3; } message DescribeChannelResponse { @@ -1013,15 +988,11 @@ message DescribeChannelResponse { // How many notifications the channel retains for pollers. int32 retained_count = 3; temporal.api.notification.v1.ChannelKind kind = 4; - - // Was temporal.api.common.v1.WorkflowExecution linked_to. - reserved 5; - // The owner of a linked channel and the run that holds it. Empty for an // independent channel. // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" names the owner the channel is linked to. --) - temporal.api.common.v1.Execution linked_to = 6; + temporal.api.common.v1.Execution linked_to = 5; } message SignalWithStartWorkflowExecutionRequest {