HandleAfterResetCallback never hits

Hi

I am resolving the obsolete methods left over after the MongoDB package update and I am stuck in the middle of the client reset functionality. I have tested the DiscardLocalResetHandler and it behaves a little bit odd compared to the documentation. I do destructive change and I receive the HandleBeforeResetCallback but I never receive the HandleAfterResetCallback even If I do not close the app for a long period of time. Besides, when I close the app and reopen it again I receive the HandleBeforeResetCallback again. And eventually, when I close the app and open it for the third time I get the app with proper sync. So is this behaviour correct or am I doing something wrong?

Thanks.

Hi @Vardan_Sargsyan92,

The DiscardLocalResetHandler strategy discards the local changes and gets a fresh copy of the realm stored on the sync server. A destructive schema change makes this impossible as downloading the fresh realm is impossible given the mismatching schemas. In that case, your client and server need to re-align with the schema first, then sync can be restarted.

I do destructive change and I receive the HandleBeforeResetCallback but I never receive the HandleAfterResetCallback even If I do not close the app for a long period of time. Besides, when I close the app and reopen it again I receive the HandleBeforeResetCallback again

Generally, when the DiscardLocalResetHandler strategy fails because of an error, it fallsback to the ManualResetFallback. So it’s normal that you don’t see the OnAfterReset being triggered. However, you’d see the ManualResetFallback triggered.

And eventually, when I close the app and open it for the third time I get the app with proper sync.

This is a little strange. Are you sure that in the meanwhile you haven’t updated the schema on the client to match the server?

As a little conclusion, DiscardLocalResetHandler is expected to be really useful in a situation where the client and the server don’t share anymore the same history; but still share the same schema.

It’s generally really helpful to have an overview of what could cause a client reset. I’d recommend to take a look at our docs about this subject.

Additionally, we specifically explain in our docs that a destructive schema change is not something that DiscardLocalResetHandler can handle, and it’ll fallback to ManualResetFallback.

2 Likes

Hi @Andrea_Catalini
the question is based on your docs and thank you for just re-denoting the links which I have already read. Indeed the DiscardLocalResetHandler can not handle the destructive change you have mentioned. The destructive change is one of the ways to imitate a client reset which I am trying to achieve. So I am not asking what the DiscardLocalResetHandler do or what it can handle. I am just asking why I am not receive the after-reset callback.
Now Regarding the points that you have mentioned above.

So it’s normal that you don’t see the OnAfterReset being triggered. However, you’d see the ManualResetFallback triggered.

  1. So when the OnAfterReset is triggered?

This is a little strange. Are you sure that in the meanwhile you haven’t updated the schema on the client to match the server?

  1. I play only with the database through the app. I mean I add/remove/update records. I do not touch any schema

As a one off, you could simply disable and re-enable sync. That’ll trigger both OnBeforeReset and OnAfterReset.
But if you’re writing integration tests, this is clearly not a good avenue as you need automation. I’ll get back to you with some advice for triggering a client reset in a programmatic way.

1 Like

@Andrea_Catalini

As a one-off, you could simply disable and re-enable sync. That’ll trigger both OnBeforeReset and OnAfterReset.

I have tried but no luck. Thank you for the info. I will do manual reset

I’m investigating why disabling and re-enabling sync isn’t doing it.
If integration tests is not what you’re after, but you’re just after hitting the before and after callbacks, you could use a method we expose on the session exactly to simulate a client reset. The method is SimulateAutomaticClientResetFailure and you can see how we use it in our tests.

I hope this does it for you.

1 Like

HI @Andrea_Catalini. Thank you for your efforts. SimulateAutomaticClientResetFailure would be a good option for my case.

HI @Andrea_Catalini .
Could you please answer the following question?

I am using the methods inside the TestingExtensions. The application crashes when I use the SimulateError method and pass the DivergingHistories error code. Besides, I have implemented error handling for session exceptions discussed here but the problem is that the app crashes before achieving to the exception handling method.
So Is that acceptable behaviour or not?

