Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Custom data converter #477

Merged
merged 34 commits into from
Mar 2, 2022
Merged

Conversation

lorensr
Copy link
Contributor

@lorensr lorensr commented Feb 11, 2022

What was changed

Sorry, I should have un-reverted in a subsequent PR so that it wouldn't be included in this PR diff. Here's a diff without that first un-revert commit:

lorensr/sdk-typescript@cc0d414...custom-data-converter

Checklist

  1. Closes [Feature Request] Custom data converters #130

  2. Closes [Feature Request] Protobuf payload converter support #237

  3. How was this tested:

Draft PR: tests aren't passing yet, and I need to write more to cover codec.

  1. Any docs updates needed?

Yep

… WorkerOptions.dataConverterPath (temporalio#430)""

This reverts commit 139550d.
export interface DataConverter {
  payloadConverterPath?: string;
  payloadCodec?: PayloadCodec;
}

export interface PayloadConverter {
  toPayload<T>(value: T): Payload;
  fromPayload<T>(payload: Payload): T;
}

export interface PayloadCodec {
  encode(payloads: Payload[]): Promise<Payload[]>;
  decode(payloads: Payload[]): Promise<Payload[]>;
}
docs/data-converter.md Show resolved Hide resolved
docs/data-converter.md Outdated Show resolved Hide resolved
docs/data-converter.md Show resolved Hide resolved
packages/common/src/codec-helpers.ts Outdated Show resolved Hide resolved
packages/common/src/codec-helpers.ts Outdated Show resolved Hide resolved
packages/workflow-common/package.json Outdated Show resolved Hide resolved
packages/workflow-common/src/converter/data-converter.ts Outdated Show resolved Hide resolved
packages/workflow-common/src/converter/data-converter.ts Outdated Show resolved Hide resolved
"@temporalio/workflow-common": "file:../workflow-common"
},
"devDependencies": {
"@temporalio/proto": "file:../proto"
Copy link
Member

Choose a reason for hiding this comment

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

Is this a dev dependency now? Is it even a dependency anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added as devDep because we import type { coresdk } in a few places in this package

Copy link
Member

Choose a reason for hiding this comment

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

It should be a non dev dependency if we export any of those types or anything that uses those types (e.g. function signatures), to be safe, let's make it non dev.


- Most popular lib (5M downloads/wk)
- Fairly inactive maintainers (infrequent updates, many open PRs & issues)
- Non-standard JSON serialization
Copy link
Member

@cretz cretz Feb 15, 2022

Choose a reason for hiding this comment

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

What makes it non-standard? I expect every message class to have a toJSON method (which I believe is basically toObject + JSON.stringify) and a fromObject which can be used after JSON.parse.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Every class has a toJSON method, but JSON.stringify() doesn't match the spec:

https://developers.google.com/protocol-buffers/docs/proto3#json

Eg stringify throws on BigInts and doesn't base64-encode binary. protobufjs hasn't implemented a number of the types that need custom encoding: protobufjs/protobuf.js#1304

Copy link
Member

Choose a reason for hiding this comment

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

Eg stringify throws on BigInts and doesn't base64-encode binary.

Why use stringify? Doesn't the toJSON directly work with these? Is there an issue to follow?

protobufjs hasn't implemented a number of the types that need custom encoding: protobufjs/protobuf.js#1304

Makes sense. They just need better support for well known types. I wonder if we can contribute? Otherwise, I wonder if we can walk the object for WKT's and convert them ourselves.

Copy link
Member

Choose a reason for hiding this comment

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

The last commit in their repo was in last May, I fear contribution is not a viable option.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why use stringify? Doesn't the toJSON directly work with these? Is there an issue to follow?

I just meant that if toJSON were implemented with JSON.stringify, it wouldn't work. I made this issue: #481

I wonder if we can contribute? Otherwise, I wonder if we can walk the object for WKT's and convert them ourselves.

I'd like to try contributing. If converting ourselves, unless we fork protobufjs, we'd be doing what proto3-json-serializer does, and would have the same requirement on how the user generates & provides their messages.

Copy link
Member

@cretz cretz Feb 15, 2022

Choose a reason for hiding this comment

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

👍 I am not sure it's reasonable to ask people to use our custom protoc. Ideally we can do this from the outside on protobufjs-generated classes/descriptors.

Having said that, this being the only one of our languages without a canonical, supported proto-JSON implementation, I'd support just erroring in data converters if protos are provided in either direction, but allow them to use a custom converter to pick their preferred conversion (or binary only, but we should still error on from-payload when we receive json/protobuf).

Copy link
Contributor Author

@lorensr lorensr Feb 15, 2022

Choose a reason for hiding this comment

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

to ask people to use our custom protoc

We're still having them use the protobufjs generator (pbjs). It may not be the way they're currently running it. If not, I think it's straightforward to switch or to do both—our way for temporal code and their way for the rest of their code. The main difference in use is doing Message.create() instead of new Message().

If we think running the generator our way is too big an ask for some users, we could add support for just binary/protobuf for them, without removing current functionality.

the only one of our languages without a canonical, supported proto-JSON implementation

I think the library we're using is well-maintained and from Google/canonical: https://github.com/googleapis/proto3-json-serializer-nodejs

Copy link
Member

Choose a reason for hiding this comment

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

The main difference in use is doing Message.create() instead of new Message().

Can you document why in one of your docs? Is this a limitation of proto3-json-serializer?

If we think running the generator our way is too big an ask for some user

I don't think it's too big of an ask, just offering the alternative of having json/protobuf be opt-in instead of by default if it's too much trouble.

I think the library we're using is well-maintained and from Google/canonical: https://github.com/googleapis/proto3-json-serializer-nodejs

Sorry, to clarify, there's a reason that JS isn't listed at https://developers.google.com/protocol-buffers/docs/tutorials or in official docs. But I agree that JSON serializer works if we document how to use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you document why in one of your docs? Is this a limitation of proto3-json-serializer?

Yes. Limitation of this way of running pbjs.

@lorensr
Copy link
Contributor Author

lorensr commented Mar 1, 2022

Addressed review comments and:

  • Fixed and added tests
  • Stopped encoding/decoding headers (pending further discussion, will probably re-enable and add to otel in future PR)
  • Restructured common packages to:
common
internal-workflow-common
internal-non-workflow-common

where the first exports things you might want to use in your own common code (shared between client/worker/workflow) like data converters, failures, and errors.

@lorensr lorensr marked this pull request as ready for review March 1, 2022 04:19
@lorensr lorensr requested a review from bergundy March 1, 2022 05:46
Copy link
Member

@Sushisource Sushisource left a comment

Choose a reason for hiding this comment

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

Like this new design! Didn't look to much into gritty details but just some comments on high-level stuff.

packages/common/src/converter/payload-codec.ts Outdated Show resolved Hide resolved
packages/common/src/converter/payload-converter.ts Outdated Show resolved Hide resolved
packages/worker/src/workflow-codec-runner.ts Show resolved Hide resolved
Copy link
Member

@bergundy bergundy left a comment

Choose a reason for hiding this comment

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

Didn't have many comments, most of the critical ones I communicated on slack.
I want to try to use TS type checking to ensure that we always encode and decode payloads, but it doesn't block this PR.

I'm approving, this is good to merge as soon as you go over the comments.

docs/data-converter.md Outdated Show resolved Hide resolved
packages/client/src/workflow-client.ts Outdated Show resolved Hide resolved
packages/common/src/converter/data-converter.ts Outdated Show resolved Hide resolved
packages/common/src/converter/payload-converter.ts Outdated Show resolved Hide resolved
packages/test/src/test-custom-payload-codec.ts Outdated Show resolved Hide resolved
};
await Promise.all([worker.run(), runAndShutdown()]);
t.is(workflowLogs[0], 'encoded');
t.is(activityLogs[0], 'Activityencodedencoded');
Copy link
Member

Choose a reason for hiding this comment

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

nit: You can probably test this without the logger if you use a single codec for both encoding and decoding.

packages/worker/src/worker.ts Outdated Show resolved Hide resolved
"@temporalio/workflow-common": "file:../workflow-common"
},
"devDependencies": {
"@temporalio/proto": "file:../proto"
Copy link
Member

Choose a reason for hiding this comment

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

It should be a non dev dependency if we export any of those types or anything that uses those types (e.g. function signatures), to be safe, let's make it non dev.

packages/workflow/src/worker-interface.ts Outdated Show resolved Hide resolved
docs/data-converter.md Outdated Show resolved Hide resolved
packages/common/src/converter/payload-converter.ts Outdated Show resolved Hide resolved
packages/test/src/mock-native-worker.ts Outdated Show resolved Hide resolved
@lorensr lorensr merged commit 82351cc into temporalio:main Mar 2, 2022
@lorensr
Copy link
Contributor Author

lorensr commented Mar 2, 2022

I'll update sdk-features

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.

[Feature Request] Protobuf payload converter support [Feature Request] Custom data converters
4 participants