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

Add support for custom failure converters #924

Merged

Conversation

Quinn-With-Two-Ns
Copy link
Contributor

Adds an interface called FailureConverter that allows users to customize failure serialization and deserialization.

What was changed

Adds a FailureConverter interface to allow users to customize how errors are serialized and deserialized.

See also:
temporalio/sdk-typescript#887
https://github.com/temporalio/api/blob/master/temporal/api/failure/v1/message.proto#L81

Checklist

  1. Closes

  2. How was this tested:

  1. Any docs updates needed?

Adds an interface called `FailureConverter` that allows users to
customize failure serialization and deserialization.
@Quinn-With-Two-Ns Quinn-With-Two-Ns requested a review from a team as a code owner September 29, 2022 20:47
@CLAassistant
Copy link

CLAassistant commented Sep 29, 2022

CLA assistant check
All committers have signed the CLA.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

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.

I didn't look at the implementation, leaving that to @cretz.
The API LGTM.

import failurepb "go.temporal.io/api/failure/v1"

type (
FailureConverter interface {
Copy link
Member

Choose a reason for hiding this comment

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

All exported types need godoc.

Also, don't have to put this in a type ( block if you'd rather just type FailureConverter interface {. Consistency for this isn't that important.

Comment on lines 398 to 399
// default: defaultFailureConverter, does not encode any fields of the error. See `temporal/default_failure_converter` for
// options to configure or create a custom converter.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// default: defaultFailureConverter, does not encode any fields of the error. See `temporal/default_failure_converter` for
// options to configure or create a custom converter.
// default: temporal.DefaultFailureConverter, does not encode any fields of the error. Use temporal.NewDefaultFailureConverter
// to configure or create a custom converter.
  • Reference the type name
  • Ask people to read Godoc for doc on how to do things instead of code (and if we're missing Godoc, can add it)
  • Don't use markdown-style formatting in Godoc

internal/default_failure_converter.go Outdated Show resolved Hide resolved
internal/default_failure_converter.go Outdated Show resolved Hide resolved
internal/failure_converter.go Outdated Show resolved Hide resolved
Comment on lines 261 to 266
err := dfc.dataConverter.FromPayload(failure.GetEncodedAttributes(), &ea)
if err != nil {
return nil
}
failure.Message = ea.Message
failure.StackTrace = ea.StackTrace
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
err := dfc.dataConverter.FromPayload(failure.GetEncodedAttributes(), &ea)
if err != nil {
return nil
}
failure.Message = ea.Message
failure.StackTrace = ea.StackTrace
if dfc.dataConverter.FromPayload(failure.GetEncodedAttributes(), &ea) == nil {
failure.Message = ea.Message
failure.StackTrace = ea.StackTrace
}

If we can't decode the attributes into that type we should probably continue with our work. They may have encoded their own attributes.

failure.FailureInfo = &failurepb.Failure_ApplicationFailureInfo{ApplicationFailureInfo: failureInfo}
}

failure.Cause = InternalErrorToFailure(errors.Unwrap(err), dc)
Copy link
Member

@cretz cretz Sep 30, 2022

Choose a reason for hiding this comment

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

This does not properly encode nested attributes.

Instead of walking the causes after this is done and to avoid these mistakes in the codebase in general, lets get rid of these two top-level functions and put them inside the methods of the default converter itself. If someone wants to call these from their converter, they can call GetDefaultFailureConverter().ErrorToFailure or GetDefaultFailureConverter().FailureToError to get default behavior.

(we don't have to make custom converters work for nested errors, so long as they are called for the root one, custom converters can do whatever recursive work they choose to)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah good catch, i'll refactor

internal/internal_worker.go Outdated Show resolved Hide resolved
@@ -79,14 +79,14 @@ type (
ContinueAsNewError = internal.ContinueAsNewError
)

// ConvertErrorToFailure converts Go error to the correspondent Failure protobuf.
Copy link
Member

@cretz cretz Sep 30, 2022

Choose a reason for hiding this comment

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

This internalbindings package is here to support https://github.com/temporalio/roadrunner-temporal I believe. I think we can 1) remove all error conversion APIs here, and 2) open an issue over there pointing to this PR on how to use the now-public default error conversion API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

temporal/default_failure_converter.go Show resolved Hide resolved
Copy link
Member

@cretz cretz left a comment

Choose a reason for hiding this comment

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

Looks great! If/when CI passes, merge whenever you'd like (or make that minor change if you want).

Don't forget to give https://github.com/temporalio/roadrunner-temporal a heads up on the removal of their internalbindings call at least via an issue.

@@ -109,7 +110,7 @@ func (d *SingleActivityWorkflowDefinition) Execute(env bindings.WorkflowEnvironm
env.Complete(nil, errors.New("error expected"))
return
}
failure := bindings.ConvertErrorToFailure(err, converter.GetDefaultDataConverter())
failure := internal.GetDefaultFailureConverter().ErrorToFailure(err)
Copy link
Member

@cretz cretz Sep 30, 2022

Choose a reason for hiding this comment

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

Suggested change
failure := internal.GetDefaultFailureConverter().ErrorToFailure(err)
failure := temporal.GetDefaultFailureConverter().ErrorToFailure(err)

No biggie though

@Quinn-With-Two-Ns
Copy link
Contributor Author

Looks great! If/when CI passes, merge whenever you'd like (or make that minor change if you want).

Don't forget to give https://github.com/temporalio/roadrunner-temporal a heads up on the removal of their internalbindings call at least via an issue.

Thanks, yep I made a issue over there temporalio/roadrunner-temporal#277

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