Skip to content

Interops#283

Open
maxdml wants to merge 29 commits intomainfrom
interops
Open

Interops#283
maxdml wants to merge 29 commits intomainfrom
interops

Conversation

@maxdml
Copy link
Copy Markdown
Collaborator

@maxdml maxdml commented Mar 25, 2026

Add the ability to mark a workflow "portable", in which case it uses our interop format to decode its inputs, and use plain JSON encoding/decoding for other data. This supports:

  • Enqueueing from Go to another language
  • Dequeueing or recovering a portable workflow with inputs received from another language
  • Direct call of a portable RunWorkflow (which can thus be recovered/resumed on a different language.)

Client

The client can do a normal enqueue with a special type to enqueue a portable workflow:

args := dbos.PortableWorkflowArgs{
    PositionalArgs: []any{"hello", 42},
    NamedArgs:      map[string]any{"key": "value"},
}
handle, err := dbos.Enqueue[dbos.PortableWorkflowArgs, any](client, "queue", "py_workflow", args)

Dequeue, recovery

The code automatically flags a workflow as "portable" when detecting the portable fomat from the DB.

Direct Call

Call RunWorkflow with WithPortableWorkflow.

Comment on lines +153 to 180
// typedCustomSerializerAdapter wraps a user-provided Serializer[any] into Serializer[T],
// handling the type assertion on decode.
type typedCustomSerializerAdapter[T any] struct {
inner Serializer[any]
}

func (a *typedCustomSerializerAdapter[T]) Name() string {
return a.inner.Name()
}

func (a *typedCustomSerializerAdapter[T]) Encode(data T) (*string, error) {
return a.inner.Encode(data)
}

func (a *typedCustomSerializerAdapter[T]) Decode(data *string) (T, error) {
decoded, err := a.inner.Decode(data)
if err != nil {
return *new(T), err
}
return newJSONSerializer[any]().Encode(data)
if decoded == nil {
return getNilOrZeroValue[T](), nil
}
typed, ok := decoded.(T)
if !ok {
return *new(T), fmt.Errorf("custom serializer returned %T, expected %T", decoded, *new(T))
}
return typed, nil
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Simple helper for custom serializers that performs type-assert on the user-provided serializer's output (which is any)

Comment on lines +1514 to +1517
type awaitWorkflowResultOutput struct {
output *string
serialization string
}
Copy link
Copy Markdown
Collaborator Author

@maxdml maxdml Mar 25, 2026

Choose a reason for hiding this comment

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

here and for recv and getEvent, we need to funnel to the typed, package methods, the serializer name, for decoding.

err: nil,
startedAt: startTime,
completedAt: completedTime,
serialization: "DBOS_JSON",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

sleep always use our default serializer, no custom/portable.

@maxdml maxdml marked this pull request as ready for review March 25, 2026 22:56
Base automatically changed from custom-serializers to main March 26, 2026 00:33
Copy link
Copy Markdown
Member

@kraftp kraftp left a comment

Choose a reason for hiding this comment

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

Can we add the PL/plSQL client (just copy-paste its migration) and tests for it? That would go a long way towards testing interoperability.

More broadly, is it possible to include in the repo a test involving at least one other language, likely Python or TS? The simplest thing to do would be a TS client for a Go application. With modern Node, this is very easy, you just need a short TS script and a package.json, you don't even need to compile the TS.

@@ -303,7 +329,15 @@ func (c *client) Send(destinationID string, message any, topic string) error {

// GetEvent retrieves a key-value event from a target workflow.
func (c *client) GetEvent(targetWorkflowID, key string, timeout time.Duration) (any, error) {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

pre-existing issue that I'll fix in another PR: #284

}
b, jsonErr := json.Marshal(errData)
if jsonErr != nil {
return err.Error() // fallback to plain string
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's ok to have this fallback, rather than adding complex error handling to the error handling

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.

2 participants