From b23db7baec921c96185e5b3452dce55229cd2b49 Mon Sep 17 00:00:00 2001 From: Shahab Date: Wed, 14 Oct 2026 15:00:39 -0700 Subject: [PATCH 1/7] Add activity session_options --- temporal/api/command/v1/message.proto | 3 +++ temporal/api/session/v1/message.proto | 18 ++++++++++++++++++ .../workflowservice/v1/request_response.proto | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 temporal/api/session/v1/message.proto diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index ee839115b..b8be64315 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -16,6 +16,7 @@ import "temporal/api/enums/v1/command_type.proto"; import "temporal/api/common/v1/message.proto"; import "temporal/api/failure/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; +import "temporal/api/session/v1/message.proto"; import "temporal/api/workflow/v1/message.proto"; import "temporal/api/sdk/v1/user_metadata.proto"; import "temporal/api/sdk/v1/event_group_marker.proto"; @@ -68,6 +69,8 @@ message ScheduleActivityTaskCommandAttributes { // Priority metadata. If this message is not present, or any fields are not // present, they inherit the values from the workflow. temporal.api.common.v1.Priority priority = 14; + + temporal.api.session.v1.ActivitySessionOptions session_options = 15; } message RequestCancelActivityTaskCommandAttributes { diff --git a/temporal/api/session/v1/message.proto b/temporal/api/session/v1/message.proto new file mode 100644 index 000000000..e1e6d567e --- /dev/null +++ b/temporal/api/session/v1/message.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package temporal.api.session.v1; + +option go_package = "go.temporal.io/api/session/v1;session"; +option java_package = "io.temporal.api.session.v1"; +option java_multiple_files = true; +option java_outer_classname = "MessageProto"; +option ruby_package = "Temporalio::Api::Session::V1"; +option csharp_namespace = "Temporalio.Api.Session.V1"; + +message ActivitySessionOptions { + oneof session { + bool workflow_sticky_session = 1; + + // later session_id, etc + } +} diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index f4b4cdb7f..5a1c61257 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -323,6 +323,15 @@ message PollWorkflowTaskQueueRequest { // Removed in 1.55.0; was temporal.api.worker.v1.WorkerHeartbeat worker_heartbeat reserved 7; reserved "worker_heartbeat"; + + // EXPERIMENTAL. Subject to removal in the future. + // This flag is only applicable to sticky polls. In this mode, server will stop forwarding the sticky polls to the + // normal queue once the set number of tasks are dispatched from the normal queue to pollers of this particular + // sticky queue. + // The counter is persisted in server as part of sticky task queue metadata, so server crushes will not reset the + // counter. + // (-- api-linter: core::0140::prepositions=disabled + int32 max_sticky_to_normal_forwards = 11; } message PollWorkflowTaskQueueResponse { @@ -553,6 +562,15 @@ message PollActivityTaskQueueRequest { // Removed in 1.55.0; was temporal.api.worker.v1.WorkerHeartbeat worker_heartbeat reserved 7; reserved "worker_heartbeat"; + + // EXPERIMENTAL. Subject to removal in the future. + // This flag is only applicable to sticky polls. In this mode, server will stop forwarding the sticky polls to the + // normal queue once the set number of tasks are dispatched from the normal queue to pollers of this particular + // sticky queue. + // The counter is persisted in server as part of sticky task queue metadata, so server crushes will not reset the + // counter. + // (-- api-linter: core::0140::prepositions=disabled + int32 max_sticky_to_normal_forwards = 11; } message PollActivityTaskQueueResponse { From c28c289a9dbcaba8297e5a61108c451b7d20777c Mon Sep 17 00:00:00 2001 From: Shahab Date: Thu, 15 Oct 2026 13:33:55 -0700 Subject: [PATCH 2/7] `single_workflow_mode` poll flag --- .../workflowservice/v1/request_response.proto | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 5a1c61257..892c1f3cf 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -325,13 +325,15 @@ message PollWorkflowTaskQueueRequest { reserved "worker_heartbeat"; // EXPERIMENTAL. Subject to removal in the future. - // This flag is only applicable to sticky polls. In this mode, server will stop forwarding the sticky polls to the - // normal queue once the set number of tasks are dispatched from the normal queue to pollers of this particular - // sticky queue. - // The counter is persisted in server as part of sticky task queue metadata, so server crushes will not reset the - // counter. + // This flag is only applicable to sticky polls. In this mode, server will forward the workflow task sticky polls + // to the normal queue only once. The forwarding to normal queue will stop once a single task is dispatched from the + // normal queue to pollers of this particular sticky queue. + // Server persists the workflow ID of the first matched task in the sticky task queue's metadata. This will allow + // system to enforce single-workflow mode even when the server crashes, or a worker with stable identity restarts + // (when worker's sticky queue name remains the same after restart.) + // For activity sticky polls, this mode will disable forwarding to normal queue completely. // (-- api-linter: core::0140::prepositions=disabled - int32 max_sticky_to_normal_forwards = 11; + bool single_workflow_mode = 11; } message PollWorkflowTaskQueueResponse { @@ -564,13 +566,15 @@ message PollActivityTaskQueueRequest { reserved "worker_heartbeat"; // EXPERIMENTAL. Subject to removal in the future. - // This flag is only applicable to sticky polls. In this mode, server will stop forwarding the sticky polls to the - // normal queue once the set number of tasks are dispatched from the normal queue to pollers of this particular - // sticky queue. - // The counter is persisted in server as part of sticky task queue metadata, so server crushes will not reset the - // counter. + // This flag is only applicable to sticky polls. In this mode, server will forward the workflow task sticky polls + // to the normal queue only once. The forwarding to normal queue will stop once a single task is dispatched from the + // normal queue to pollers of this particular sticky queue. + // Server persists the workflow ID of the first matched task in the sticky task queue's metadata. This will allow + // system to enforce single-workflow mode even when the server crashes, or a worker with stable identity restarts + // (when worker's sticky queue name remains the same after restart.) + // For activity sticky polls, this mode will disable forwarding to normal queue completely. // (-- api-linter: core::0140::prepositions=disabled - int32 max_sticky_to_normal_forwards = 11; + bool single_workflow_mode = 11; } message PollActivityTaskQueueResponse { From 7132c51e4fc1856dd17dfc2115b06649d184b214 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 19 Oct 2026 15:07:25 -0400 Subject: [PATCH 3/7] Model single-session isolation in poll session options --- temporal/api/session/v1/message.proto | 22 +++++++++++++ .../workflowservice/v1/request_response.proto | 33 ++++++++----------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/temporal/api/session/v1/message.proto b/temporal/api/session/v1/message.proto index e1e6d567e..f9d174ca0 100644 --- a/temporal/api/session/v1/message.proto +++ b/temporal/api/session/v1/message.proto @@ -9,6 +9,28 @@ option java_outer_classname = "MessageProto"; option ruby_package = "Temporalio::Api::Session::V1"; option csharp_namespace = "Temporalio.Api.Session.V1"; +// EXPERIMENTAL. Subject to redesign or removal. +// Configures which sessions may share a worker, not a security boundary. +message SessionOptions { + oneof isolation { + SingleSessionOptions single_session = 1; + } +} + +// EXPERIMENTAL. Subject to redesign or removal. +// Dedicates a sticky destination to the first workflow matched to it. +// Workflow sticky polls may be forwarded to the normal queue until the first +// workflow task is dispatched. The server persists that workflow ID and does +// not forward subsequent polls, including after workflow completion. +// Activity sticky polls are never forwarded to the normal queue. +message SingleSessionOptions { + // Stable sticky task queue name, shared by workflow and activity polls. + // Reuse this value across worker restarts within the same session, but not + // across concurrently running workers. This is distinct from worker identity. + // The SDK must also set the polled sticky task queue's name to this value. + string stable_identity = 1; +} + message ActivitySessionOptions { oneof session { bool workflow_sticky_session = 1; diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 892c1f3cf..067f4fe6d 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -38,6 +38,7 @@ import "temporal/api/replication/v1/message.proto"; import "temporal/api/rules/v1/message.proto"; import "temporal/api/sdk/v1/worker_config.proto"; import "temporal/api/schedule/v1/message.proto"; +import "temporal/api/session/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; import "temporal/api/update/v1/message.proto"; import "temporal/api/version/v1/message.proto"; @@ -324,16 +325,12 @@ message PollWorkflowTaskQueueRequest { reserved 7; reserved "worker_heartbeat"; - // EXPERIMENTAL. Subject to removal in the future. - // This flag is only applicable to sticky polls. In this mode, server will forward the workflow task sticky polls - // to the normal queue only once. The forwarding to normal queue will stop once a single task is dispatched from the - // normal queue to pollers of this particular sticky queue. - // Server persists the workflow ID of the first matched task in the sticky task queue's metadata. This will allow - // system to enforce single-workflow mode even when the server crashes, or a worker with stable identity restarts - // (when worker's sticky queue name remains the same after restart.) - // For activity sticky polls, this mode will disable forwarding to normal queue completely. - // (-- api-linter: core::0140::prepositions=disabled - bool single_workflow_mode = 11; + // Replaced experimental single_workflow_mode with session_options. + reserved 11; + reserved "single_workflow_mode"; + + // EXPERIMENTAL. Subject to redesign or removal. Only applicable to sticky polls. + temporal.api.session.v1.SessionOptions session_options = 12; } message PollWorkflowTaskQueueResponse { @@ -565,16 +562,12 @@ message PollActivityTaskQueueRequest { reserved 7; reserved "worker_heartbeat"; - // EXPERIMENTAL. Subject to removal in the future. - // This flag is only applicable to sticky polls. In this mode, server will forward the workflow task sticky polls - // to the normal queue only once. The forwarding to normal queue will stop once a single task is dispatched from the - // normal queue to pollers of this particular sticky queue. - // Server persists the workflow ID of the first matched task in the sticky task queue's metadata. This will allow - // system to enforce single-workflow mode even when the server crashes, or a worker with stable identity restarts - // (when worker's sticky queue name remains the same after restart.) - // For activity sticky polls, this mode will disable forwarding to normal queue completely. - // (-- api-linter: core::0140::prepositions=disabled - bool single_workflow_mode = 11; + // Replaced experimental single_workflow_mode with session_options. + reserved 11; + reserved "single_workflow_mode"; + + // EXPERIMENTAL. Subject to redesign or removal. Only applicable to sticky polls. + temporal.api.session.v1.SessionOptions session_options = 12; } message PollActivityTaskQueueResponse { From be0506da1e5bc9b610d4220e725358824316bc1d Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 19 Oct 2026 15:12:18 -0400 Subject: [PATCH 4/7] Clarify session affinity documentation and remove dev reservations --- temporal/api/session/v1/message.proto | 4 +++- temporal/api/workflowservice/v1/request_response.proto | 8 -------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/temporal/api/session/v1/message.proto b/temporal/api/session/v1/message.proto index f9d174ca0..67eff7d20 100644 --- a/temporal/api/session/v1/message.proto +++ b/temporal/api/session/v1/message.proto @@ -10,7 +10,9 @@ option ruby_package = "Temporalio::Api::Session::V1"; option csharp_namespace = "Temporalio.Api.Session.V1"; // EXPERIMENTAL. Subject to redesign or removal. -// Configures which sessions may share a worker, not a security boundary. +// Configures a worker's polling for sessions that keep workflow and activity +// tasks on a stable worker destination. Activities opt into session routing +// through ActivitySessionOptions on their scheduling commands. message SessionOptions { oneof isolation { SingleSessionOptions single_session = 1; diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 067f4fe6d..09b5ffd9a 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -325,10 +325,6 @@ message PollWorkflowTaskQueueRequest { reserved 7; reserved "worker_heartbeat"; - // Replaced experimental single_workflow_mode with session_options. - reserved 11; - reserved "single_workflow_mode"; - // EXPERIMENTAL. Subject to redesign or removal. Only applicable to sticky polls. temporal.api.session.v1.SessionOptions session_options = 12; } @@ -562,10 +558,6 @@ message PollActivityTaskQueueRequest { reserved 7; reserved "worker_heartbeat"; - // Replaced experimental single_workflow_mode with session_options. - reserved 11; - reserved "single_workflow_mode"; - // EXPERIMENTAL. Subject to redesign or removal. Only applicable to sticky polls. temporal.api.session.v1.SessionOptions session_options = 12; } From d59dfca4732e25f120ec26bf43ef13835d95fb29 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 19 Oct 2026 15:16:47 -0400 Subject: [PATCH 5/7] Name worker session options and use next available poll tags --- temporal/api/session/v1/message.proto | 2 +- temporal/api/workflowservice/v1/request_response.proto | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/temporal/api/session/v1/message.proto b/temporal/api/session/v1/message.proto index 67eff7d20..78593574d 100644 --- a/temporal/api/session/v1/message.proto +++ b/temporal/api/session/v1/message.proto @@ -13,7 +13,7 @@ option csharp_namespace = "Temporalio.Api.Session.V1"; // Configures a worker's polling for sessions that keep workflow and activity // tasks on a stable worker destination. Activities opt into session routing // through ActivitySessionOptions on their scheduling commands. -message SessionOptions { +message WorkerSessionOptions { oneof isolation { SingleSessionOptions single_session = 1; } diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 09b5ffd9a..d216339cd 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -326,7 +326,7 @@ message PollWorkflowTaskQueueRequest { reserved "worker_heartbeat"; // EXPERIMENTAL. Subject to redesign or removal. Only applicable to sticky polls. - temporal.api.session.v1.SessionOptions session_options = 12; + temporal.api.session.v1.WorkerSessionOptions session_options = 11; } message PollWorkflowTaskQueueResponse { @@ -559,7 +559,7 @@ message PollActivityTaskQueueRequest { reserved "worker_heartbeat"; // EXPERIMENTAL. Subject to redesign or removal. Only applicable to sticky polls. - temporal.api.session.v1.SessionOptions session_options = 12; + temporal.api.session.v1.WorkerSessionOptions session_options = 11; } message PollActivityTaskQueueResponse { From 01c117a719e5df9c1fad2936fd7ee1a75ff82ff1 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 19 Oct 2026 15:22:07 -0400 Subject: [PATCH 6/7] Rename activity routing intent to use_workflow_session --- temporal/api/session/v1/message.proto | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/temporal/api/session/v1/message.proto b/temporal/api/session/v1/message.proto index 78593574d..4a7cdb2e5 100644 --- a/temporal/api/session/v1/message.proto +++ b/temporal/api/session/v1/message.proto @@ -35,7 +35,9 @@ message SingleSessionOptions { message ActivitySessionOptions { oneof session { - bool workflow_sticky_session = 1; + // Execute this activity as part of its workflow's session. + // Currently routes to the workflow's sticky task queue when true. + bool use_workflow_session = 1; // later session_id, etc } From 6811161a3c59c7773ac236dbf9adf9eb359f9288 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Tue, 20 Oct 2026 08:41:55 -0400 Subject: [PATCH 7/7] Record activity session options in scheduled history events --- temporal/api/history/v1/message.proto | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index 1c6efd305..8f5a233bf 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -20,6 +20,7 @@ import "temporal/api/common/v1/message.proto"; import "temporal/api/deployment/v1/message.proto"; import "temporal/api/failure/v1/message.proto"; import "temporal/api/nexus/v1/message.proto"; +import "temporal/api/session/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; import "temporal/api/update/v1/message.proto"; import "temporal/api/workflow/v1/message.proto"; @@ -465,6 +466,9 @@ message ActivityTaskScheduledEventAttributes { // Priority metadata. If this message is not present, or any fields are not // present, they inherit the values from the workflow. temporal.api.common.v1.Priority priority = 14; + // Preserves the scheduling command's session routing options so activity state + // can be reconstructed from history. + temporal.api.session.v1.ActivitySessionOptions session_options = 15; } message ActivityTaskStartedEventAttributes {