Skip to content

NEXUS-485: Support Workflow Update as a Nexus Operation - #2945

Merged
mavemuri merged 4 commits into
mainfrom
updates-nexus-op
Jul 31, 2026
Merged

NEXUS-485: Support Workflow Update as a Nexus Operation#2945
mavemuri merged 4 commits into
mainfrom
updates-nexus-op

Conversation

@mavemuri

@mavemuri mavemuri commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What was changed

Adds support for Workflow Update as a Nexus operation

Why?

Part of effort to expose all Temporal primitives via Nexus operations

Checklist

  1. Closes NEXUS-485

  2. How was this tested:

  • added integration tests
  1. Any docs updates needed?
  • TBD

@mavemuri
mavemuri force-pushed the updates-nexus-op branch 2 times, most recently from 7852a29 to 93ee1a9 Compare July 10, 2026 03:36
Comment thread temporal-sdk/src/main/java/io/temporal/internal/common/LinkConverter.java Outdated
Comment thread temporal-sdk/src/main/java/io/temporal/nexus/TemporalNexusClientImpl.java Outdated
Comment thread temporal-sdk/src/main/java/io/temporal/nexus/TemporalNexusClientImpl.java Outdated
.setWorkflowId(workflowID)
.setRunId(runID));
} catch (Exception e) {
log.error("Failed to parse Nexus link URL", e);

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.

Might still want to log the bad URL, like you did above

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 as before, might have gotten unfixed!

@mavemuri
mavemuri force-pushed the updates-nexus-op branch 3 times, most recently from 798079e to d1cd99c Compare July 13, 2026 21:02
// only run this test if the server supports update completion callbacks
// currently only for local server manual checks until the in-memory test
// server can support update completion callbacks
assumeTrue(

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.

We should just skip the Update tests for the in-memory test server we should always be running these tests when using a real server for test. You can look at how we tested other things like Stand Alone Activities

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.

So we won't need UPDATE_CALLBACKS_SUPPORTED

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.

SDKTestWorkflowRule.useExternalService this wasnt enough as the test also needs update callbacks to be enabled via dynamic config
Didnt want to rely on just local server flag because someone could run server without those flags - they arent required anyway
Added this way to ensure that if someone runs this against their own server- for testing other SDK changes etc- itll only get tested iff the server supports it
Or can add a note if someone runs into this that theyd need to enable flags on their real server

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.

Yes you should assume the users server has this DC set or fail the test

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.

To run SDK test against a local server you have to set the require DC, that is true is all SDKs CI

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.

Got it - will change and just add a comment

requestLinks))
.addAllLinks(requestLinks);
} else {
log.error("unexpected error fetching nexusOperationMetadata");

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.

This is an expected case if the user makes a regular update call from the regular Temporal client inside a Nexus handler, we need to make sure that still works like before and we didn't break anything there

@Override
public <T, A1, A2, A3, A4> TemporalOperationResult<Void> startWorkflowUpdate(
Class<T> workflowClass,
String workflowId,

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 need overloads that take the workflow execution as well to target the specific run ID , @maciejdudko thoughts?

@mavemuri mavemuri Jul 16, 2026

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.

added one just to align on the shape here https://github.com/temporalio/sdk-java/pull/2945/changes#diff-5f53049561675309c6d8a1a6b09638b2e6e9096a5b614047e2d96d4491ed5a73R776

Just so we're on the same page, if we want to do this, there will be 15 more(additional, not replacement) that will have workflow execution instead of the workflowID right?

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.

Yeah it is a lot of overloads , but we need to be able to target by workflow ID or (Workflow ID, Run ID) pair

private <R> void checkNexusUpdateOptionsValid(UpdateOptions<R> options)
throws OperationException {
if (options.getWaitForStage() != WorkflowUpdateStage.ACCEPTED) {
throw OperationException.failed(

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 don't think this should fail the Nexus operation, I thought we already decided that in the Go SDK PR

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.

updated to handler error to retry


public CancelUpdateWorkflowInput(String workflowId, String runId, String updateId) {
this.workflowId = Objects.requireNonNull(workflowId);
this.runId = Strings.nullToEmpty(runId);

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.

Note still need to update this to require

@mavemuri
mavemuri force-pushed the updates-nexus-op branch 5 times, most recently from 626bcd9 to 1d3395d Compare July 16, 2026 17:07
@mavemuri
mavemuri marked this pull request as ready for review July 16, 2026 18:50
@mavemuri
mavemuri requested a review from a team as a code owner July 16, 2026 18:50
Comment thread temporal-sdk/src/main/java/io/temporal/nexus/TemporalNexusClientImpl.java Outdated
.setWorkflowId(workflowID)
.setRunId(runID));
} catch (Exception e) {
log.error("Failed to parse Nexus link URL", e);

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 as before, might have gotten unfixed!

@mavemuri

Copy link
Copy Markdown
Contributor Author

Might have gotten unfixed. :-D It's still BAD_REQUEST!

This was an intentional change - the idea was that we keep retrying and the user will deploy a new version of the handler that will fix it. Still up for discussion before it goes GA but for the preview, this is consistent across go/python/java
#2945 (comment)

Same as before, might have gotten unfixed!

This was more just to be in-line with the existing convention - dont fail if unparsable

// Swallow un-parsable links since they are not critical to processing

Should we fix for all?

Can you also please comment on this thread #2945 (comment) - unsure about adding 14/15 more overloads but there might be a valid need for it - targeting by runID
@Evanthx

@Quinn-With-Two-Ns

Copy link
Copy Markdown
Contributor

BAD_REQUEST is not retried by default

@mavemuri

mavemuri commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

BAD_REQUEST is not retried by default

Yes, but was also setting the retry explicitly for that reason- iiuc, this will ensure it will retry
https://github.com/temporalio/sdk-java/pull/2945/changes#diff-b859b54dda09bacf7f8d2fddcb6c9f6727315ceb62f22d472a5cc889bd457507R607

This case is bit odd because it is a bad request- but is retriable so this seemed like the right way to do it

@Quinn-With-Two-Ns

Copy link
Copy Markdown
Contributor

I'd probably capture the validation failure as INTERNAL, but if we did BAD_REQUEST in other SDKs then I am fine with it for now

@maciejdudko maciejdudko left a comment

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.

Just a few small things, then it should be ready to merge.

Comment on lines +538 to +540
} catch (JsonProcessingException e) {
throw new IllegalStateException("failed to generate update operation token", e);
}

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.

  1. Under what circumstances can JsonProcessingException happen? Is it user error or server/implementation error? If it's user error then we ought to have better message.

  2. This doesn't catch unchecked exceptions. catch (Exception e) would be a better choice.

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.

It should only be a server error unless the user is trying to manually craft a new token- but that is not the normal case

Comment on lines +6 to +9
public enum OperationTokenType {
UNKNOWN(0),
WORKFLOW_RUN(1);
WORKFLOW_RUN(1),
WORKFLOW_UPDATE(3);

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.

Value 2 is missing. If it's intentional, a comment would be helpful.

throws OperationException;

/**
* TBD: determine if all the other 14 func+proc+void are required and add if needed Starts a

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.

  1. Change TBD to TODO.
  2. TBD/TODO should not be part of the docstring. Make it a regular comment before docstring.

* #startWorkflowUpdate(Class, String, Functions.Func1, UpdateOptions)} for the full behavior
* contract.
*
* @param <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.

Doc tag contents are missing for this overload.

@Evanthx Evanthx left a comment

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.

A few small things ...

"invalid update request",
new IllegalArgumentException(
"nexus op workflow updates only support WorkflowUpdateStageAccepted for async updates"),
RetryBehavior.RETRYABLE);

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 this fails, it will always fail, won't it? So the error shouldn't be RETRYABLE

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.

This was the one discussed here - #2945 (comment)
The idea was that a new version of the handler could be deployed which fixes the wid and it would then start passing
This is consistent with the go/python SDKs temporalio/sdk-go#2417 (comment)

@Evanthx Evanthx left a comment

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.

LGTM

@mavemuri
mavemuri merged commit b1b2d58 into main Jul 31, 2026
55 of 57 checks passed
@mavemuri
mavemuri deleted the updates-nexus-op branch July 31, 2026 18:59
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