From 33bf1784e13d645e7e55c901e5b3150978bb47ef Mon Sep 17 00:00:00 2001 From: Veeral Patel Date: Mon, 2 Nov 2026 18:35:44 -0800 Subject: [PATCH 1/6] Pass unset poller behavior to core so workers can auto-enroll in poller autoscaling When the user sets neither a poller behavior nor a max poll count, pass nil through the bridge instead of a SimpleMaximum(5) default. sdk-core then enrolls the poller into autoscaling when the namespace advertises the poller_autoscaling_auto_enroll capability, and falls back to SimpleMaximum(5) otherwise. Explicitly configured behaviors are passed through unchanged. --- temporalio/ext/src/worker.rs | 22 ++++++------ temporalio/lib/temporalio/worker.rb | 36 +++++++++++-------- .../sig/temporalio/internal/bridge/worker.rbs | 8 ++--- temporalio/sig/temporalio/worker.rbs | 24 ++++++------- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/temporalio/ext/src/worker.rs b/temporalio/ext/src/worker.rs index b9caac74..d30a0340 100644 --- a/temporalio/ext/src/worker.rs +++ b/temporalio/ext/src/worker.rs @@ -511,21 +511,23 @@ fn build_config(options: Struct, runtime_handle: &RuntimeHandle) -> Result>(id!("identity_override"))?) .max_cached_workflows(options.member::(id!("max_cached_workflows"))?) - .workflow_task_poller_behavior({ - let poller_behavior = options + // None lets core auto-enroll the poller into autoscaling when the namespace supports it + .maybe_workflow_task_poller_behavior( + options .child(id!("workflow_task_poller_behavior"))? - .ok_or_else(|| error!("Worker options must have workflow_task_poller_behavior"))?; - extract_poller_behavior(poller_behavior)? - }) + .map(extract_poller_behavior) + .transpose()?, + ) .nonsticky_to_sticky_poll_ratio( options.member::(id!("nonsticky_to_sticky_poll_ratio"))?, ) - .activity_task_poller_behavior({ - let poller_behavior = options + // None lets core auto-enroll the poller into autoscaling when the namespace supports it + .maybe_activity_task_poller_behavior( + options .child(id!("activity_task_poller_behavior"))? - .ok_or_else(|| error!("Worker options must have activity_task_poller_behavior"))?; - extract_poller_behavior(poller_behavior)? - }) + .map(extract_poller_behavior) + .transpose()?, + ) .task_types(WorkerTaskTypes { enable_workflows: options.member(id!("enable_workflows"))?, enable_local_activities: options.member(id!("enable_local_activities"))?, diff --git a/temporalio/lib/temporalio/worker.rb b/temporalio/lib/temporalio/worker.rb index 3e35f653..8ec100d1 100644 --- a/temporalio/lib/temporalio/worker.rb +++ b/temporalio/lib/temporalio/worker.rb @@ -395,15 +395,15 @@ def self._validate_plugins!(plugins) # @param logger [Logger] Logger to override client logger with. Default is the client logger. # @param max_cached_workflows [Integer] Number of workflows held in cache for use by sticky task queue. If set to 0, # workflow caching and sticky queuing are disabled. - # @param max_concurrent_workflow_task_polls [Integer] Maximum number of concurrent poll workflow task requests we - # will perform at a time on this worker's task queue. + # @param max_concurrent_workflow_task_polls [Integer, nil] Maximum number of concurrent poll workflow task requests + # we will perform at a time on this worker's task queue. # @param nonsticky_to_sticky_poll_ratio [Float] `max_concurrent_workflow_task_polls` * this number = the number of # max pollers that will be allowed for the nonsticky queue when sticky tasks are enabled. If both defaults are # used, the sticky queue will allow 4 max pollers while the nonsticky queue will allow one. The minimum for either # poller is 1, so if `max_concurrent_workflow_task_polls` is 1 and sticky queues are enabled, there will be 2 # concurrent polls. - # @param max_concurrent_activity_task_polls [Integer] Maximum number of concurrent poll activity task requests we - # will perform at a time on this worker's task queue. + # @param max_concurrent_activity_task_polls [Integer, nil] Maximum number of concurrent poll activity task requests + # we will perform at a time on this worker's task queue. # @param no_remote_activities [Boolean] If true, this worker will only handle workflow tasks and local activities, # it will not poll for activity tasks. # @param sticky_queue_schedule_to_start_timeout [Float] How long a workflow task is allowed to sit on the sticky @@ -450,10 +450,10 @@ def self._validate_plugins!(plugins) # @param patch_activation_callback [Proc, nil] Experimental callback to decide whether the first non-replay call to # {Workflow.patched} for a patch ID should activate that patch. The callback receives a {PatchActivationInput} and # must return `true` to activate the patch or `false` to leave it inactive. - # @param workflow_task_poller_behavior [PollerBehavior] Specify the behavior of workflow task - # polling. Defaults to a 5-poller maximum. - # @param activity_task_poller_behavior [PollerBehavior] Specify the behavior of activity task - # polling. Defaults to a 5-poller maximum. + # @param workflow_task_poller_behavior [PollerBehavior, nil] Specify the behavior of workflow task + # polling. Defaults to a 5-poller maximum, or autoscaling if the namespace auto-enrolls workers. + # @param activity_task_poller_behavior [PollerBehavior, nil] Specify the behavior of activity task + # polling. Defaults to a 5-poller maximum, or autoscaling if the namespace auto-enrolls workers. # @param debug_mode [Boolean] If true, deadlock detection is disabled. Deadlock detection will fail workflow tasks # if they block the thread for too long. This defaults to true if the `TEMPORAL_DEBUG` environment variable is # `true` or `1`. @@ -473,9 +473,9 @@ def initialize( identity: nil, logger: client.options.logger, max_cached_workflows: 1000, - max_concurrent_workflow_task_polls: 5, + max_concurrent_workflow_task_polls: nil, nonsticky_to_sticky_poll_ratio: 0.2, - max_concurrent_activity_task_polls: 5, + max_concurrent_activity_task_polls: nil, no_remote_activities: false, sticky_queue_schedule_to_start_timeout: 10, max_heartbeat_throttle_interval: 60, @@ -491,13 +491,21 @@ def initialize( unsafe_workflow_io_enabled: false, deployment_options: Worker.default_deployment_options, patch_activation_callback: nil, - workflow_task_poller_behavior: PollerBehavior::SimpleMaximum.new(max_concurrent_workflow_task_polls), - activity_task_poller_behavior: PollerBehavior::SimpleMaximum.new(max_concurrent_activity_task_polls), + workflow_task_poller_behavior: nil, + activity_task_poller_behavior: nil, debug_mode: %w[true 1].include?(ENV['TEMPORAL_DEBUG'].to_s.downcase), disable_payload_error_limit: false ) Internal::ProtoUtils.assert_non_reserved_name(task_queue) + # Left nil when neither is set, so core can auto-enroll the poller into autoscaling + if max_concurrent_workflow_task_polls + workflow_task_poller_behavior ||= PollerBehavior::SimpleMaximum.new(max_concurrent_workflow_task_polls) + end + if max_concurrent_activity_task_polls + activity_task_poller_behavior ||= PollerBehavior::SimpleMaximum.new(max_concurrent_activity_task_polls) + end + @options = Options.new( client:, task_queue:, @@ -582,9 +590,9 @@ def _initialize_from_options tuner: @options.tuner._to_bridge_options, identity_override: @options.identity, max_cached_workflows: @options.max_cached_workflows, - workflow_task_poller_behavior: @options.workflow_task_poller_behavior._to_bridge_options, + workflow_task_poller_behavior: @options.workflow_task_poller_behavior&._to_bridge_options, nonsticky_to_sticky_poll_ratio: @options.nonsticky_to_sticky_poll_ratio, - activity_task_poller_behavior: @options.activity_task_poller_behavior._to_bridge_options, + activity_task_poller_behavior: @options.activity_task_poller_behavior&._to_bridge_options, enable_workflows: !@options.workflows.empty?, enable_local_activities: !@options.workflows.empty? && !@options.activities.empty?, enable_remote_activities: !@options.activities.empty? && !@options.no_remote_activities, diff --git a/temporalio/sig/temporalio/internal/bridge/worker.rbs b/temporalio/sig/temporalio/internal/bridge/worker.rbs index 34220c39..c0017f50 100644 --- a/temporalio/sig/temporalio/internal/bridge/worker.rbs +++ b/temporalio/sig/temporalio/internal/bridge/worker.rbs @@ -8,9 +8,9 @@ module Temporalio attr_accessor tuner: TunerOptions attr_accessor identity_override: String? attr_accessor max_cached_workflows: Integer - attr_accessor workflow_task_poller_behavior: PollerBehaviorSimpleMaximum | PollerBehaviorAutoscaling + attr_accessor workflow_task_poller_behavior: (PollerBehaviorSimpleMaximum | PollerBehaviorAutoscaling)? attr_accessor nonsticky_to_sticky_poll_ratio: Float - attr_accessor activity_task_poller_behavior: PollerBehaviorSimpleMaximum | PollerBehaviorAutoscaling + attr_accessor activity_task_poller_behavior: (PollerBehaviorSimpleMaximum | PollerBehaviorAutoscaling)? attr_accessor enable_workflows: bool attr_accessor enable_local_activities: bool attr_accessor enable_remote_activities: bool @@ -34,9 +34,9 @@ module Temporalio tuner: TunerOptions, identity_override: String?, max_cached_workflows: Integer, - workflow_task_poller_behavior: PollerBehaviorSimpleMaximum | PollerBehaviorAutoscaling, + workflow_task_poller_behavior: (PollerBehaviorSimpleMaximum | PollerBehaviorAutoscaling)?, nonsticky_to_sticky_poll_ratio: Float, - activity_task_poller_behavior: PollerBehaviorSimpleMaximum | PollerBehaviorAutoscaling, + activity_task_poller_behavior: (PollerBehaviorSimpleMaximum | PollerBehaviorAutoscaling)?, enable_workflows: bool, enable_local_activities: bool, enable_remote_activities: bool, diff --git a/temporalio/sig/temporalio/worker.rbs b/temporalio/sig/temporalio/worker.rbs index 352dd727..cadc5a00 100644 --- a/temporalio/sig/temporalio/worker.rbs +++ b/temporalio/sig/temporalio/worker.rbs @@ -21,9 +21,9 @@ module Temporalio attr_reader identity: String attr_reader logger: Logger attr_reader max_cached_workflows: Integer - attr_reader max_concurrent_workflow_task_polls: Integer + attr_reader max_concurrent_workflow_task_polls: Integer? attr_reader nonsticky_to_sticky_poll_ratio: Float - attr_reader max_concurrent_activity_task_polls: Integer + attr_reader max_concurrent_activity_task_polls: Integer? attr_reader no_remote_activities: bool attr_reader sticky_queue_schedule_to_start_timeout: Float attr_reader max_heartbeat_throttle_interval: Float @@ -37,8 +37,8 @@ module Temporalio attr_reader workflow_failure_exception_types: Array[singleton(Exception)] attr_reader workflow_payload_codec_thread_pool: ThreadPool? attr_reader unsafe_workflow_io_enabled: bool - attr_reader workflow_task_poller_behavior: PollerBehavior - attr_reader activity_task_poller_behavior: PollerBehavior + attr_reader workflow_task_poller_behavior: PollerBehavior? + attr_reader activity_task_poller_behavior: PollerBehavior? attr_reader deployment_options: Worker::DeploymentOptions attr_reader patch_activation_callback: Proc? attr_reader debug_mode: bool @@ -57,9 +57,9 @@ module Temporalio identity: String?, logger: Logger, max_cached_workflows: Integer, - max_concurrent_workflow_task_polls: Integer, + max_concurrent_workflow_task_polls: Integer?, nonsticky_to_sticky_poll_ratio: Float, - max_concurrent_activity_task_polls: Integer, + max_concurrent_activity_task_polls: Integer?, no_remote_activities: bool, sticky_queue_schedule_to_start_timeout: Float, max_heartbeat_throttle_interval: Float, @@ -73,8 +73,8 @@ module Temporalio workflow_failure_exception_types: Array[singleton(Exception)], workflow_payload_codec_thread_pool: ThreadPool?, unsafe_workflow_io_enabled: bool, - workflow_task_poller_behavior: PollerBehavior, - activity_task_poller_behavior: PollerBehavior, + workflow_task_poller_behavior: PollerBehavior?, + activity_task_poller_behavior: PollerBehavior?, deployment_options: Worker::DeploymentOptions, patch_activation_callback: Proc?, debug_mode: bool, @@ -123,9 +123,9 @@ module Temporalio ?identity: String?, ?logger: Logger, ?max_cached_workflows: Integer, - ?max_concurrent_workflow_task_polls: Integer, + ?max_concurrent_workflow_task_polls: Integer?, ?nonsticky_to_sticky_poll_ratio: Float, - ?max_concurrent_activity_task_polls: Integer, + ?max_concurrent_activity_task_polls: Integer?, ?no_remote_activities: bool, ?sticky_queue_schedule_to_start_timeout: Float, ?max_heartbeat_throttle_interval: Float, @@ -139,8 +139,8 @@ module Temporalio ?workflow_failure_exception_types: Array[singleton(Exception)], ?workflow_payload_codec_thread_pool: ThreadPool?, ?unsafe_workflow_io_enabled: bool, - ?workflow_task_poller_behavior: PollerBehavior, - ?activity_task_poller_behavior: PollerBehavior, + ?workflow_task_poller_behavior: PollerBehavior?, + ?activity_task_poller_behavior: PollerBehavior?, ?deployment_options: Worker::DeploymentOptions, ?patch_activation_callback: Proc?, ?debug_mode: bool, From 31d6342c17e9f2493e9f0b7bdf5eb2a7c6f47fdd Mon Sep 17 00:00:00 2001 From: Veeral Patel Date: Mon, 2 Nov 2026 21:31:04 -0800 Subject: [PATCH 2/6] Allow nil poller options in Sorbet signatures; add changelog fragment --- changelog/fixed/eager-poller-pelican.md | 1 + .../rbi/temporalio/internal/bridge/worker.rbi | 8 ++++---- temporalio/rbi/temporalio/worker.rbi | 16 ++++++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 changelog/fixed/eager-poller-pelican.md diff --git a/changelog/fixed/eager-poller-pelican.md b/changelog/fixed/eager-poller-pelican.md new file mode 100644 index 00000000..7f5802f4 --- /dev/null +++ b/changelog/fixed/eager-poller-pelican.md @@ -0,0 +1 @@ +Workers with no poller configuration now auto-enroll in poller autoscaling when the namespace enables it, instead of always using a fixed 5-poller maximum. diff --git a/temporalio/rbi/temporalio/internal/bridge/worker.rbi b/temporalio/rbi/temporalio/internal/bridge/worker.rbi index 2e04d53f..8f05b35b 100644 --- a/temporalio/rbi/temporalio/internal/bridge/worker.rbi +++ b/temporalio/rbi/temporalio/internal/bridge/worker.rbi @@ -58,15 +58,15 @@ class Temporalio::Internal::Bridge::Worker::Options < ::Struct tuner: Temporalio::Internal::Bridge::Worker::TunerOptions, identity_override: T.nilable(String), max_cached_workflows: Integer, - workflow_task_poller_behavior: T.any( + workflow_task_poller_behavior: T.nilable(T.any( Temporalio::Internal::Bridge::Worker::PollerBehaviorSimpleMaximum, Temporalio::Internal::Bridge::Worker::PollerBehaviorAutoscaling - ), + )), nonsticky_to_sticky_poll_ratio: T.any(Integer, Float), - activity_task_poller_behavior: T.any( + activity_task_poller_behavior: T.nilable(T.any( Temporalio::Internal::Bridge::Worker::PollerBehaviorSimpleMaximum, Temporalio::Internal::Bridge::Worker::PollerBehaviorAutoscaling - ), + )), enable_workflows: T::Boolean, enable_local_activities: T::Boolean, enable_remote_activities: T::Boolean, diff --git a/temporalio/rbi/temporalio/worker.rbi b/temporalio/rbi/temporalio/worker.rbi index 269f7d51..4424a0e8 100644 --- a/temporalio/rbi/temporalio/worker.rbi +++ b/temporalio/rbi/temporalio/worker.rbi @@ -20,9 +20,9 @@ class Temporalio::Worker identity: T.nilable(String), logger: Logger, max_cached_workflows: Integer, - max_concurrent_workflow_task_polls: Integer, + max_concurrent_workflow_task_polls: T.nilable(Integer), nonsticky_to_sticky_poll_ratio: Numeric, - max_concurrent_activity_task_polls: Integer, + max_concurrent_activity_task_polls: T.nilable(Integer), no_remote_activities: T::Boolean, sticky_queue_schedule_to_start_timeout: Numeric, max_heartbeat_throttle_interval: Numeric, @@ -38,8 +38,8 @@ class Temporalio::Worker unsafe_workflow_io_enabled: T::Boolean, deployment_options: Temporalio::Worker::DeploymentOptions, patch_activation_callback: T.nilable(T.proc.params(input: Temporalio::Worker::PatchActivationInput).returns(T::Boolean)), - workflow_task_poller_behavior: Temporalio::Worker::PollerBehavior, - activity_task_poller_behavior: Temporalio::Worker::PollerBehavior, + workflow_task_poller_behavior: T.nilable(Temporalio::Worker::PollerBehavior), + activity_task_poller_behavior: T.nilable(Temporalio::Worker::PollerBehavior), debug_mode: T::Boolean, disable_payload_error_limit: T::Boolean ).void @@ -197,13 +197,13 @@ class Temporalio::Worker::Options < ::Data sig { returns(Integer) } def max_cached_workflows; end - sig { returns(Integer) } + sig { returns(T.nilable(Integer)) } def max_concurrent_workflow_task_polls; end sig { returns(Numeric) } def nonsticky_to_sticky_poll_ratio; end - sig { returns(Integer) } + sig { returns(T.nilable(Integer)) } def max_concurrent_activity_task_polls; end sig { returns(T::Boolean) } @@ -248,10 +248,10 @@ class Temporalio::Worker::Options < ::Data sig { returns(T.nilable(T.proc.params(input: Temporalio::Worker::PatchActivationInput).returns(T::Boolean))) } def patch_activation_callback; end - sig { returns(Temporalio::Worker::PollerBehavior) } + sig { returns(T.nilable(Temporalio::Worker::PollerBehavior)) } def workflow_task_poller_behavior; end - sig { returns(Temporalio::Worker::PollerBehavior) } + sig { returns(T.nilable(Temporalio::Worker::PollerBehavior)) } def activity_task_poller_behavior; end sig { returns(Temporalio::Worker::DeploymentOptions) } From 6746c18111ae22a36516fe2301910be5d5078370 Mon Sep 17 00:00:00 2001 From: Veeral Patel Date: Tue, 3 Nov 2026 09:45:34 -0800 Subject: [PATCH 3/6] Remove unneeded comments --- temporalio/ext/src/worker.rs | 2 -- temporalio/lib/temporalio/worker.rb | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/temporalio/ext/src/worker.rs b/temporalio/ext/src/worker.rs index d30a0340..d2e905b3 100644 --- a/temporalio/ext/src/worker.rs +++ b/temporalio/ext/src/worker.rs @@ -511,7 +511,6 @@ fn build_config(options: Struct, runtime_handle: &RuntimeHandle) -> Result>(id!("identity_override"))?) .max_cached_workflows(options.member::(id!("max_cached_workflows"))?) - // None lets core auto-enroll the poller into autoscaling when the namespace supports it .maybe_workflow_task_poller_behavior( options .child(id!("workflow_task_poller_behavior"))? @@ -521,7 +520,6 @@ fn build_config(options: Struct, runtime_handle: &RuntimeHandle) -> Result(id!("nonsticky_to_sticky_poll_ratio"))?, ) - // None lets core auto-enroll the poller into autoscaling when the namespace supports it .maybe_activity_task_poller_behavior( options .child(id!("activity_task_poller_behavior"))? diff --git a/temporalio/lib/temporalio/worker.rb b/temporalio/lib/temporalio/worker.rb index 8ec100d1..1532a2af 100644 --- a/temporalio/lib/temporalio/worker.rb +++ b/temporalio/lib/temporalio/worker.rb @@ -451,9 +451,9 @@ def self._validate_plugins!(plugins) # {Workflow.patched} for a patch ID should activate that patch. The callback receives a {PatchActivationInput} and # must return `true` to activate the patch or `false` to leave it inactive. # @param workflow_task_poller_behavior [PollerBehavior, nil] Specify the behavior of workflow task - # polling. Defaults to a 5-poller maximum, or autoscaling if the namespace auto-enrolls workers. + # polling. Defaults to a 5-poller maximum. # @param activity_task_poller_behavior [PollerBehavior, nil] Specify the behavior of activity task - # polling. Defaults to a 5-poller maximum, or autoscaling if the namespace auto-enrolls workers. + # polling. Defaults to a 5-poller maximum. # @param debug_mode [Boolean] If true, deadlock detection is disabled. Deadlock detection will fail workflow tasks # if they block the thread for too long. This defaults to true if the `TEMPORAL_DEBUG` environment variable is # `true` or `1`. @@ -498,7 +498,6 @@ def initialize( ) Internal::ProtoUtils.assert_non_reserved_name(task_queue) - # Left nil when neither is set, so core can auto-enroll the poller into autoscaling if max_concurrent_workflow_task_polls workflow_task_poller_behavior ||= PollerBehavior::SimpleMaximum.new(max_concurrent_workflow_task_polls) end From 714e0ae075c5c9225e5927bcf82bc42ed1368754 Mon Sep 17 00:00:00 2001 From: Veeral Patel Date: Tue, 3 Nov 2026 10:03:12 -0800 Subject: [PATCH 4/6] Remove changelog fragment --- changelog/fixed/eager-poller-pelican.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 changelog/fixed/eager-poller-pelican.md diff --git a/changelog/fixed/eager-poller-pelican.md b/changelog/fixed/eager-poller-pelican.md deleted file mode 100644 index 7f5802f4..00000000 --- a/changelog/fixed/eager-poller-pelican.md +++ /dev/null @@ -1 +0,0 @@ -Workers with no poller configuration now auto-enroll in poller autoscaling when the namespace enables it, instead of always using a fixed 5-poller maximum. From 529b039ffcccac79ceaa5e78848dfab1d183ddde Mon Sep 17 00:00:00 2001 From: Veeral Patel Date: Tue, 3 Nov 2026 18:00:04 -0800 Subject: [PATCH 5/6] Add changelog fragment --- changelog/fixed/eager-poller-pelican.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/fixed/eager-poller-pelican.md diff --git a/changelog/fixed/eager-poller-pelican.md b/changelog/fixed/eager-poller-pelican.md new file mode 100644 index 00000000..7f5802f4 --- /dev/null +++ b/changelog/fixed/eager-poller-pelican.md @@ -0,0 +1 @@ +Workers with no poller configuration now auto-enroll in poller autoscaling when the namespace enables it, instead of always using a fixed 5-poller maximum. From e7bf9f8bcab299df5b69d44e4763264651cf8925 Mon Sep 17 00:00:00 2001 From: Veeral Patel Date: Wed, 4 Nov 2026 14:06:12 -0800 Subject: [PATCH 6/6] Reword changelog fragment --- changelog/added/eager-poller-pelican.md | 1 + changelog/fixed/eager-poller-pelican.md | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 changelog/added/eager-poller-pelican.md delete mode 100644 changelog/fixed/eager-poller-pelican.md diff --git a/changelog/added/eager-poller-pelican.md b/changelog/added/eager-poller-pelican.md new file mode 100644 index 00000000..9ccc9589 --- /dev/null +++ b/changelog/added/eager-poller-pelican.md @@ -0,0 +1 @@ +Workers that don't configure poller behavior now auto-enroll in poller autoscaling when the server enables it (`frontend.pollerAutoscalingAutoEnroll`); otherwise they keep using a fixed 5-poller maximum. diff --git a/changelog/fixed/eager-poller-pelican.md b/changelog/fixed/eager-poller-pelican.md deleted file mode 100644 index 7f5802f4..00000000 --- a/changelog/fixed/eager-poller-pelican.md +++ /dev/null @@ -1 +0,0 @@ -Workers with no poller configuration now auto-enroll in poller autoscaling when the namespace enables it, instead of always using a fixed 5-poller maximum.