Skip to content

Add Create and Update for Worker Deployment Version, update scaler config and enable per-task-type compute providers#731

Open
02strich wants to merge 4 commits intoserverlessfrom
stefan/serverless-v2
Open

Add Create and Update for Worker Deployment Version, update scaler config and enable per-task-type compute providers#731
02strich wants to merge 4 commits intoserverlessfrom
stefan/serverless-v2

Conversation

@02strich
Copy link

@02strich 02strich commented Mar 18, 2026

What changed?

  • Enables validating, creating and updating compute providers at the worker deployment version level, as that is where it at the most basic belongs
  • Making the scaler config more flexible to allow for pluggable scalers
  • Allowing for different compute providers depending on task types and regions

@02strich 02strich force-pushed the stefan/serverless-v2 branch 2 times, most recently from 4b74a6b to 448fcaa Compare March 18, 2026 15:47
@02strich 02strich force-pushed the stefan/serverless-v2 branch 4 times, most recently from 86892e0 to 7ca9f8b Compare March 18, 2026 21:35
@02strich 02strich force-pushed the stefan/serverless-v2 branch 2 times, most recently from 42cbcff to 7ce67c6 Compare March 18, 2026 23:17
@semgrep-managed-scans
Copy link

Semgrep found 1 missing-explicit-permissions finding:

  • .github/workflows/trigger-api-go-update.yml

No explicit GITHUB_TOKEN permissions found at the workflow or job level. Add a permissions: block at the workflow root (applies to all jobs) or per job with least privilege (e.g., contents: read and only specific writes like pull-requests: write if needed).

