Docs Menu
Docs Home
/ /
Atlas Device SDKs
/ /

Handle Sync Errors - C++ SDK

On this page

  • Handle Sync Errors
  • Client Reset
  • Automatic vs. Manual Client Reset
  • Specify a Client Reset Mode
  • Handle Schema Changes
  • Recover Unsynced Changes
  • Discard Unsynced Changes
  • Manual Client Reset Mode
  • Test Client Reset Handling

While developing an application that uses Device Sync, you should set an error handler. This error handler detects and can respond to any failed sync-related API calls.

Set an error handler on the sync_config. When an error occurs, the C++ SDK calls the error handler with the sync_error object and the sync_session where the error occurred.

auto appConfig = realm::App::configuration();
appConfig.app_id = APP_ID;
auto app = realm::App(appConfig);
auto user = app.login(realm::App::credentials::anonymous()).get();
auto dbConfig = user.flexible_sync_configuration();
// Setting an error handler on the sync_config gives you access to
// sync_session and sync_error
dbConfig.sync_config().set_error_handler(
[](const realm::sync_session &session,
const realm::internal::bridge::sync_error &error) {
std::cerr << "A sync error occurred. Message: " << error.message()
<< std::endl;
});
auto syncRealm = realm::db(dbConfig);

Tip

For a list of common Device Sync errors and how to handle them, refer to Sync Errors in the App Services Device Sync documentation.

When using Device Sync, a client reset is an error recovery task that your client app must perform when the server can no longer sync with the device database. In this case, the device must reset its database to a state that matches the server in order to restore the ability to sync.

When this occurs, the unsyncable database on the device may contain data that has not yet synced to the server. The SDK can attempt to recover or discard that data during the client reset process.

For more information about what might cause a client reset to occur, go to Client Resets in the App Services documentation.

The SDK provides client reset modes that automatically handle most client reset errors. Automatic client reset modes restore your device's database file to a syncable state without closing the database or missing notifications.

All the client reset modes except manual() perform an automatic client reset. The differences between the modes are based on how they handle changes on the device that have not yet synced to the backend.

Choose recover_unsynced_changes() to handle most client reset scenarios automatically. This attempts to recover unsynced changes when a client reset occurs.

In some cases, you may want or need to set a manual client reset handler. You may want to do this if your app requires specific client reset logic that can't be handled automatically.

The C++ SDK provides the option to specify a client reset handler in your database configuration. This client reset handler can take a client_reset_mode_base. This struct allows you to specify:

  • A block to execute before the client reset

  • A block to execute after the client reset

  • The mode to use when handling the client reset

auto user = app.login(realm::App::credentials::anonymous()).get();
auto syncConfig = user.flexible_sync_configuration();
// Set the client reset handler with your preferred client reset mode.
syncConfig.set_client_reset_handler(
realm::client_reset::recover_unsynced_changes(beforeReset, afterReset));
auto syncedRealm = realm::db(syncConfig);

You can use one of the available client reset modes to specify how the SDK should attempt to resolve any unsynced data on the device during a client reset:

  • recover_unsynced_changes()

  • recover_or_discard_unsynced_changes()

  • discard_unsynced_changes()

  • manual()

You can specify a before and after block to execute during the automatic client reset process. You might use this to perform recovery logic that is important to your application.

/* You can define blocks to call before and after the client reset occur
if you need to execute specific logic, such as reporting or debugging. */
auto beforeReset = [&](realm::db before) {
/* A block called after a client reset error is detected, but before the
client recovery process is executed. You could use this block for any
custom logic, reporting, debugging etc. You have access to the database
before the client reset occurs in this block. */
};
auto afterReset = [&](realm::db device, realm::db server) {
/* A block called after the client recovery process has executed.
This block could be used for custom recovery, reporting, debugging etc.
You have access to the database that is currently on the device - the
one that can no longer sync - and the new database that has been
restored from the server. */
};

If your app has specific client recovery needs, you can specify the manual() client reset mode and set a manual client reset handler. You might do this if you have specific custom logic your app must perform during a client reset, or if the client recovery rules do not work for your app.

Client Recovery is a feature that is enabled by default when you configure Device Sync. When Client Recovery is enabled, the SDK can automatically manage the client reset process in most cases. When you make schema changes:

  • The client can recover unsynced changes when there are no schema changes, or non-breaking schema changes.

  • When you make breaking schema changes, the automatic client reset modes fall back to a manual error handler. You can set a manual client reset error handler for this case. Automatic client recovery cannot occur when your app makes breaking schema changes.

For information on breaking vs. non-breaking schema changes, see Breaking vs. Non-Breaking Change Quick Reference.

During a client reset, client applications can attempt to recover data in the synced database on the device that has not yet synced to the backend. To recover unsynced changes, Client Recovery must be enabled in your App Services App, which it is by default.