I take that this is a completely different issue. You are now asking about testing session errors.
What you describe is not supposed to happen. Can you show the full test? I’m interested in seeing your ClientResetHandler, your OnSessionError, the overall logic of the test and how you use SimulateError.

I just tried terminating and restarting sync myself and it does trigger a client reset. The only detail, that I understand is not obvious, is that the client generally tries to reconnect for up to an hour.
To avoid the waiting time, once you know you’ve terminated the server, just close and reopen the realm. The skeleton of a test of this type would look like this

var conf = new PartitionSyncConfiguration(partition, user)
{
    ClientResetHandler = new DiscardLocalResetHandler
    {
        OnBeforeReset = (beforeFrozen) =>
        {
        // executed right before a client reset is about to happen
        },
        OnAfterReset = (beforeFrozen, after) =>
        {
        // executed right after an automatic recovery from a client reset has completed
        },
        ManualResetFallback = (session, err) =>
        {
        // handle the reset manually
        }
    }
};

var realm = Realm.GetInstance(config);

// ... do whatever you need to do
// let the app wait for sync to be terminated
await Task.Delay(20000);

// manually terminate sync

realm.Dispose();
while (!realm.IsClosed)
{
    await Task.Delay(500);
}

realm = Realm.GetInstance(config);

// your `OnBeforeReset` and `OnAfterReset` callbacks should be hit now

I hope this works for you. Let me know if you still encounter issues.


Andrea

HI @Andrea_Catalini

Thanks for the info. Apparently, the whole client reset process is not much improved from before. Thus I will keep the workaround the same as discussed here.

I have a button inside the app which executes the following part of the code.

private async Task<bool> ExecuteCoreAsync(object parameter, CancellationToken token = default)
        {
            using var realm = await _realmFactory.GetRealmAsync(_databaseManager.CurrentDatabase);
            realm.SyncSession.SimulateError(_viewModel.RealmErrorCode, _viewModel.RealmErrorCode.ToString());
            return true;
        }

Here is the part of the PartitionSyncConfiguration setup.

private async Task<RealmConfigurationBase> GetSyncedConfigurationAsync(string realmDbPath, string databasePartitionKey)

        {

            IRealmUserContext userContext = await _realmAuthService.SignInAsync();

            var syncedConfiguration = new PartitionSyncConfiguration(databasePartitionKey, userContext.User, realmDbPath)

                                      {

                                          Schema = _realmTypes,

                                          OnSessionError = HandleSessionError,

                                          ClientResetHandler = new DiscardLocalResetHandler

                                                               {

                                                                   ManualResetFallback = HandleManualReset

                                                               }

                                      };

            return syncedConfiguration;

        }

        private void HandleSessionError(Session session, SessionException error)

        {

...

        }

        private void HandleManualReset(ClientResetException clientResetException)

        {

...

        }

So when I tap on the button the app simulates a session exception with the DivergingHistories error code and nothing happens. When I tap on the button again the app crashes before reaching the HandleSessionError method. If I just call a method SimulateAutomaticClientResetFailure then the app enters to HandleSessionError method.

What we worked on for the client reset was adding strategies that greatly simplify developers’ lives when it comes to data handling and recovery when compared to the previous manual handler.
When it comes to testing, if you are fine with not doing an integration test but just testing code paths, then the suggested SimulateAutomaticClientResetFailure should do.
If you are after integration testing, I suggested a working way that unfortunately needs some user interaction, namely terminating and reenabling sync. If it’s very important for you to programmatically trigger a client reset on the server, please open a github issue so that the whole team can investigate the feature.

Let me know if there is still something unclear.

Thank you @Andrea_Catalini. I’ll do that

Overall the code shown seems reasonable and should work, granted that the missing parts of code like what the factory does etc have no errors.

So when I tap on the button the app simulates a session exception with the DivergingHistories error code and nothing happens. When I tap on the button again the app crashes before reaching the HandleSessionError method. If I just call a method SimulateAutomaticClientResetFailure then the app enters to HandleSessionError method.

None of this is normal but our tests don’t show such behaviour. If you can extract the issue in a small project and send it to us, we can investigate.

1 Like