Skip to content

Commit

Permalink
Sync client error migration to Status from std::error_code (#6882)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jbreams authored Aug 11, 2023
1 parent 59e874a commit d89a330
Show file tree
Hide file tree
Showing 48 changed files with 1,096 additions and 1,590 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
* None.

### Breaking changes
* None.
* The `WebSocketObserver` interface in the sync `SocketProvider` API now takes a `WebSocketError` enum/`std::string_view` for the `websocket_closed_handler()` instead of a `Status`. Implementers of platform networking should make sure all their error handling is expressed in terms of the WebSocketError enum. ([PR #6859](https://github.com/realm/realm-core/pull/6859))
* `Status` no longer holds a `std::error_code` for `SystemError`'s ([PR #6869](https://github.com/realm/realm-core/pull/6869))
* C API no longer has a special type for sync error codes. Instead sync errors codes are converted to `realm_error_t` ([PR #6869](https://github.com/realm/realm-core/pull/6869))
* WebSocket specific error codes are no longer in the ErrorCodes enum or C API. ([PR #6869](https://github.com/realm/realm-core/pull/6869))
* `ProtocolError` is no longer a `std::error_code` enum and is no longer directly exposed by the sync error API ([PR #6869](https://github.com/realm/realm-core/pull/6869))
* The ClientError enum/`std::error_code` in the sync client has been removed in favor of a simplified error set using Status/ErrorCodes ([PR #6846](https://github.com/realm/realm-core/pull/6846)).
* SyncError now contains a Status to hold the error information from the sync client instead of a `std::error_code`/`std::string` ([PR #6824](https://github.com/realm/realm-core/pull/6824)).

### Compatibility
* Fileformat: Generates files with format v23. Reads and automatically upgrade from fileformat v5.
Expand Down
50 changes: 9 additions & 41 deletions src/realm.h
Original file line number Diff line number Diff line change
Expand Up @@ -3334,26 +3334,6 @@ typedef enum realm_sync_progress_direction {
RLM_SYNC_PROGRESS_DIRECTION_DOWNLOAD,
} realm_sync_progress_direction_e;

/**
* Possible error categories realm_sync_error_code_t can fall in.
*/
typedef enum realm_sync_error_category {
RLM_SYNC_ERROR_CATEGORY_CLIENT,
RLM_SYNC_ERROR_CATEGORY_CONNECTION,
RLM_SYNC_ERROR_CATEGORY_SESSION,
RLM_SYNC_ERROR_CATEGORY_WEBSOCKET,

/**
* System error - POSIX errno, Win32 HRESULT, etc.
*/
RLM_SYNC_ERROR_CATEGORY_SYSTEM,

/**
* Unknown source of error.
*/
RLM_SYNC_ERROR_CATEGORY_UNKNOWN,
} realm_sync_error_category_e;

typedef enum realm_sync_error_action {
RLM_SYNC_ERROR_ACTION_NO_ACTION,
RLM_SYNC_ERROR_ACTION_PROTOCOL_VIOLATION,
Expand All @@ -3370,17 +3350,6 @@ typedef enum realm_sync_error_action {
typedef struct realm_sync_session realm_sync_session_t;
typedef struct realm_async_open_task realm_async_open_task_t;

// This type should never be returned from a function.
// It's only meant as an asynchronous callback argument.
// Pointers to this struct and its pointer members are only valid inside the scope
// of the callback they were passed to.
typedef struct realm_sync_error_code {
realm_sync_error_category_e category;
int value;
const char* message;
const char* category_name;
} realm_sync_error_code_t;

typedef struct realm_sync_error_user_info {
const char* key;
const char* value;
Expand All @@ -3397,8 +3366,7 @@ typedef struct realm_sync_error_compensating_write_info {
// Pointers to this struct and its pointer members are only valid inside the scope
// of the callback they were passed to.
typedef struct realm_sync_error {
realm_sync_error_code_t error_code;
const char* detailed_message;
realm_error_t status;
const char* c_original_file_path_key;
const char* c_recovery_file_path_key;
bool is_fatal;
Expand All @@ -3422,7 +3390,7 @@ typedef struct realm_sync_error {
*
* @param error Null, if the operation completed successfully.
*/
typedef void (*realm_sync_wait_for_completion_func_t)(realm_userdata_t userdata, realm_sync_error_code_t* error);
typedef void (*realm_sync_wait_for_completion_func_t)(realm_userdata_t userdata, realm_error_t* error);
typedef void (*realm_sync_connection_state_changed_func_t)(realm_userdata_t userdata,
realm_sync_connection_state_e old_state,
realm_sync_connection_state_e new_state);
Expand Down Expand Up @@ -3864,13 +3832,13 @@ RLM_API void realm_sync_session_wait_for_upload_completion(realm_sync_session_t*
/**
* Wrapper for SyncSession::OnlyForTesting::handle_error. This routine should be used only for testing.
* @param session ptr to a valid sync session
* @param error_code error code to simulate
* @param category category of the error to simulate
* @param error_message string representing the error
* @param error_code realm_errno_e representing the error to simulate
* @param error_str error message to be included with Status
* @param is_fatal boolean to signal if the error is fatal or not
*/
RLM_API void realm_sync_session_handle_error_for_testing(const realm_sync_session_t* session, int error_code,
int category, const char* error_message, bool is_fatal);
RLM_API void realm_sync_session_handle_error_for_testing(const realm_sync_session_t* session,
realm_errno_e error_code, const char* error_str,
bool is_fatal);

/**
* In case of exception thrown in user code callbacks, this api will allow the sdk to store the user code exception
Expand Down Expand Up @@ -4106,8 +4074,8 @@ RLM_API realm_sync_socket_t* realm_sync_socket_new(
realm_sync_socket_websocket_async_write_func_t websocket_write_func,
realm_sync_socket_websocket_free_func_t websocket_free_func);

RLM_API void realm_sync_socket_callback_complete(realm_sync_socket_callback_t* realm_callback,
realm_web_socket_errno_e status, const char* reason);
RLM_API void realm_sync_socket_callback_complete(realm_sync_socket_callback_t* realm_callback, realm_errno_e status,
const char* reason);

RLM_API void realm_sync_socket_websocket_connected(realm_websocket_observer_t* realm_websocket_observer,
const char* protocol);
Expand Down
40 changes: 32 additions & 8 deletions src/realm/error_codes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ ErrorCategory ErrorCodes::error_categories(Error code)
case SubscriptionFailed:
case UnsupportedFileFormatVersion:
case OperationAborted:
case AutoClientResetFailed:
case ConnectionClosed:
case SyncClientResetRequired:
case SyncCompensatingWrite:
case SyncConnectFailed:
case SyncPermissionDenied:
case SyncProtocolInvariantFailed:
case SyncProtocolNegotiationFailed:
case SyncServerPermissionsChanged:
case SyncUserMismatch:
case TlsHandshakeFailed:
case SyncWriteNotAllowed:
return ErrorCategory().set(ErrorCategory::runtime_error);

case DecryptionFailed:
Expand Down Expand Up @@ -116,6 +128,10 @@ ErrorCategory ErrorCodes::error_categories(Error code)
case TopLevelObject:
case TypeMismatch:
case UnexpectedPrimaryKey:
case BadSyncPartitionValue:
case InvalidSubscriptionQuery:
case SyncInvalidSchemaChange:
case WrongSyncType:
return ErrorCategory().set(ErrorCategory::invalid_argument).set(ErrorCategory::logic_error);

case CustomError:
Expand Down Expand Up @@ -209,11 +225,6 @@ ErrorCategory ErrorCodes::error_categories(Error code)
.set(ErrorCategory::app_error)
.set(ErrorCategory::service_error);

case WebSocketResolveFailedError:
case WebSocketConnectionClosedClientError:
case WebSocketConnectionClosedServerError:
return ErrorCategory().set(ErrorCategory::runtime_error).set(ErrorCategory::websocket_error);

case UnknownError:
break;
}
Expand All @@ -240,10 +251,12 @@ static const MapElem string_to_error_code[] = {
{"AuthProviderAlreadyExists", ErrorCodes::AuthProviderAlreadyExists},
{"AuthProviderDuplicateName", ErrorCodes::AuthProviderDuplicateName},
{"AuthProviderNotFound", ErrorCodes::AuthProviderNotFound},
{"AutoClientResetFailed", ErrorCodes::AutoClientResetFailed},
{"BadBsonParse", ErrorCodes::BadBsonParse},
{"BadChangeset", ErrorCodes::BadChangeset},
{"BadRequest", ErrorCodes::BadRequest},
{"BadServerUrl", ErrorCodes::BadServerUrl},
{"BadSyncPartitionValue", ErrorCodes::BadSyncPartitionValue},
{"BadToken", ErrorCodes::BadToken},
{"BadVersion", ErrorCodes::BadVersion},
{"BrokenInvariant", ErrorCodes::BrokenInvariant},
Expand All @@ -255,6 +268,7 @@ static const MapElem string_to_error_code[] = {
{"ClientUserNotFound", ErrorCodes::ClientUserNotFound},
{"ClientUserNotLoggedIn", ErrorCodes::ClientUserNotLoggedIn},
{"ClosedRealm", ErrorCodes::ClosedRealm},
{"ConnectionClosed", ErrorCodes::ConnectionClosed},
{"CrossTableLinkTarget", ErrorCodes::CrossTableLinkTarget},
{"CustomError", ErrorCodes::CustomError},
{"DecryptionFailed", ErrorCodes::DecryptionFailed},
Expand Down Expand Up @@ -299,6 +313,7 @@ static const MapElem string_to_error_code[] = {
{"InvalidServerResponse", ErrorCodes::InvalidServerResponse},
{"InvalidSession", ErrorCodes::InvalidSession},
{"InvalidSortDescriptor", ErrorCodes::InvalidSortDescriptor},
{"InvalidSubscriptionQuery", ErrorCodes::InvalidSubscriptionQuery},
{"InvalidTableRef", ErrorCodes::InvalidTableRef},
{"InvalidatedObject", ErrorCodes::InvalidatedObject},
{"KeyAlreadyUsed", ErrorCodes::KeyAlreadyUsed},
Expand Down Expand Up @@ -353,9 +368,20 @@ static const MapElem string_to_error_code[] = {
{"ServiceTypeNotFound", ErrorCodes::ServiceTypeNotFound},
{"StaleAccessor", ErrorCodes::StaleAccessor},
{"SubscriptionFailed", ErrorCodes::SubscriptionFailed},
{"SyncClientResetRequired", ErrorCodes::SyncClientResetRequired},
{"SyncCompensatingWrite", ErrorCodes::SyncCompensatingWrite},
{"SyncConnectFailed", ErrorCodes::SyncConnectFailed},
{"SyncInvalidSchemaChange", ErrorCodes::SyncInvalidSchemaChange},
{"SyncPermissionDenied", ErrorCodes::SyncPermissionDenied},
{"SyncProtocolInvariantFailed", ErrorCodes::SyncProtocolInvariantFailed},
{"SyncProtocolNegotiationFailed", ErrorCodes::SyncProtocolNegotiationFailed},
{"SyncServerPermissionsChanged", ErrorCodes::SyncServerPermissionsChanged},
{"SyncUserMismatch", ErrorCodes::SyncUserMismatch},
{"SyncWriteNotAllowed", ErrorCodes::SyncWriteNotAllowed},
{"SyntaxError", ErrorCodes::SyntaxError},
{"SystemError", ErrorCodes::SystemError},
{"TableNameInUse", ErrorCodes::TableNameInUse},
{"TlsHandshakeFailed", ErrorCodes::TlsHandshakeFailed},
{"TopLevelObject", ErrorCodes::TopLevelObject},
{"TwilioError", ErrorCodes::TwilioError},
{"TypeMismatch", ErrorCodes::TypeMismatch},
Expand All @@ -369,9 +395,7 @@ static const MapElem string_to_error_code[] = {
{"ValueAlreadyExists", ErrorCodes::ValueAlreadyExists},
{"ValueDuplicateName", ErrorCodes::ValueDuplicateName},
{"ValueNotFound", ErrorCodes::ValueNotFound},
{"WebSocketConnectionClosedClientError", ErrorCodes::WebSocketConnectionClosedClientError},
{"WebSocketConnectionClosedServerError", ErrorCodes::WebSocketConnectionClosedServerError},
{"WebSocketResolveFailedError", ErrorCodes::WebSocketResolveFailedError},
{"WrongSyncType", ErrorCodes::WrongSyncType},
{"WrongThread", ErrorCodes::WrongThread},
{"WrongTransactionState", ErrorCodes::WrongTransactionState},
};
Expand Down
55 changes: 17 additions & 38 deletions src/realm/error_codes.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ typedef enum realm_errno {
RLM_ERR_NO_SUBSCRIPTION_FOR_WRITE = 1026,
RLM_ERR_OPERATION_ABORTED = 1027,

RLM_ERR_AUTO_CLIENT_RESET_FAILED = 1028,
RLM_ERR_BAD_SYNC_PARTITION_VALUE = 1029,
RLM_ERR_CONNECTION_CLOSED = 1030,
RLM_ERR_INVALID_SUBSCRIPTION_QUERY = 1031,
RLM_ERR_SYNC_CLIENT_RESET_REQUIRED = 1032,
RLM_ERR_SYNC_COMPENSATING_WRITE = 1033,
RLM_ERR_SYNC_CONNECT_FAILED = 1034,
RLM_ERR_SYNC_INVALID_SCHEMA_CHANGE = 1035,
RLM_ERR_SYNC_PERMISSION_DENIED = 1036,
RLM_ERR_SYNC_PROTOCOL_INVARIANT_FAILED = 1037,
RLM_ERR_SYNC_PROTOCOL_NEGOTIATION_FAILED = 1038,
RLM_ERR_SYNC_SERVER_PERMISSIONS_CHANGED = 1039,
RLM_ERR_SYNC_USER_MISMATCH = 1040,
RLM_ERR_TLS_HANDSHAKE_FAILED = 1041,
RLM_ERR_WRONG_SYNC_TYPE = 1042,
RLM_ERR_SYNC_WRITE_NOT_ALLOWED = 1043,

RLM_ERR_SYSTEM_ERROR = 1999,

RLM_ERR_LOGIC = 2000,
Expand Down Expand Up @@ -185,48 +202,10 @@ typedef enum realm_errno {
RLM_ERR_INVALID_SERVER_RESPONSE = 4354,
RLM_ERR_APP_SERVER_ERROR = 4355,

RLM_ERR_WEBSOCKET_RESOLVE_FAILED_ERROR = 4400,
RLM_ERR_WEBSOCKET_CONNECTION_CLOSED_CLIENT_ERROR = 4401,
RLM_ERR_WEBSOCKET_CONNECTION_CLOSED_SERVER_ERROR = 4402,

RLM_ERR_CALLBACK = 1000000, /**< A user-provided callback failed. */
RLM_ERR_UNKNOWN = 2000000 /* Should not be used in code */
} realm_errno_e;

typedef enum realm_sync_errno_client {
RLM_SYNC_ERR_CLIENT_CONNECTION_CLOSED = 100,
RLM_SYNC_ERR_CLIENT_UNKNOWN_MESSAGE = 101,
RLM_SYNC_ERR_CLIENT_BAD_SYNTAX = 102,
RLM_SYNC_ERR_CLIENT_LIMITS_EXCEEDED = 103,
RLM_SYNC_ERR_CLIENT_BAD_SESSION_IDENT = 104,
RLM_SYNC_ERR_CLIENT_BAD_MESSAGE_ORDER = 105,
RLM_SYNC_ERR_CLIENT_BAD_CLIENT_FILE_IDENT = 106,
RLM_SYNC_ERR_CLIENT_BAD_PROGRESS = 107,
RLM_SYNC_ERR_CLIENT_BAD_CHANGESET_HEADER_SYNTAX = 108,
RLM_SYNC_ERR_CLIENT_BAD_CHANGESET_SIZE = 109,
RLM_SYNC_ERR_CLIENT_BAD_ORIGIN_FILE_IDENT = 110,
RLM_SYNC_ERR_CLIENT_BAD_SERVER_VERSION = 111,
RLM_SYNC_ERR_CLIENT_BAD_CHANGESET = 112,
RLM_SYNC_ERR_CLIENT_BAD_REQUEST_IDENT = 113,
RLM_SYNC_ERR_CLIENT_BAD_ERROR_CODE = 114,
RLM_SYNC_ERR_CLIENT_BAD_COMPRESSION = 115,
RLM_SYNC_ERR_CLIENT_BAD_CLIENT_VERSION = 116,
RLM_SYNC_ERR_CLIENT_SSL_SERVER_CERT_REJECTED = 117,
RLM_SYNC_ERR_CLIENT_PONG_TIMEOUT = 118,
RLM_SYNC_ERR_CLIENT_BAD_CLIENT_FILE_IDENT_SALT = 119,
RLM_SYNC_ERR_CLIENT_BAD_FILE_IDENT = 120,
RLM_SYNC_ERR_CLIENT_CONNECT_TIMEOUT = 121,
RLM_SYNC_ERR_CLIENT_BAD_TIMESTAMP = 122,
RLM_SYNC_ERR_CLIENT_BAD_PROTOCOL_FROM_SERVER = 123,
RLM_SYNC_ERR_CLIENT_CLIENT_TOO_OLD_FOR_SERVER = 124,
RLM_SYNC_ERR_CLIENT_CLIENT_TOO_NEW_FOR_SERVER = 125,
RLM_SYNC_ERR_CLIENT_PROTOCOL_MISMATCH = 126,
RLM_SYNC_ERR_CLIENT_BAD_STATE_MESSAGE = 127,
RLM_SYNC_ERR_CLIENT_MISSING_PROTOCOL_FEATURE = 128,
RLM_SYNC_ERR_CLIENT_HTTP_TUNNEL_FAILED = 131,
RLM_SYNC_ERR_CLIENT_AUTO_CLIENT_RESET_FAILURE = 132,
} realm_sync_errno_client_e;

typedef enum realm_sync_errno_connection {
RLM_SYNC_ERR_CONNECTION_CONNECTION_CLOSED = 100,
RLM_SYNC_ERR_CONNECTION_OTHER_ERROR = 101,
Expand Down
21 changes: 17 additions & 4 deletions src/realm/error_codes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@ class ErrorCodes {
BadVersion = RLM_ERR_BAD_VERSION,
OperationAborted = RLM_ERR_OPERATION_ABORTED,

AutoClientResetFailed = RLM_ERR_AUTO_CLIENT_RESET_FAILED,
BadSyncPartitionValue = RLM_ERR_BAD_SYNC_PARTITION_VALUE,
ConnectionClosed = RLM_ERR_CONNECTION_CLOSED,
InvalidSubscriptionQuery = RLM_ERR_INVALID_SUBSCRIPTION_QUERY,
SyncClientResetRequired = RLM_ERR_SYNC_CLIENT_RESET_REQUIRED,
SyncCompensatingWrite = RLM_ERR_SYNC_COMPENSATING_WRITE,
SyncConnectFailed = RLM_ERR_SYNC_CONNECT_FAILED,
SyncInvalidSchemaChange = RLM_ERR_SYNC_INVALID_SCHEMA_CHANGE,
SyncPermissionDenied = RLM_ERR_SYNC_PERMISSION_DENIED,
SyncProtocolInvariantFailed = RLM_ERR_SYNC_PROTOCOL_INVARIANT_FAILED,
SyncProtocolNegotiationFailed = RLM_ERR_SYNC_PROTOCOL_NEGOTIATION_FAILED,
SyncServerPermissionsChanged = RLM_ERR_SYNC_SERVER_PERMISSIONS_CHANGED,
SyncUserMismatch = RLM_ERR_SYNC_USER_MISMATCH,
TlsHandshakeFailed = RLM_ERR_TLS_HANDSHAKE_FAILED,
WrongSyncType = RLM_ERR_WRONG_SYNC_TYPE,
SyncWriteNotAllowed = RLM_ERR_SYNC_WRITE_NOT_ALLOWED,

SystemError = RLM_ERR_SYSTEM_ERROR,

LogicError = RLM_ERR_LOGIC,
Expand Down Expand Up @@ -229,10 +246,6 @@ class ErrorCodes {
InvalidServerResponse = RLM_ERR_INVALID_SERVER_RESPONSE,
AppServerError = RLM_ERR_APP_SERVER_ERROR,

WebSocketResolveFailedError = RLM_ERR_WEBSOCKET_RESOLVE_FAILED_ERROR,
WebSocketConnectionClosedClientError = RLM_ERR_WEBSOCKET_CONNECTION_CLOSED_CLIENT_ERROR,
WebSocketConnectionClosedServerError = RLM_ERR_WEBSOCKET_CONNECTION_CLOSED_SERVER_ERROR,

CallbackFailed = RLM_ERR_CALLBACK,
UnknownError = RLM_ERR_UNKNOWN,
};
Expand Down
13 changes: 2 additions & 11 deletions src/realm/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,20 +367,11 @@ struct SystemError : RuntimeError {

~SystemError() noexcept override;

std::error_code get_system_error() const
{
return to_status().get_std_error_code();
}

const std::error_category& get_category() const
{
return get_system_error().category();
}

private:
static Status make_status(std::error_code err, std::string_view msg, bool msg_is_prefix)
{
return Status(err, msg_is_prefix ? util::format("%1: %2 (%3)", msg, err.message(), err.value()) : msg);
return Status(ErrorCodes::SystemError,
msg_is_prefix ? util::format("%1: %2 (%3)", msg, err.message(), err.value()) : msg);
}
};

Expand Down
Loading

0 comments on commit d89a330

Please sign in to comment.