Skip to content

Support Workflow Update as a Nexus Operation#2194

Open
Evanthx wants to merge 9 commits into
mainfrom
nexus-update-workflow
Open

Support Workflow Update as a Nexus Operation#2194
Evanthx wants to merge 9 commits into
mainfrom
nexus-update-workflow

Conversation

@Evanthx

@Evanthx Evanthx commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What was changed

Why?

Checklist

  1. Closes

  2. How was this tested:

  1. Any docs updates needed?

@Evanthx
Evanthx requested a review from a team as a code owner July 10, 2026 22:28
@Evanthx
Evanthx force-pushed the nexus-update-workflow branch from fc51ac9 to c219b68 Compare July 10, 2026 23:39
@Evanthx
Evanthx force-pushed the nexus-update-workflow branch from 52d2045 to 121ff88 Compare July 13, 2026 23:21
// requires server-side support for delivering Workflow Update completion callbacks to Nexus, which
// the bundled ephemeral server (Temporal 1.31.2) does not yet provide (workflow-run completion
// callbacks do work there). Enable once the test server delivers Update completion callbacks.
test.skip('UpdateWorkflow Nexus operation - pending update completes asynchronously via callback', async (t) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same comment as dotnet, this should be enabled

Comment thread packages/nexus/src/workflow-helpers.ts Outdated
case OperationTokenType.UPDATE_WORKFLOW: {
let updateToken;
try {
updateToken = loadUpdateWorkflowOperationToken(token);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why are we reparsing the token, we already have opToken?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, now using opToken

/**
* The Run ID extracted from the operation token. May be empty if the run was not pinned.
*/
readonly runId: string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The run ID should be know here since the server returns the run ID on the update response

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Now using handle.workflowRunId

Comment thread packages/client/src/workflow-client.ts Outdated
Comment thread packages/client/src/workflow-client.ts Outdated
Comment thread packages/nexus/src/workflow-helpers.ts Outdated
Comment on lines +338 to +353
export interface NexusUpdateWorkflowOptions<Ret, Args extends any[]> {
/**
* ID of the Workflow to send the Update to.
*/
readonly workflowId: string;

/**
* Run ID of the Workflow to send the Update to. If unset, the Update is sent to the most recent
* run of the Workflow.
*/
readonly runId?: string;

/**
* The Update definition (as returned by `defineUpdate`) or the Update name.
*/
readonly update: UpdateDefinition<Ret, Args> | string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should consider threading the Name type through to the UpdateDefinition like the WorkflowHandle

  executeUpdate<Ret, Args extends [], Name extends string = string>(
    def: UpdateDefinition<Ret, Args, Name> | string,
    options?: WorkflowUpdateOptions & { args?: Args }
  ): Promise<Ret>;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment thread packages/nexus/src/workflow-helpers.ts Outdated
Comment on lines +368 to +373
/**
* The Update lifecycle stage to wait for. Only {@link WorkflowUpdateStage.ACCEPTED} is supported
* for Nexus operations; any other value is rejected. Defaults to
* {@link WorkflowUpdateStage.ACCEPTED}.
*/
readonly waitForStage?: WorkflowUpdateStage;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we expose this at all if there's only one valid value and we default to it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point, removed it

Comment thread packages/nexus/src/link-converter.ts Outdated
Comment on lines +94 to +97
const url = new URL(
`temporal:///namespaces/${encodeURIComponent(wl.namespace)}/workflows/${encodeURIComponent(
wl.workflowId
)}/${encodeURIComponent(wl.runId ?? '')}/history`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the wl.runId property is coalesced to '' does the URL still resolve to a valid page on the UI?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Point. RunId is now required

Comment thread packages/nexus/src/token.ts Outdated
Comment thread packages/test/src/test-nexus-update-operation.ts Outdated
Comment on lines +95 to +97
if (sleepMs > 0) {
await workflow.sleep(sleepMs);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this sleep necessary? I'm wondering if there's a potential flake being hidden by this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

workflow.sleep suspends the update handler after it accepts, that is what forces the async completion callback path. So when it sleeps we get the async test, when it doesn't sleep it's a sync test.

Comment thread packages/nexus/src/workflow-helpers.ts Outdated
Comment on lines +424 to +443

/**
* Sends an Update to a Workflow as the backing operation for the current Nexus Operation.
*
* The Update request is augmented with the Nexus Operation's request ID (for deduplication), its
* request links, and a completion callback carrying the operation token, so the Update's
* completion is delivered back to the Nexus caller.
*
* Only asynchronous, `ACCEPTED`-stage Updates are supported: this method returns once the Update
* is accepted, yielding an asynchronous {@link TemporalOperationResult} whose completion is
* delivered via the callback. If the Update has already completed by the time it is accepted (for
* example a retried request with the same Update ID, or an Update that completes immediately), a
* synchronous result is returned instead; if that completed Update failed (e.g. validation
* rejection, which is non-retryable), it surfaces as a failed Nexus Operation.
*
* @experimental Nexus support in Temporal SDK is experimental.
*/
updateWorkflow<Ret, Args extends any[] = []>(
options: NexusUpdateWorkflowOptions<Ret, Args>
): Promise<TemporalOperationResult<Ret>>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should put this function onto the Nexus specific workflow handle to match the signal method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved!

Comment thread packages/nexus/src/workflow-helpers.ts Outdated
'failed',
'Nexus operation Workflow Updates only support the ACCEPTED wait stage for async updates'
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Similar to the above comment, I think we should consider removing this check and just always using ACCEPTED.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Evanthx and others added 6 commits July 16, 2026 15:44
Co-authored-by: Alex Mazzeo <alex.mazzeo@temporal.io>
Co-authored-by: Alex Mazzeo <alex.mazzeo@temporal.io>
Co-authored-by: Alex Mazzeo <alex.mazzeo@temporal.io>
Co-authored-by: Alex Mazzeo <alex.mazzeo@temporal.io>
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