NEXUS-485: Support Workflow Update as a Nexus Operation#2945
Conversation
7852a29 to
93ee1a9
Compare
| if (commonLink.hasNexusOperation()) { | ||
| return nexusOperationToNexusLink(commonLink.getNexusOperation()); | ||
| } | ||
| // check for workflow link only if a more specific variant is not set |
There was a problem hiding this comment.
Nit: I don't think this comment makes sense, we are just checking variants here and there can only be one
There was a problem hiding this comment.
fixed - misread it as hasWorkflowID initially
| import org.junit.ClassRule; | ||
| import org.junit.Test; | ||
|
|
||
| public class UpdateWorkflowOperationTest extends BaseNexusTest { |
There was a problem hiding this comment.
Shouldn't need to inherit from BaseNexusTest, we should probably mark it as deprecated or remove it. (not saying do that in this PR)
There was a problem hiding this comment.
Any reason for removing it? looks useful as a utility
There was a problem hiding this comment.
All the utility is already in the SDK rule
| effectiveOptsBuilder.setUpdateId(requestId); | ||
| } | ||
| if (options.getWaitForStage() == null) { | ||
| effectiveOptsBuilder.setWaitForStage(WorkflowUpdateStage.ACCEPTED); |
There was a problem hiding this comment.
Oh WaitForStage should be explicitly set, that is the clients behaviour so we should try to match that here
There was a problem hiding this comment.
Ah ok wanted to see if we could make it easier with a default callout - stricter makes sense, will change
| if (input.getUpdateId() != null) { | ||
| optionsBuilder.setUpdateId(input.getUpdateId()); | ||
| } | ||
| return client.startWorkflowUpdate( |
There was a problem hiding this comment.
The Nexus client wasn't intended to take the stubs. The proposed API was to take the unbound method reference like all new Java SDK APIs like
public <T, A1, R> TemporalOperationResult<R> updateWorkflow(
String workflowId,
Class<T> workflowClass,
unctions.Func2<T, A1, R>> updateMethod,
A1 arg);We should discuss if the proposed API does not work
| // only set for updates | ||
| private final String updateId; | ||
|
|
||
| public OperationToken( |
There was a problem hiding this comment.
Could consider splitting the OperationToken into different Java types for the different t. Should be possible using jackson. Although I would be fine defering this until we have activities here as well and then doing the refactor then
There was a problem hiding this comment.
Thought about it with a base class that does common encode/decodes but left it as overloads give hints and the remaining is mostly similar
But agree on maybe changing after activities- if changing here, will track in other SDKs too for consistency
| * cancel. | ||
| */ | ||
| @Experimental | ||
| public final class CancelUpdateWorkflowExecutionInput { |
There was a problem hiding this comment.
| public final class CancelUpdateWorkflowExecutionInput { | |
| public final class CancelUpdateWorkflowInput { |
I thought we would call it CancelUpdateWorkflowInput ,open to other ideas though, regardless we should make sure we are consistent across all the SDKs and docs here.
|
|
||
| /** Returns the run ID extracted from the operation token, or null if not present. */ | ||
| @Nullable | ||
| public String getRunId() { |
There was a problem hiding this comment.
I think for CancelUpdateWorkflowInput this is always non null
There was a problem hiding this comment.
There are 2 cases -
- Right now, we are generating it based on the input - if user provides no runID, there wont be one in the token
Not sure if we need to grab it from a handle either - intent of user could be that they want to just run an update on current workflow run - but for cancel its not clear if the cancel should also "preserve" that original intent - i thought it should
eg. run update- without specifying runID- while workflow is on runID1, get token. Now, when workflow is on runID2, still want to cancel the update => the current token will work
OTOH, if they specifically had a runID, then it wont work but that is stricter and is fully consistent with what they need
This is consistent across the go/python/java SDK PRs
But we can discuss if this preserving original intent means getting runID via handle if empty- or if thats decided already - user just wants to cancel it, crafts the encoded token themselves without runID to cancel current one. Required for above case as well if token was initially minted with a specific runID
changed it to empty string default but i think its still same functionally
There was a problem hiding this comment.
We should not be generating based on the input, we should generate based on the output of the update response. Updates do not move between runs so please use the run ID of the update.
There was a problem hiding this comment.
Please also make sure you fix this in Go as well this needs to encode where the workflow update actually is so this is blocker for the Go SDK as well
| if (nexusOperationMetadata.operationCompleted) { | ||
| try { | ||
| R value = handle.getResult(); | ||
| return TemporalOperationResult.sync(value); |
There was a problem hiding this comment.
This vranch doesn't call asyncOperationStarted.set(false) - does it need to? It looks like it is left set to true.
There was a problem hiding this comment.
The asyncOperationStarted is more of a guard against multiple invocations using the same ephemeral nexusClient. Whether it completed sync or async is the result and isnt tied to how the req was made - because in either case, request itself was always an asyncReq
Only case where it resets to false is if the op can be retried safely- due to real issues like op failing due to invalid params etc
| private <R> void checkNexusUpdateOptionsValid(UpdateOptions<R> options) { | ||
| if (options.getWaitForStage() != WorkflowUpdateStage.ACCEPTED) { | ||
| throw new HandlerException( | ||
| HandlerException.ErrorType.BAD_REQUEST, |
There was a problem hiding this comment.
Go returns this as a nexus.OperationError - should this be consistent?
| URLEncoder.encode(w.getWorkflowId(), StandardCharsets.UTF_8.toString()) | ||
| .replace("+", "%20"); // handle workflowIds supporting spaces | ||
| String runId = URLEncoder.encode(w.getRunId(), StandardCharsets.UTF_8.toString()); | ||
| String url = String.format(linkPathFormat, namespace, workflowId, runId); |
There was a problem hiding this comment.
String.format can throw IllegalFormatException which is uncaught - should you just catch Exception instead of limiting it?
| .setWorkflowId(workflowID) | ||
| .setRunId(runID)); | ||
| } catch (Exception e) { | ||
| log.error("Failed to parse Nexus link URL", e); |
There was a problem hiding this comment.
Might still want to log the bad URL, like you did above
1758efd to
ba6377e
Compare
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
Closes NEXUS-485
How was this tested: