-
Notifications
You must be signed in to change notification settings - Fork 110
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
errors: refactor error types on a connection level #1067
Conversation
See the following report for details: cargo semver-checks output
|
1012a92
to
fc0b4fe
Compare
fc0b4fe
to
c31a520
Compare
Rebased on main |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: it looks like you are not using non_exhaustive on new error types?
Did you decide that types introduced in this PR don't need it or is there some other reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR made me feel like being told a breath-taking story of fighting the evil QueryError
that has infected our codebase 😄
Amazing commit separation and messages, the review was a very smooth experience.
v1.1: |
2a491f6
to
c63e415
Compare
v2: Addressed most of review comments. |
c63e415
to
c0e8e05
Compare
v2.1: rebased on main |
c0e8e05
to
93234d8
Compare
v2.2: replaced |
Previously, all functions related to selecting a connection, would return a QueryError::IoError. There is no need to return such broad error type as QueryError, when only std::io::Error can appear.
Receiving a non-event response on a -1 stream should be considered an error.
93234d8
to
8282d0a
Compare
Rebased on main. |
8282d0a
to
7776ff1
Compare
v2.3: introduced |
Add an enum representing possible CQL requests. This enum is useful for creating strongly typed error types (e.g. ConnectionSetupRequestError introduced in a following commit).
Introduced new error type designed to be used for the requests performed during connection setup - e.g. OPTIONS or STARTUP. In this commit, we also adjust Connection::get_options method. It will now extract the supported options, and return appropriate error in case of failure.
Now error filtering happens in Connection::startup method. Introduced NonErrorStartupResponse to represent one of two possible non-error responses to STARTUP request.
Since we tidied up the error types for connection setup requests, so now they return the ConnectionSetupRequestError, we can remove the QueryError dependency from ConnectionError.
This is an error that appears case of failure of user request (requests triggered via public API, i.e. PREPARE, EXECUTE, QUERY, BATCH). Its only purpose is to filter out impossible error variants from `CqlResponseParseError` that appear in `RequestError` type. For example, it should not be possible to receive `CqlSupportedParseError` when parsing the response to user request. It means that the server sent an invalid response to the corresponding request. The From<UserRequestError> implementation will be adjusted in a following commits. Firstly, we need to narrow the type of corresponding functions and methods to UserRequestError.
As commit title states, this commit implements a transition from RequestError to UserRequestError. The only accepted responses for user requests are RESULT and ERROR. If we started deserialization of some other response, but failed, we will treat it as unexpected response error.
It just narrows the error type from QueryError to UserRequestError
Unfortunately, I didn't know how to decouple this commit into two separate commits. This is due to the trait bounds on e.g. RowIteratorWorker::work implementation.
Narrowed return error type of functions that convert QueryResponse into NonErrorQueryResponse and QueryResult.
Ultimately, we want to get rid of From<RequestError> for QueryError implementation. This is because, RequestError contains a CqlResponseParseError which is overloaded with variants such as CqlAuthChallengeParseError which should not be returned to the user who uses only BATCH, QUERY, EXECUTE and PREPARE requests. This is why, we make two transitions in error types in this place. The first transition is RequestError -> UserRequestError (map_err()), which filters out variants such as CqlAuthChallengeParseError, and leaves only either CqlErrorParseError or CqlResultParseError. The second transition (? operator) is UserRequestError -> QueryError which makes use of From<UserRequestError> for QueryError implementation.
Since we introduced a transition error type for user requests (UserRequestError), and adjusted the code to it, we can now revert the temporary From implementation.
Since requests triggered via user API, cannot receive a response other than ERROR or RESULT, we need to narrow the response parsing errors accordingly. Before this commit, we would hold a CqlResponseParseError which contains a parsing errors for responses that should never be returned by the server for user requests. After hard work from previous commits, we can finally replace this overloaded variant with two variants - CqlResultParseError -> failed to deserialize RESULT response - CqlErrorParseError -> failed to deserialize ERROR response In case of failure of parsing other response, we will return a QueryError::ProtocolError saying that server returned an unexpected response. I think we should add more context to this error, but let's leave it for other PR which will be focused on ProtocolError refactor.
In a following commit, we will introduce a ConnectionPoolError, which will contain a variant with a ConnectionError (the error registered for last connection). At the place of creating this variant, we only have access to the reference of ConnectionError. Thus, to make it owned, cloning is required.
Introduced a ConnectionPoolError which appears when we were unable to select a connection from the connection pool.
This conversion is no longer used, as IO errors are already handled by transition error types.
Appended `RESULT:` prefix to RESULT response types.
f1eab36
to
9259dea
Compare
I opened a follow-up PR that moves error types to scylla crate and adjusts their pub-visibility: #1074 |
Looks fine to me now, but there are still open comments from @wprzytula. Please go over them and mark the fixed ones as resolved. |
Done. The comments were regarding pub-visibility of error types. This issue is addressed in a follow-up PR. |
Ref: #519
Motivation
My main motivation for this PR, was the @Lorak-mmk comment, that currently the
QueryError
contains aCqlResponseParseError
.CqlResponseParseError
contains variants which represent a parsing errors which SHOULD NOT appear when executing requests via user API e.g.CqlAuthChallengeParseError
, which can only happen during connection setup (unless a protocol error appeared, and server sent us an incorrect response, with an incorrect type).Since
QueryError
is abused everywhere, a lot of changes needed to be introduced before getting rid of the variants mentioned above. However, there are some positives, since we could address some other issues as well.BrokenConnectionError
One of the main entry points for a connection is a
Connection::router
function, which starts 4 main async tasks (reader, writer, keepaliver, orphaner). With current driver's design, if one of them fails, the whole connection is treated as broken, and closed. Before this PR, all of these tasks would return a QueryError, which is a really broad type destined for the users. It contains serialization errors, which cannot appear during connection's async tasks work. This is why I introduced aBrokenConnectionError
type, returned if one of these 4 tasks fail.In this PR, we perform an error transition refactor in two places:
PoolRefiller
<->Connection
layerSession
(user) <->Connection
layer (addresses the issue mentioned in PR's motivation)PoolRefiller <-> Connection
There are some errors that can appear on a connection level which will not reach user's end. They are handled and logged by PoolRefiller of a corresponding node. This is why, it's a great place to introduce a new error type destined specifically for this case -
ConnectionError
.The most important variant of this error is
ConnectionSetupRequestError
(with the type of the same name). Once again, theCqlResponseParseError
contains some variants which are not applicable here - e.g.CqlResultParseError
. I added more context for the errors that can appear during connection setup (STARTUP, OPTIONS, AUTH requests).Session <-> Connection
This is completely analogous to the above transition. We need to filter out some errors types which are not applicable in this case, and add some context to the errors. The most important error type here is
UserRequestError
which corresponds to the errors that can appear when executing one of PREPARE, QUERY, EXECUTE or BATCH requests.Transition error types
To achieve all the things listed above, we also needed to introduce some driver's internal transition error types, such as
RequestError
orResponseParseError
They were created only to narrow the error types of some driver's internal functions.Pre-review checklist
[ ] I have adjusted the documentation in./docs/source/
.Fixes:
annotations to PR description.