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

Unify remaining std::error_codes into Status/ErrorCodes in sync client #6869

Merged
merged 37 commits into from
Aug 11, 2023

Conversation

jbreams
Copy link
Contributor

@jbreams jbreams commented Aug 8, 2023

What, How & Why?

This is the last step of the big error unification refactor. It moves translation of ProtocolError codes into realm ErrorCodes into the sync client. It removes std::error_code from Status and friends. It adjusts tests that were depending on old std::error_code values.

☑️ ToDos

  • 📝 Changelog update
  • 🚦 Tests (or not relevant)
  • C-API, if public C++ API changed.

@jbreams jbreams changed the title Jbr/sync error last steps Unify remaining std::error_codes into Status/ErrorCodes in sync client Aug 9, 2023
@jbreams jbreams marked this pull request as ready for review August 9, 2023 13:31
@@ -485,8 +485,14 @@ static inline realm_version_id_t to_capi(const VersionID& v)
return version_id;
}

realm_sync_error_code_t to_capi(const Status& status, std::string& message);
void sync_error_to_error_code(const realm_sync_error_code_t& sync_error_code, std::error_code* error_code_out);
static inline realm_error_t to_capi(const Status s)
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: keep the const&

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

util::Optional<ShouldBackup> delete_file;
bool log_out_user = false;
bool unrecognized_by_client = false;

// Can be called from within SyncSession with an AuthError to handle app-level authentication problems,
// so even if there's an error action, we need to set a fall-back to logging out the user and making
Copy link
Collaborator

Choose a reason for hiding this comment

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

when there is no error action (i.e, a synthetized error), we'll set unrecognized_by_client to true. I don't think we want that. This behavior is different than before.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whoops. I've actually mis-understood this code flow a bit and I've changed this so that if you want this behavior you need to pass in an action of LogOutUser

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

}

std::error_code get_std_error_code() const
Status(ErrorCodes::Error code, std::string&& reason)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want/need both constructors?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, so the first constructor makes a copy of a string. This constructor is if you've just created a Status with a new string via util::format() or something. So this is just a little optimization to avoid an allocation/copy.

SessionErrorInfo(const ProtocolErrorInfo& info, Status status)
: ProtocolErrorInfo(info)
, status(std::move(status))
{
}

SessionErrorInfo(Status status, IsFatal is_fatal)
: ProtocolErrorInfo(status.get_std_error_code().value(), status.reason(), is_fatal)
: ProtocolErrorInfo(0, {}, is_fatal)
Copy link
Collaborator

Choose a reason for hiding this comment

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

shouldn't we pass the info from status to the ProtocolErrorInfo constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So this is the case where if you are constructing a SessionErrorInfo for something besides a protocol error. I wasn't sure what the right thing to do here is/was, but I settled on leaving the ProtocolErrorInfo portion of the class in an invalid state rather than trying to reverse-translate Status's to ProtocolErrors.

return {ErrorCodes::SyncProtocolInvariantFailed,
util::format("Received ERROR message with unknown error code %1", info.raw_error_code)};
auto status = protocol_error_to_status(static_cast<ProtocolError>(info.raw_error_code), info.message);
// If the server has requested an action but sent an invalid error code we can still process the error and pass an
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the assumption is that the server always sends an action (cc @tkaye407), so maybe an assert is more appropriate.

Copy link
Contributor

Choose a reason for hiding this comment

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

The server should always send an action, yes

Copy link
Contributor

Choose a reason for hiding this comment

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

Addendum: we will only do it for realm devices that know about the JSONErrorMessage, but I dont think that matters in this case

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 going to fix this in a separate PR.

if (status == ErrorCodes::UnknownError && info.server_requests_action != ProtocolErrorInfo::Action::NoAction) {
return {
ErrorCodes::SyncProtocolInvariantFailed,
util::format("Received ERROR message with unknown error code %1 and no action.", info.raw_error_code)};
Copy link
Collaborator

Choose a reason for hiding this comment

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

an action is received in this case

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, this logic is wrong I've undone it and will fix it up in a separate review.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'll take that you'll update this message as well in that pr.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes

}
if (user) {
user->log_out();
}

