Skip to content

Conversation

@yuandrew
Copy link
Contributor

@yuandrew yuandrew commented Nov 13, 2025

What was changed

Now when registering a worker, users can explicitly specify workflow/nexus/activity workers, instead of always waiting for polls for workflow/nexus workers.

💥 Removed the no_remote_activities in favor of this new WorkerConfig option

Why?

This came up in #1058, to allow for multiple workers with different types to be registered to the same TQ.

This should also simplify shutdown, and make things a little more efficient.

Checklist

  1. Closes

  2. How was this tested:

Added some tests

  1. Any docs updates needed?

Note

Introduce WorkerTaskTypes to selectively enable polling for workflows, activities, and nexus; remove no_remote_activities; update worker behavior, errors, C-bridge, and tests accordingly.

  • Config/Types:
    • Add WorkerTaskTypes (all, workflow_only, activity_only, nexus_only) and replace WorkerConfig.no_remote_activities with task_types.
    • Validate at least one task type; ensure max_cached_workflows only when workflows enabled; tighten poller/cache constraints.
  • Worker Behavior:
    • Instantiate pollers/managers conditionally per task_types; workflows/local activities/nexus are optional.
    • Adjust shutdown to handle absent components; return ShutDown when polling disabled.
  • Errors:
    • Add WorkflowNotEnabled, ActivityNotEnabled, NexusNotEnabled for inappropriate completions.
  • C-Bridge:
    • Extend worker options with TemporalCoreWorkerTaskTypes; map to core WorkerTaskTypes.
  • Docs/Comments:
    • Update shutdown guidance to reference task_types.
  • Tests:
    • Add comprehensive tests for task type combinations and validation.
    • Update existing unit/integration tests to use task_types and new behavior.

Written by Cursor Bugbot for commit 5deef19. This will update automatically on new commits. Configure here.

@yuandrew yuandrew requested a review from a team as a code owner November 13, 2025 21:57
@yuandrew yuandrew changed the title Add explicit Worker configuration for type (workflow/activity/nexus) 💥 Add explicit Worker configuration for type (workflow/activity/nexus) Nov 13, 2025
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Tuner configuration allows conflicting settings.

The validation check for mutual exclusivity between tuner and max_outstanding_* fields doesn't include max_outstanding_nexus_tasks. This allows users to set both a tuner and max_outstanding_nexus_tasks, which should be mutually exclusive like the other max_outstanding_* fields, potentially causing configuration conflicts.

crates/common/src/worker.rs#L354-L362

return Err(
"Legacy build id-based versioning must have a non-empty build_id"
.to_owned(),
);
}
}
}
}

Fix in Cursor Fix in Web


Comment on lines 899 to 900
future::pending::<()>().await;
unreachable!()
Copy link
Member

Choose a reason for hiding this comment

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

If we are in activity-only mode, I think this could hang shutdown. If non_local_activities_complete is set true up at line 867, in the next poll we'll end up inside the pending at 859 and be stuck there forever because local_activities_complete was never set true.

I think we need to set it true in this branch, probably.

At minimum, we should verify that shutting down an activity-only worker works properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch, added unit and integration tests testing this shutdown

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Configuration conflict bypasses validation.

The validation check for mutual exclusivity between tuner and max_outstanding_* fields doesn't include max_outstanding_nexus_tasks. This allows users to set both a tuner and max_outstanding_nexus_tasks, which violates the documented constraint that these fields are mutually exclusive. The check should include self.max_outstanding_nexus_tasks.is_some() in the condition.

crates/common/src/worker.rs#L369-L373

