Skip to content

Commit

Permalink
Client reset w/recovery (#4711)
Browse files Browse the repository at this point in the history
* Add new client reset modes
* Expand manual mode
Co-authored-by: Kræn Hansen <[email protected]>
  • Loading branch information
kneth committed Nov 1, 2022
1 parent df757a7 commit 5c8cf79
Show file tree
Hide file tree
Showing 16 changed files with 1,066 additions and 202 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
## vNext (TBD)

### Enhancements
* Add support for using functions as default property values, in order to allow dynamic defaults [#5001](https://github.com/realm/realm-js/pull/5001), [#2393](https://github.com/realm/realm-js/issues/2393)
* All fields of a `Realm.Object` treated as optional by TypeScript when constructing a new class-based model, unless specified in the second type parameter [#5000](https://github.com/realm/realm-js/pull/5000)
* Improve performance of client reset with automatic recovery and converting top-level tables into embedded tables. ([realm/realm-core#5897](https://github.com/realm/realm-core/pull/5897))
* If a sync client sends a message larger than 16 MB, the sync server will request a client reset. ([realm/realm-core#5209](https://github.com/realm/realm-core/issues/5209))
* Add two new modes to client reset: `RecoverUnsyncedChanges` and `RecoverOrDiscardUnsyncedChanges`. The two modes will recover local/unsynced changes with changes from the server if possible. If not possible, `RecoverOrDiscardUnsyncedChanges` will remove the local Realm file and download a fresh file from the server. The mode `DiscardLocal` is duplicated as `DiscardUnsyncedChanges`, and `DiscardLocal` is be removed in a future version. ([#4135](https://github.com/realm/realm-js/issues/4135))

### Fixed
* Fixed a use-after-free if the last external reference to an encrypted Realm was closed between when a client reset error was received and when the download of the new Realm began. ([realm/realm-core#5949](https://github.com/realm/realm-core/pull/5949), since v10.20.0)
Expand Down
8 changes: 5 additions & 3 deletions docs/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@
/**
* This describes the options to configure client reset.
* @typedef {Object} Realm.App.Sync~ClientResetConfiguration
* @property {string} mode - Either "manual" (see also `Realm.App.Sync.initiateClientReset()`) or "discardLocal" (download a fresh copy from the server). Default is "discardLocal".
* @property {callback(realm)|null} [onBefore] - called before sync initiates a client reset.
* @property {callback(beforeRealm, afterRealm)|null} [onAfter] - called after client reset has been executed; `beforeRealm` and `afterRealm` are instances of the Realm before and after the client reset.
* @property {string} mode - Either "manual" (deprecated, see also `Realm.App.Sync.initiateClientReset()`), "discardUnsyncedChanges" (download a fresh copy from the server), "recoverUnsyncedChanges" (merged remote and local, unsynced changes), or "recoverOrDiscardUnsyncedChanges" (download a fresh copy from the server if recovery of unsynced changes is not possible)
* @property {callback(realm)|null} [onBefore] - called before sync initiates a client reset (only for "discardUnsyncedChanges", "recoverUnsyncedChanges" or "recoverOrDiscardUnsyncedChanges" modes).
* @property {callback(beforeRealm, afterRealm)|null} [onAfter] - called after client reset has been executed; `beforeRealm` and `afterRealm` are instances of the Realm before and after the client reset (only for "discardUnsyncedChanges", "recoverUnsyncedChanges" or "recoverOrDiscardUnsyncedChanges" modes).
* @property {callback(session, path)|null} [onFallback] - called if recovery or discard fail (only for "recoverUnsyncedChanges" or "recoverOrDiscardUnsyncedChanges" modes).
* @property {callback(session, path)|null} [onManual] - perform manual client reset - see also `Realm.App.Sync.initiateClientReset()` (only "manual" mode).
* @since {10.11.0}
*/

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "triggerClientReset",
"private": false,
"run_as_system": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2022 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

/* eslint-env node */
/* global context */

exports = async function (appId, userId) {
return (await deleteClientFile(`__realm_sync_${appId}`, userId)) || (await deleteClientFile(`__realm_sync`, userId));
};

async function deleteClientFile(db, userId) {
const mongodb = context.services.get("mongodb");
return (await mongodb.db(db).collection("clientfiles").deleteMany({ ownerId: userId })).deletedCount > 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "triggerClientReset",
"private": false,
"run_as_system": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2022 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

/* eslint-env node */
/* global context */

exports = async function (appId, userId) {
return (await deleteClientFile(`__realm_sync_${appId}`, userId)) || (await deleteClientFile(`__realm_sync`, userId));
};

async function deleteClientFile(db, userId) {
const mongodb = context.services.get("mongodb");
return (await mongodb.db(db).collection("clientfiles").deleteMany({ ownerId: userId })).deletedCount > 0;
}
1 change: 1 addition & 0 deletions integration-tests/tests/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ import "./sync/sync-as-local";
import "./transaction";
import "./schema";
import "./types";
import "./sync/client-reset";
1 change: 1 addition & 0 deletions integration-tests/tests/src/tests/sync/asymmetric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { authenticateUserBefore, importAppBefore, openRealmBeforeEach } from "..

describe.skipIf(environment.missingServer, "Asymmetric sync", function () {
describe("Configuration and schema", function () {
this.timeout(20 * 1000);
const PersonSchema: Realm.ObjectSchema = {
name: "Person",
asymmetric: true,
Expand Down
Loading

0 comments on commit 5c8cf79

Please sign in to comment.