@02strich 02strich force-pushed the stefan/serverless-v2 branch from 7ce67c6 to 041a11a Compare March 18, 2026 23:27
// Informs a worker lifecycle controller *when* and *how often* to perform
// certain worker lifecycle actions like starting a serverless worker.
temporal.api.compute.v1.ComputeScaler scaler = 2;
message ComputeConfigScalingGroup {
Copy link
Member

Choose a reason for hiding this comment

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

nit: not need to qualify this as ComputeConfig... if it's embedded inside a ComputeConfig message.
That being said, if you think there's any chance that this message would need to be used outside of this context, I would make it top level.

Copy link
Author

Choose a reason for hiding this comment

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

Removed the prefix


// The region of the temporal server for which this compute config should apply.
// This is used together with replication to enable clean-hand-off when migrating regions.
optional string region = 4;
Copy link
Member

Choose a reason for hiding this comment

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

nit: why is region field number 4 if it's the second field defined here?

Copy link
Author

Choose a reason for hiding this comment

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

no special reason, re-numbered

// type of the compute scaler. this string is implementation-specific and
// can be used by implementations to understand how to interpret the
// contents of the scaler_details field.
string type = 1;
Copy link
Member

Choose a reason for hiding this comment

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

nit: use upper case letters when starting sentences to be consistent with the rest of the API.

Copy link
Author

Choose a reason for hiding this comment

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

done


// Contains scaler-specific instructions and configuration.
oneof detail {
// will be an unencrypted, JSON-encoded object of scaler-specific
Copy link
Member

Choose a reason for hiding this comment

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

nit: "will be" doesn't quite sound right here, i wouldn't use the future tense.

Copy link
Author

Choose a reason for hiding this comment

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

done

// (-- api-linter: core::0146::any=disabled
// aip.dev/not-precedent: This needs to be extensible to
// externally-written compute scalers --)
string detail_json = 2;
Copy link
Member

Choose a reason for hiding this comment

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

Why not use payload always? Payload with metdata["encoding"] = "json/plain" achieves what you want here.

Copy link
Author

Choose a reason for hiding this comment

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

The detail_json is consumed by the server (i.e. parsed and understood), while the payload is meant to be used with remote providers and passed onward as-is without the server inspecting it.

If even with that context, you believe this should always be a payload, happy to change it (this was specific feedback we had received on the first API PR).

repeated temporal.api.compute.v1.ComputeConfig.ComputeConfigScalingGroup remove_compute_config_scaling_groups = 5;

// Optional. The identity of the client who initiated this request.
string identity = 4;
Copy link
Member

Choose a reason for hiding this comment

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

nit: make this field number 2 because it's mostly metadata and common across different requests.

Copy link
Author

Choose a reason for hiding this comment

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

done

// Required.
string deployment_name = 2;

// Optional. Contains the new worker compute configuration for the Worker
Copy link
Member

Choose a reason for hiding this comment

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

What happens if this is empty? Shouldn't it be "required"?

Copy link
Author

Choose a reason for hiding this comment

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

thanks, copy&paste mistake. Text updated

string identity = 4;
}

message UpdateWorkerDeploymentVersionComputeConfigResponse {
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to return a conflict token here?

Copy link
Author

Choose a reason for hiding this comment

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

done

}

// Used to update the compute config of a Worker Deployment Version.
message UpdateWorkerDeploymentVersionComputeConfigRequest {
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this take a conflict token and a ComputeConfig object?

Copy link
Member

Choose a reason for hiding this comment

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

What about a request ID?

Copy link
Author

Choose a reason for hiding this comment

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

done - both


// Optional. The identity of the client who initiated this request.
string identity = 4;
// A unique identifier for this create request for idempotence. Typically UUIDv4.
Copy link
Member

Choose a reason for hiding this comment

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

Worth noting how this is used, does the server dedupe requests with the same idempotency key?

Copy link
Author

Choose a reason for hiding this comment

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

@ShahabT can you confirm the details so I am not mis-representing here?

Copy link
Contributor

Choose a reason for hiding this comment

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

here is the behavior, we can document:

  • Retrying with same request id is a successful no-op.
  • Retrying with different request id is an error.
  • One the object is deleted, any request id (even one used for a previous creation) will re-create it.

Copy link
Author

Choose a reason for hiding this comment

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

Updated the comment with Shahab's notes

@02strich 02strich force-pushed the stefan/serverless-v2 branch from 041a11a to 49d52e9 Compare March 19, 2026 00:36
@02strich 02strich requested review from ShahabT and bergundy March 19, 2026 00:46
string namespace = 1;

// Required.
string deployment_name = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

@02strich what do you expect the server do for this API? also, related, does deployment name play any role in the validation? if not, should we remove it?

Copy link
Author

Choose a reason for hiding this comment

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

Only in the sense that a deployment should be present. I think having it can come in beneficial if we decide to go for default values in the deployment or similar approaches down the road. Given that these are mapped to HTTP paths, adding it later won't be super straightforward.


// The region of the temporal server for which this compute config should apply.
// This is used together with replication to enable clean-hand-off when migrating regions.
optional string region = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

let's remove optional for consistency and instead clarify in the comment that it is optional. Specify want would be the behavior for a replicated NS if this value is not provided. Can one have a scaling group with region set and another scaling group without region, say as a catch-all?

Also, if the implementation is not going to be ready in the first release, we should not merge it yet.

Copy link
Contributor

Choose a reason for hiding this comment

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

Overall, I'm not sure if we should add the region dimension just yet. It's definitely not needed for the MVP and I worry we're complicating things by adding a premature dimension.

Copy link
Author

Choose a reason for hiding this comment

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

Made consistent using the "Optional." in the comment instead of the protobuf label. And updated the comment to be clear that it is a catch all if not provided.

I am aiming for this being supported in the first release as MRN is a really interesting use case.

Copy link
Author

Choose a reason for hiding this comment

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

removed it for now until we can settle on the name

message ComputeConfigEntry {
message ScalingGroup {
// The set of task queue types this compute config serves.
repeated temporal.api.enums.v1.TaskQueueType task_queue_types = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Would empty list be supported? if so, would it mean a catch-all?
Also clarify that same region cannot have more than one scaling group for the same tq type.

Copy link
Author

Choose a reason for hiding this comment

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

yes empty would be supported and catch-all - updated the text

@02strich 02strich force-pushed the stefan/serverless-v2 branch 2 times, most recently from 11be549 to ae4546c Compare March 19, 2026 20:17
@02strich 02strich requested a review from ShahabT March 19, 2026 21:15
@02strich 02strich force-pushed the stefan/serverless-v2 branch from ae4546c to 309b565 Compare March 19, 2026 23:13
@02strich 02strich marked this pull request as ready for review March 19, 2026 23:14
@02strich 02strich requested review from a team as code owners March 19, 2026 23:14
Adding a Create API for Worker Deployment Versions, as well as a compute config setting at the version level as that is the matching one. A Deployment Version references a specific running version of a worker, which is also what a compute provider tracks.
Both scaler and compute providers are pluggable, so aligning the formats for easier understanding.
Advanced customers might want to define different compute providers for different task types, so enabling splitting that up.
@02strich 02strich force-pushed the stefan/serverless-v2 branch 2 times, most recently from a3cbeea to 387d6ad Compare March 19, 2026 23:24
Comment on lines +2466 to +2467
// Optional. Contains the compute config scaling groups to remove from the Worker Deployment.
repeated string remove_compute_config_scaling_groups = 7;

Choose a reason for hiding this comment

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

I'm not a huge fan of separating out a "remove" list from an "add/update" list, but if this is a pattern used elsewhere in the API, ok :) I prefer a Replace-style pattern where the user passes the exact desired state, including any existing list or map fields and the server simply replaces those fields with the supplied values. Additionally, having a add or remove separate API call to add/remove individual list items from a list field is a good idea. So, in this case, we'd add AddWorkerDeploymentVersionScalingGroup and RemoveWorkerDeploymentVersionScalingGroup API calls...

But again, if this is not a pattern used in the existing API, so be it :)

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, this was changed from replace-style to add/update based on feedback what is used elsewhere in the API

string namespace = 1;

// Required.
string deployment_name = 2;

Choose a reason for hiding this comment

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

How is the build ID/version of the WDV identified in this request message?

Copy link
Author

Choose a reason for hiding this comment

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

There is no build ID/version yet when running this validate API as it is not used in validation

Choose a reason for hiding this comment

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

Hmm. Seems odd to have WorkerDeploymentVersion in the API name, then... Maybe better to go with ValidateComputeConfig?

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, I had been unhappy with that contradiction as well. That said ValidateComputeConfig seems too generic as well which is why I didn't go with it.

I now reframed it a bit: added in the build ID as well. This way the story is more that the validate-compute-config should mirror the update-compute-config more than the create version API. That means it clearly should have the build ID. Down the road this might also be useful once we can validate that the compute reference points at the configured build ID.

@02strich 02strich force-pushed the stefan/serverless-v2 branch from 387d6ad to a19c407 Compare March 20, 2026 17:04
Copy link

@jaypipes jaypipes left a comment

Choose a reason for hiding this comment

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

👍

// Informs a worker lifecycle controller *when* and *how often* to perform
// certain worker lifecycle actions like starting a serverless worker.
temporal.api.compute.v1.ComputeScaler scaler = 2;
message ScalingGroup {
Copy link
Member

Choose a reason for hiding this comment

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

nit: I would flatten this out to a top level message if there's any chance that this message will be reused in the future.

// how to decrypt the payload.
// Encrypted, encoded bytestring containing provider-specific
// information. The implementation must understand how to decrypt the payload.
temporal.api.common.v1.Payload detail_payload = 3;
Copy link
Member

Choose a reason for hiding this comment

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

Use only payload please, there's a straightforward way of expressing plain JSON in a payload.

// will be an encrypted, encoded bytestring containing
// provider-specific information. The implementation must understand
// how to decrypt the payload.
// Encrypted, encoded bytestring containing provider-specific
Copy link
Member

Choose a reason for hiding this comment

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

This would be optionally encrypted.

Comment on lines +28 to +32
string detail_json = 2;

// Encrypted, encoded bytestring containing scaler-specific
// information. The implementation must understand how to decrypt the payload.
temporal.api.common.v1.Payload detail_payload = 3;
Copy link
Member

Choose a reason for hiding this comment

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

Same here, no need for both of these fields.


// Optional. Contains the compute config scaling groups to add or updated for the Worker
// Deployment.
map<string, temporal.api.compute.v1.ComputeConfig.ScalingGroup> compute_config_scaling_groups = 6;
Copy link
Member

Choose a reason for hiding this comment

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

I would recommend using field masks for update APIs. We've done that in a few places (see UpdateActivityOptions). This way if you add fields that the client is not aware of they will not accidentally be deleted on update.

Copy link
Author

Choose a reason for hiding this comment

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

@ShahabT ?

This was what other APIs around here already used.

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.

4 participants