{
return Err("max_outstanding_* fields are mutually exclusive with `tuner`".to_owned());
}
if let Some(wv) = self.versioning_strategy.as_ref() {

Fix in Cursor Fix in Web


if let Some(la_mgr) = &self.local_act_mgr
&& self
.handle_la_complete_action(la_mgr.complete(&task_token, la_res))
.is_some()
Copy link

Choose a reason for hiding this comment

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

Bug: Inconsistent Local Activity Error Handling

The complete_local_act function silently succeeds when local_act_mgr is None (workflows disabled), but this is inconsistent with how other task completions are handled. When attempting to complete an activity task with a local activity task token on a worker without workflows enabled, it should return an error like CompleteActivityError::ActivityNotEnabled rather than silently doing nothing, matching the error handling pattern used for regular activities and other task types.

Fix in Cursor Fix in Web

Comment on lines 936 to 937
let worker = starter.get_worker().await;

Copy link
Member

Choose a reason for hiding this comment

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

I think we'll want to provide a variant of this test where there is one task of each of the enabled types that gets polled for and completed, just to make sure that once the machinery is active shutdown still works.

Sorry, I know it's a bit annoying but we've got to be super careful with the shutdown stuff.

You can also potentially use rstest to help with the parameterization here

pub max_cached_workflows: u32,
pub tuner: TunerHolder,
pub no_remote_activities: bool,
pub task_types: u8,
Copy link
Member

Choose a reason for hiding this comment

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

Over the C boundary, I think it's definitely clearer to just use three bools instead of having these magic bitmask integers the SDK will hardcode

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ended up making a struct of bools

///
/// Note: At least one task type must be specified or the worker will fail validation.
#[builder(default = "worker_task_types::all()")]
pub task_types: WorkerTaskTypes,
Copy link
Member

Choose a reason for hiding this comment

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

I figure just three bools is simplest, even if you have to make a hash internally, but I guess not a big deal. Also at this point it could be argued it shouldn't have a default and should be required to be set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanna say having default be all by default matches existing behavior today, but then again not sure it matters much since it's not user facing

Copy link
Member

Choose a reason for hiding this comment

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

No strong opinion on whether we should have a default since every SDK needs to start setting this anyways and the default therefore will never be used if done properly by SDKs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fair point, I can remove the default to force SDKs to be explicit

Copy link
Member

Choose a reason for hiding this comment

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

FWIW, I'm ok w/ defaulted too, just that I hope SDKs will ignore that and always set.

///
/// Note: At least one task type must be specified or the worker will fail validation.
#[builder(default = "worker_task_types::all()")]
pub task_types: WorkerTaskTypes,
Copy link
Member

Choose a reason for hiding this comment

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

Is there somewhere in the client code that has to be altered to properly account for this as part of the worker dedupe check?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Planning on keeping that PR separate, #1059, since that feature introduces different behavior, whereas this PR introduces a new feature, but maintains existing behavior, and to make review easier

Copy link
Member

@cretz cretz left a comment

Choose a reason for hiding this comment

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

Did not review rest of logic beyond just the lang-facing structure

uint32_t max_cached_workflows;
struct TemporalCoreTunerHolder tuner;
bool no_remote_activities;
struct TemporalCoreWorkerTaskTypes task_types;
Copy link
Member

@cretz cretz Nov 17, 2025

Choose a reason for hiding this comment

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

This doesn't need to be a whole separate struct IMO just for the few fields within, but it's mostly harmless

///
/// Note: At least one task type must be specified or the worker will fail validation.
#[builder(default = "worker_task_types::all()")]
pub task_types: WorkerTaskTypes,
Copy link
Member

Choose a reason for hiding this comment

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

No strong opinion on whether we should have a default since every SDK needs to start setting this anyways and the default therefore will never be used if done properly by SDKs

@yuandrew yuandrew merged commit 32cdd90 into temporalio:master Nov 17, 2025
48 of 50 checks passed
#[case::activity_and_nexus_idle(false, true, true, false, "activity-nexus-idle")]
#[tokio::test]
async fn test_task_type_combinations_unified(
#[case] enable_workflows: bool,
Copy link
Member

Choose a reason for hiding this comment

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

Could've used #[values] here to have it do the combination matrix for you, but, this is fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants