Add endpoint-aware serialization for Nexus operations - #2644
Add endpoint-aware serialization for Nexus operations#2644JoshuaFrenchwood wants to merge 4 commits into
Conversation
6d95b6a to
e31beb5
Compare
chrsmith
left a comment
There was a problem hiding this comment.
Lots of nitpicks and some questions. But this all looks right. (Though I'd defer approval to someone with a little more background on the SDKs and/or stronger opinions about how any newly exported types/methods should look.)
|
|
||
| // SerializationContext provides metadata about where serialization is occurring. | ||
| // Implementations include [WorkflowSerializationContext] for workflow-level | ||
| // payloads, and [ActivitySerializationContext] for activity-level payloads. |
There was a problem hiding this comment.
Minor nit: since there are 3x different things, maybe we should use a list rather than a really long sentence? e.g.
// SerializationContext provides metadata about where serialization is occurring,
// with the concrete type depending on the context.
// - [WorkflowSerializationContext] for workflow-level payloads
// - [ActivitySerializationContext] for activity-level payloads
// - [NexusSerializationContext] for caller-side Nexus operation payloads
Also, what is a "caller-side Nexus operation payload"? Would it be more accurate or clearer to say "Nexus handlers" instead?
There was a problem hiding this comment.
Thanks, I updated this to be more clear. By caller side, I meant workflows that are calling nexus operations.
|
|
||
| // NexusSerializationContext is the serialization context for caller-side Nexus | ||
| // operation input, successful result, and failure decoding. Operation summaries | ||
| // and user metadata use the workflow serialization context instead. |
There was a problem hiding this comment.
Is the typical reader of this code supposed to know what "Operation summaries" or "user metadata" are referring to? It's probably benign, but if we expect different fields of the same proto to be ran through different serializers, that sounds like a bad thing?
There was a problem hiding this comment.
I removed that last sentence and made this more readable.
| @@ -2088,7 +2094,7 @@ func (weh *workflowExecutionEventHandlerImpl) handleNexusOperationCompleted(even | |||
| state := command.getData().(*scheduledNexusOperation) | |||
There was a problem hiding this comment.
Nit: Is there a better name we can use here than state? We are essentially casting the command's data to the underlying "event type", right? Would something like event or eventSource be clearer?
There was a problem hiding this comment.
I renamed it scheduleNexusOperation
| callback func(*commonpb.Payload, error), | ||
| startedHandler func(opID string, e error), | ||
| ) int64 { | ||
| failureConverter := params.failureConverter |
There was a problem hiding this comment.
Nit: This is perfectly fine as-is, but you can use cmp.Or(params.failureConverter, env.failureConverter) to simplify this type of initialization.
https://pkg.go.dev/cmp#Or
There was a problem hiding this comment.
Nice! used that instead thanks
| return ExecuteNexusOperationParams{}, fmt.Errorf("invalid 'operation' parameter, must be an OperationReference or a string") | ||
| } | ||
|
|
||
| nsc := converter.NexusSerializationContext{ |
There was a problem hiding this comment.
Nit: For readability, maybe add a comment to group these things? What do you think of:
// Nexus context and data converters.
|
|
||
| const intTestNexusHMACEncoding = "binary/nexus-hmac-test" | ||
|
|
||
| func intTestNexusContextKey(endpoint, service, operation string) string { |
There was a problem hiding this comment.
Nit: Is "intTest" short for integration test? If so, maybe its all fine as-is. But IMHO, something like nexusContextToCodecKey might be clearer?
There was a problem hiding this comment.
Yeah intTest is integration test, I renamed it to "intTestNexusCodecKey"
| ts.NoError(err) | ||
| var result string | ||
| ts.NoError(run.Get(ctx, &result)) | ||
| ts.ElementsMatch([]string{"hmac", "zlib"}, strings.Split(result, "|")) |
There was a problem hiding this comment.
For readability, could we have the workflow expose a proper type rather than a string?
We want to verify that the decoded results match, but It would be great if we could also look at the raw, encoded results between the two codecs and confirm that they do NOT match. (i.e. the hmac and zlib encoders are using two different encodings for the same data.)
Is there any way we can easily verify that? Or would we need to have some sort of state smuggled out of the intTestNexusCodecSelector?
There was a problem hiding this comment.
Great point. I updated this workflow to return a struct, and ts.Equal for the final result. Also at the end of the test I look in the History to verify that the encoded data does not match.
| ### Added | ||
|
|
||
| - Added caller-side `converter.NexusSerializationContext` support for Nexus operation inputs, | ||
| results, and failures. Each scheduled operation retains an isolated contextual data and failure |
There was a problem hiding this comment.
How is this supposed to work in the long run?
- This is wired up for Nexus invocations made from workflows, but what about SANOs?
- If the handler-side is supposed to use the same converter, how will this actually work?
I'm just trying to understand the next few steps in getting this all working end-to-end.
There was a problem hiding this comment.
Yes the long term is to have SANO support. This PR starts with Nexus calls from in workflows because the Nexus Context (service, endpoint, operation) are present. Adding SANO support and handler side will require server changes, because that context is not currently returned when a SANO result or failure is polled, or consistently propagated to Temporal entities started by Nexus handlers.
What was changed
Added caller side
converter.NexusSerializationContextsupport for workflow-scheduled Nexus operations.This context gives the Nexus Endpoint, Service, and operation name to the data/failure converters
Why?
This enables codecs to select serialization behavior or encryption keys by Nexus endpoint, service, or operation.
For example, workflows calling two Nexus endpoints can encrypt each endpoint’s payloads with a different key while ensuring that inputs, results, and failures are decoded with the converter selected for the corresponding operation.
Checklist
Closes
How was this tested:
Added unit/functional tests to verify that NexusSerializationContext works as expected.