If you want your app to recover changes that have not yet synced, use one of these client recovery modes:

  • recover_unsynced_changes(): The client attempts to recover unsynced changes. Choose this mode when you do not want to fall through to discard unsynced changes.

  • recover_or_discard_unsynced_changes(): The client first attempts to recover changes that have not yet synced. If the client cannot recover unsynced data, it falls through to discard unsynced changes but continues to automatically perform the client reset. Choose this mode when you want to enable automatic client recovery to fall back to discard unsynced changes.

auto user = app.login(realm::App::credentials::anonymous()).get();
auto syncConfig = user.flexible_sync_configuration();
// Set the client reset handler with your preferred client reset mode.
syncConfig.set_client_reset_handler(
realm::client_reset::recover_unsynced_changes(beforeReset, afterReset));
auto syncedRealm = realm::db(syncConfig);

There may be times when the client reset operation cannot complete in recover_unsynced_changes() mode, like when there are breaking schema changes or Client Recovery is disabled in the Device Sync configuration. To handle this case, your app can handle a client reset error in the Sync error handler. For more information, refer to the manual client reset mode section on this page.

When Client Recovery is enabled, these rules determine how objects are integrated, including how conflicts are resolved when both the backend and the client make changes to the same object:

  • Objects created locally that were not synced before client reset are synced.

  • If an object is deleted on the server, but is modified on the recovering client, the delete takes precedence and the client discards the update.

  • If an object is deleted on the recovering client, but not the server, then the client applies the server's delete instruction.

  • In the case of conflicting updates to the same field, the client update is applied.

The discard_unsynced_changes() client reset mode permanently deletes all unsynced changes on the device since the last successful sync. You might use this mode when your app requires client recovery logic that is not consistent with the Device Sync client recovery rules, or when you don't want to recover unsynced data.

Do not use discard unsynced changes mode if your application cannot lose device data that has not yet synced to the backend.

To perform an automatic client reset that discards unsynced changes, use the discard_unsynced_changes() client reset mode.

auto user = app.login(realm::App::credentials::anonymous()).get();
auto syncConfig = user.flexible_sync_configuration();
// Set the client reset handler with your preferred client reset mode.
syncConfig.set_client_reset_handler(
realm::client_reset::discard_unsynced_changes(beforeReset, afterReset));
auto syncedRealm = realm::db(syncConfig);

Note

Discard with Recovery

If you'd like to attempt to recover unsynced changes, but discard any changes that cannot be recovered, refer to the recover_or_discard_unsynced_changes() documentation in the Recover Unsynced Changes section on this page.

There may be times when the client reset operation cannot complete in discard_unsynced_changes() mode, like when there are breaking schema changes. To handle this case, your app can handle a client reset error in the Sync error handler. For more information, refer to the Manual Client Reset Mode section on this page.

When you use manual() client reset mode, you must implement a custom client reset handler in the Sync error handler. We recommend using the automatic client recovery modes when possible, and only choosing manual() mode if the automatic recovery logic is not suitable for your app.

auto user = app.login(realm::App::credentials::anonymous()).get();
auto syncConfig = user.flexible_sync_configuration();
// Set the client reset handler to manual client reset mode.
syncConfig.set_client_reset_handler(realm::client_reset::manual());
// Define a Sync error handler for handling the client reset.
syncConfig.sync_config().set_error_handler(
[&](realm::sync_session session, realm::sync_error error) {
if (error.is_client_reset_requested()) {
/* You might use this for reporting or to instruct the user to delete
and re-install the app. */
};
});
auto syncedRealm = realm::db(syncConfig);

If the client reset operation cannot complete automatically, like when there are breaking schema changes, the client reset process falls through to the manual error handler. This may occur in any of these automatic client reset modes:

  • recover_unsynced_changes()

  • recover_or_discard_unsynced_changes()

  • discard_unsynced_changes()

We recommend treating the manual handler as a tool for fatal error recovery situations where you advise users to update the app or perform some other action.

You can manually test your application's client reset handling by terminating and re-enabling Device Sync.

When you terminate and re-enable Sync, clients that have previously connected with Sync are unable to connect until after they perform a client reset. Terminating Sync deletes the metadata from the server that allows the client to synchronize. The client must download a new copy of the realm from the server. The server sends a client reset error to these clients. So, when you terminate Sync, you trigger the client reset condition.

To test client reset handling:

  1. Write data from a client application and wait for it to synchronize.

  2. Terminate and re-enable Device Sync.

  3. Run the client app again. The app should get a client reset error when it tries to connect to the server.

Warning

While you iterate on client reset handling in your client application, you may need to terminate and re-enable Sync repeatedly. Terminating and re-enabling Sync renders all existing clients unable to sync until after completing a client reset. To avoid this in production, test client reset handling in a development environment.

Back

Manage Sync Sessions