Client Resets - .NET SDK
On this page
Overview
New in version 10.17.0.
A client reset error is a scenario where a client realm cannot sync data with the application backend. Clients in this state may continue to run and save data locally but cannot send or receive sync changesets until they perform a client reset. The Realm SDKs provide methods to automatically handle client resets under most scenarios.
Client Reset Strategies
Client reset scenarios occur when the server's history is incompatible with the client's history. The most common causes of client resets are described in Client Resets.
In the .NET SDK, you can specify a client reset strategy in your FlexibleSyncConfiguration and PartitionSyncConfiguration. The ClientResetHandler property can be set to one of the following:
Handler | Strategy | Notes |
---|---|---|
RecoverOrDiscardUnsyncedChangesHandler
(Default) | Recover all unsynced local changes | If recovery fails, this handler falls back to the DiscardUnsyncedChangesHandler ,
which deletes all unsynced local changes. If the DiscardUnsyncedChangesHandler
recovery fails, the handler falls back to a ManualRecoveryHandler , which
requires you to implement a manual recovery strategy. |
Recover all unsynced local changes | If recovery fails, this handler falls back to a ManualRecoveryHandler ,
which requires you to implement a manual recovery strategy. | |
Discard unsynced changes | This strategy permanently deletes all local unsynced changes made since
the last successful sync. | |
Manual recovery | Provides a way for you to implement your own recovery strategy. |
The following sections describe the different strategies for handling a client
reset and how you can use each of the ClientResetHandlers
.
Recover Unsynced Local Changes
In a client reset that does not involve a breaking schema change, the SDK integrates objects created locally that did not sync before the client reset. The following rules determine how conflicts are resolved when both the backend and the client make changes to the same object:
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 delete instruction.
In the case of conflicting updates to the same field, the client update is applied.
Important
Breaking Changes
The recovery process cannot handle a breaking schema change. For example, if you make a non-additive change to one or more of your Realm object classes, the recovery process will fail. In this case, users will be required to update their client app. For more information on breaking changes, see Breaking vs. Non-Breaking Change Quick Reference.
There are two client reset handlers that try to automatically recover the unsycned
local data: RecoverOrDiscardUnsyncedChangesHandler
and RecoverUnsyncedChangesHandler
.
RecoverOrDiscardUnsyncedChangesHandler
This is the default handler, as it provides the most robust recovery process. If
the automatic recovery process fails, it falls back to the DiscardLocalReset
strategy explained in the Discard Unsynced Changes section.
If that process fails, it falls back again to a manual reset strategy explained
in the Manual Recovery section.
This handler provides the following callback methods:
OnBeforeReset, which the SDK invokes prior to the client reset. You can use this callback to notify the user before the client reset begins.
OnAfterRecovery, which the SDK invokes if and only if the automatic reset completes successfully. You can use it to notify the user that the client reset is complete.
OnAfterDiscard, which the SDK invokes only if the automatic client reset fails and the discard local strategy succeeds. If the discard strategy fails, this callback is not invoked.
ManualResetFallback, which the SDK invokes only if the automatic recovery and the discard strategy have failed. You implement this callback to handle the reset failure. Your logic here should be similar to that described for a ManualRecoveryHandler.
The following example shows using each of these callbacks:
var conf = new FlexibleSyncConfiguration(user) { ClientResetHandler = new RecoverOrDiscardUnsyncedChangesHandler { // The following callbacks are optional OnBeforeReset = (beforeReset) => { // Executed before the client reset begins // Can be used to notify the user that a reset is going // to happen }, OnAfterRecovery = (beforeReset, afterReset) => { // Executed after the client reset is complete // Can be used to notify the user that the reset is done }, OnAfterDiscard = (beforeReset, afterReset) => { // Executed if the automatic recovery has failed // but the DiscardUnsyncedChanges fallback has completed // successfully }, ManualResetFallback = (err) => { // Automatic reset failed; handle the reset manually here } } };
RecoverUnsyncedChangesHandler
Like the RecoverOrDiscardUnsyncedChangesHandler
, this handler attempts to
recover all unsynced local changes automatically. However, unlike
RecoverOrDiscardUnsyncedChangesHandler
, this handler does not fall back to a
DiscardUnsyncedChangesHandler
if the automatic recovery fails. Instead, it
falls back to a manual reset strategy explained in the
Manual Recovery section. The handler
provides the following callback methods:
OnBeforeReset, which you can use to notify the user before the reset begins.
OnAfterReset, which you can use to notify the user when the reset has completed successfully.
ManualResetFallback, which you implement to handle a reset failure. Your logic here should be similar to that described for a ManualRecoveryHandler.
The following example shows using each of these callbacks:
var conf = new FlexibleSyncConfiguration(user) { ClientResetHandler = new RecoverUnsyncedChangesHandler { // The following callbacks are optional OnBeforeReset = (beforeReset) => { // Executed before the client reset begins // Can be used to notify the user that a reset is going // to happen }, OnAfterReset = (beforeReset, afterReset) => { // Executed after the client reset is complete // Can be used to notify the user that the reset is done }, ManualResetFallback = (err) => { // Automatic reset failed; handle the reset manually here } } };
Discard Unsynced Changes
This strategy permanently deletes all local unsynced changes
made since the last successful sync. If you choose to use a
DiscardUnsyncedChangesHandler
, the SDK restores your local realm file
to a syncable state without closing the realm and while keeping notifications
fully working. If this process fails, it falls back to a manual reset
strategy explained in the
Manual Recovery section.
This handler provides the following callback methods:
OnBeforeReset, which the SDK invokes prior to the client reset. You can use this callback to notify the user before the reset begins.
OnAfterReset, which the SDK invokes if and only if the reset completes successfully. You can use it to notify the user when the reset is complete.
ManualResetFallback, which the SDK invokes if the reset fails. Your logic here should be similar to that described for a ManualRecoveryHandler.
The following example shows how you might implement a
DiscardUnsyncedChangesHandler
:
{
var config = new FlexibleSyncConfiguration(user);
config.ClientResetHandler = new DiscardUnsyncedChangesHandler()
{
// The following callbacks are optional
OnBeforeReset = (beforeReset) =>
{ // Executed before the client reset begins // Can be used to notify the user that a reset is going // to happen },
OnAfterReset = (beforeReset, afterReset) => { // Executed after the client reset is complete // Can be used to notify the user that the reset is done },
ManualResetFallback = (err) =>
{ // Automatic reset failed; handle the reset manually here }
};
try {
var realm = await Realm.GetInstanceAsync(config); }
catch (Exception ex) { Console.WriteLine($@"Error creating or opening the realm file. {ex.Message}"); }
Manual Recovery
In most cases, you should use one of the other strategies for client resets. For those infrequent cases where you need to customize your data recovery process, select the ManualRecoveryHandler handler.
Note
Fallbacks
While the manual strategy should only be used in edge cases, the other client reset handlers might fall back to a manual strategy. The logic you use in those handlers is similar to the logic described here.
Within the ManualRecoveryHandler
, you dispose the existing realm, and then
call the InitiateClientReset()
method.
The following example demonstrates implementing the ManualRecoveryHandler
:
private void SetupRealm()
{
var config = new FlexibleSyncConfiguration(user);
config.ClientResetHandler =
new ManualRecoveryHandler(HandleClientResetError);
var realm = await Realm.GetInstanceAsync(config);
}
private void HandleClientResetError(ClientResetException clientResetException)
{
Console.WriteLine($"Client Reset requested: {clientResetException.Message}");
// Prompt user to perform a client reset immediately. If they don't,
// they won't receive any data from the server until they restart the app
// and all changes they make will be discarded when the app restarts.
var didUserConfirmReset = ShowUserAConfirmationDialog();
if (didUserConfirmReset)
{
// Close the Realm before doing the reset. It must be
// deleted as part of the reset.
fsRealm.Dispose();
// perform the client reset
var didReset = clientResetException.InitiateClientReset();
if (didReset)
{
// Navigate the user back to the main page or reopen the
// the Realm and reinitialize the current page
}
else
{
// Reset failed - notify user that they'll need to
// update the app
}
}
}
Test Client Reset Handling
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:
Write data from a client application and wait for it to synchronize.
Terminate and re-enable Device Sync.
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.