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

Rework ConnectError to ConnectException #120

Merged
merged 11 commits into from
Oct 3, 2023

Conversation

jzbrooks
Copy link
Contributor

Exceptions that might be caught should not extend throwable. Throwable
is the subtype of both Exception and Error. The latter indicates a
critical failure and should only be caught in very rare circumstances.
This change renames the class and makes the supertype more specific
to suggest that catching this throwable is reasonable.

Before submitting your PR: Please read through the contribution guide at https://github.com/connectrpc/connect-kotlin/blob/main/.github/CONTRIBUTING.md

Note: This is a source-breaking change, which was briefly discussed in slack.

Exceptions that might be caught should not extend throwable. Throwable
is the subtype of both Exception and Error. The latter indicates a
critical failure and should only be caught in very rare circumstances.
This change renames the class and makes the supertype more specific
to suggest that catching this throwable is reasonable.
@CLAassistant
Copy link

CLAassistant commented Sep 28, 2023

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

@pkwarren pkwarren left a comment

Choose a reason for hiding this comment

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

There are several other APIs that we should consider updating to use consistent names everywhere - i.e. connectError() -> connectException().

Since we just made an API change in the last release, I wonder if we shouldn't hold off on changing ConnectError -> ConnectException everywhere so we can accumulate other API changes as well prior to stabilizing the API for a v1. Maybe we split this PR into two - first doing the Throwable -> Exception change and then saving another with a full rename to merge along with other API changes.

library/src/main/kotlin/com/connectrpc/StreamResult.kt Outdated Show resolved Hide resolved
@jzbrooks
Copy link
Contributor Author

jzbrooks commented Sep 28, 2023

Changing the super type isn't a source breaking change, but it does break the API contract because some people might be depending on the super type being Throwable. If we make that breaking change, in my opinion we might as well make the source breaking change to get them all out of the way quickly.

I'll look through a little more carefully for other parts of the API surface that need a name update.

I get the concern with a breaking change (especially successive ones). I understand if we want to leave this open a while. Have y'all considered keeping a changelog in the repo? It might make changes like this one more palatable for early adopters.

Edit: the super type change could be a breaking change in certain generic type bound situations, upon further thought.

Copy link
Contributor

@pkwarren pkwarren left a comment

Choose a reason for hiding this comment

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

A few more spots need to be updated:

  • grpcCompletionToConnectError -> grpcCompletionToConnectException.
  • connectErr -> connectExcception in conformance tests and the Main.kt examples.
  • ServerStreamingResult.error -> exception in the conformance tests.
  • parseConnectUnaryError -> parseConnectUnaryException.

I think we should also change the following for consistency:

  • ResponseMessage.Failure.error -> exception or cause.
  • StreamResult.Complete.error -> exception or cause.
  • HTTPResponse.error -> exception or cause.

In most (3 of 4) uses of this method, the first argument
was known to be non-null at the call site. Since in the
event that the first argument _was_ null, we basically create
a `ConnectException` without much helpful information about
any error, it made sense to me to remove the concept of a nullable
completion from the method entirely.

The function is also a little more idiomatic now, following
the typical `to<Type>OrNull` pattern established by the language for
conversion between types.

It also reduces the classes generated by the compiler since
the method is a proper member of `GRPCCompletion`.

I would probably push this idea a little further in how
error states are modeled more generally, but that's creeping
outside the scope of this change.
@jzbrooks
Copy link
Contributor Author

I think the remaining issues are addressed. I was able to verify the conformance tests compile, but I'm not able to run them. I'll keep an eye on the PR checks when they're run. Thanks!

Note the extended commit message in 4c10702

Copy link
Contributor

@pkwarren pkwarren 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!

* Converts a completion into a [ConnectException] if the completion failed or if a throwable is passed
* @return a ConnectException on failure, null otherwise
*/
fun toConnectExceptionOrNull(serializationStrategy: SerializationStrategy, cause: Throwable? = null): ConnectException? {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice refactoring here.

@pkwarren
Copy link
Contributor

Looks like the lint check is failing - if you run make lintfix and push the changes that should fix it.

Copy link
Contributor

@pkwarren pkwarren left a comment

Choose a reason for hiding this comment

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

Blocking for now until we can address conformance tests and lint warnings.

@jzbrooks
Copy link
Contributor Author

jzbrooks commented Oct 2, 2023

The assertions in the conformance tests don't print the assertion message. This makes debugging via CI difficult. I don't have a docker license, so I can't run them locally.

trailers = trailers,
)
} else {
val exception = ConnectException(Code.UNKNOWN)
Copy link
Contributor

Choose a reason for hiding this comment

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

To resolve the test failure, this else condition should change to:

return@fold result

This way we'll preserve errors like DEADLINE_EXCEEDED.

Copy link
Contributor Author

@jzbrooks jzbrooks Oct 2, 2023

Choose a reason for hiding this comment

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

That whole if-else is an expression, which should resolve to L169 if completion != null and L176 otherwise. Could you elaborate on why this callback needs a non-local return and the others don't? It seems like the expression should bubble up where it needs to go via fold, like the onHeaders and onMessage callbacks.

I'm happy to make that change, but I'm aiming to understand it better first.

Copy link
Contributor

Choose a reason for hiding this comment

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

So result is already instantiated in some cases (with a code like DEADLINE_EXCEEDED and a cause). Some of the tests rely on this. Alternatively to doing return@fold. you could just have result here as the return value.

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'm tracking now. Thanks for the explaination.

)
} else {
val exception = ConnectException(result.code)
StreamResult.Complete(exception.code, exception, trailers)
Copy link
Contributor

Choose a reason for hiding this comment

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

We may be losing an underlying cause here - so we should just return the result here.

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 worked around the problem by using the result's code property value in the connect exception constructor, but I like that idea better.

e83429a

@pkwarren pkwarren merged commit 7b7c746 into connectrpc:main Oct 3, 2023
6 checks passed
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.

3 participants