if (auto error_handler = config(&SyncConfig::error_handler)) {
auto user_facing_error =
SyncError(Status{realm::sync::ProtocolError::bad_authentication, context_message}, true);
auto user_facing_error = SyncError({ErrorCodes::AuthError, status.reason()}, true);
Copy link
Collaborator

Choose a reason for hiding this comment

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

you need to set the correct action (LogOutUser).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So my confusion here is that this is not getting passed to handle_error(), it's getting passed to error_handler and the action actually has no effect. I can set it here for completeness, but it's not going to get used.

Copy link
Collaborator

Choose a reason for hiding this comment

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

ah, I see. It's calling the user callback directly.

Copy link
Collaborator

Choose a reason for hiding this comment

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

is it the right thing though? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure what you mean? What else would we do here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

so I was wondering if we should call handle_error() instead, so we actually log out the user.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But we log out the user right above on line 248

Copy link
Collaborator

Choose a reason for hiding this comment

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

I completely missed that. I think the only difference (from being handled in handle_error()) then is that the session may remain active. But it's the same as before so maybe not an issue.

Copy link
Contributor

Choose a reason for hiding this comment

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

The session should already be closed at this point: this path is called when a SyncSession is being revived and will never become_active() on a bad auth or if the websocket connection failed for bad auth or redirect during open.

@@ -364,34 +365,17 @@ enum class ProtocolError {
// clang-format on
};

Status protocol_error_to_status(ProtocolError raw_error_code, std::string_view msg);
Copy link
Collaborator

Choose a reason for hiding this comment

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

you have this declared at line 377.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed. I think this was a merge issue 😞

case ProtocolError::client_file_expired:
[[fallthrough]];
case ProtocolError::bad_client_file:
return ErrorCodes::SyncClientResetRequired;
Copy link
Collaborator

Choose a reason for hiding this comment

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

there are probably other cases when a client reset is required (e.g, migrate_to_flx). what is this used for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are error codes that specifically always mean that you should client reset. migrate_to_flx has the error code WrongSyncType.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

@michael-wb michael-wb left a comment

Choose a reason for hiding this comment

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

Looks good - there's a lot of good changes (and a lot of work) in the error unification project.
Two tiny comments

@@ -712,8 +712,7 @@ class MultiClientServerFixture {
return;
REALM_ASSERT(error);
unit_test::TestContext& test_context = m_test_context;
test_context.logger->error("Client disconnect: %1: %2 (is_fatal=%3)",
error->status.get_std_error_code(), error->message, error->is_fatal);
test_context.logger->error("Client disconnect: %1 (is_fatal=%3)", error->status, error->is_fatal);
Copy link
Contributor

Choose a reason for hiding this comment

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

is_fatal index is wrong

// sync websocket is closed with specific error codes.
RefreshUser,
RefreshLocation,
LogOutUser,
};

ProtocolErrorInfo() = default;
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't related to your changes, but should we explicitly set the default value for server_requests_action to be Action::NoAction - the compiler will use whatever value is at 0, whatever it is...

@jbreams jbreams merged commit 1bd0a8d into feature/sync_error_unification Aug 11, 2023
2 checks passed
@jbreams jbreams deleted the jbr/sync_error_last_steps branch August 11, 2023 14:20
jbreams added a commit that referenced this pull request Aug 11, 2023
* Add new ErrorCodes for sync error unification (#6829)

* Make SyncError/SessionErrorInfo Status-aware (#6824)

* Replace ClientError error_code with ErrorCodes/Status (#6846)

* Handle websocket errors entirely within sync client (#6859)

* Unify remaining std::error_codes into Status/ErrorCodes in sync client (#6869)

* fix changelog merge with master
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants