手動クライアントリセットデータリカバリ - Node.js SDK
項目一覧
このページでは、 手動回復 クライアントリセット モードを使用して、クライアント リセット後に同期されていない Realm データを手動で回復する方法について説明します。
手動リカバリには、大量のコード、スキーマの認可、カスタム競合解決ロジックが必要です。 同期されていないデータを失うことができず、他の自動クライアントリセット方法がユースケースを満たさない場合は、同期されていない Realm データの手動リカバリを実行する必要があります。
使用可能なその他のクライアント リセット モードの詳細については、 「 クライアント Realm のリセット 」を参照してください。
手動リカバリの詳細は、アプリケーションとスキーマによって大きく異なります。 ただし、手動リカバリに役立つ手法がいくつかあります。 「オブジェクト戦略による変更の追跡」セクションでは、クライアントのリセット中に同期されていない変更を回復する方法の 1 つがあり 。
警告
本番環境で重大なスキーマ変更を行わない
スキーマの重大な変更後に、同期されていないデータがすべて回復することは期待しないでください。 ユーザー データを保持する最良の方法は、重大なスキーマの変更をまったく行わないことです。
重要
スキーマの重大な変更にはアプリスキーマの更新が必要
スキーマの重大な変更後:
すべてのクライアントがクライアント リセットを実行する必要があります。
スキーマの重大な変更の影響を受けるクライアントモデルを更新する必要があります。
オブジェクト戦略による変更の追跡
オブジェクト手動クライアントリセットデータリカバリ戦略によって変更を追跡すると、クライアント Realm ファイルにすでに書き込まれたがバックエンドにまだ同期されていないデータを回復できます。
この戦略では、各オブジェクト モデルに「最終更新時間」を追加して、各オブジェクトが最後に変更されたタイミングを追跡します。 を監視して、Realm がその状態をバックエンドに最後にアップロードしたかを判断します。
バックエンドがクライアント リセットを呼び出すと、バックエンドとの最後の同期以降に削除、作成、または更新されたオブジェクトを見つけます。 次に、バックアップ レルムから新しいレルムにそのデータをコピーします。
次の手順は、 プロセスを高レベルで実装する方法を示しています。
クライアント リセット エラー: アプリケーションはバックエンドからクライアント リセット エラー コードを受信します。
戦略実装: SDK は戦略実装を呼び出します。
Realm のすべてのインスタンスを閉じる : クライアントがリセットされている Realm の開いているインスタンスをすべて閉じます。 アプリケーション アーキテクチャによってこれが困難な場合(たとえば、アプリがアプリケーション全体でリスナーにおいて多くの Realm インスタンスを同時に使用する場合)は、アプリケーションを再起動した方が簡単な場合があります。 これはプログラムによって、またはダイアログでユーザーに直接リクエストすることで行うことができます。
Realmを バックアップ ファイルに移動 します。Realm.App.Sync.initiateClientReset()を呼び出します 静的メソッドを使用します。 このメソッドは、クライアント Realm ファイルの現在のコピーをバックアップ ファイルに移動します。
Realm の新しいインスタンスを開きます: 一般的な同期構成を使用して、Realm の新しいインスタンスを開きます。 アプリケーションで複数の Realm が使用されている場合は、バックアップ ファイル名からクライアントがリセットされる Realm を識別できます。
バックエンドからすべての Realm データをダウンロード : 続行する前に、Realm 内のデータ セット全体をダウンロードします。 これは、 SyncConfigurationオブジェクトのデフォルトの動作です。
Realm バックアップを開きます 。
SyncConfiguration.error
コールバック関数への引数として渡されたerror.config
オブジェクトを使用します。同期されていない変更を移行 : バックアップ邦土をクエリして、復元するデータを確認します。 必要に応じて新しい Realm でデータを挿入、削除、更新します。
例
この例では、オブジェクト手動クライアントリセットデータリカバリ戦略によって変更を追跡する実装が示されています。
注意
この例の制限
この例は、単一の Realm オブジェクトタイプを含む単一の Realm を持つアプリケーションのみです。 追加のオブジェクトタイプごとに、別の同期リスナーを追加する必要があります。詳しくは、「別の Realm での同期の追跡 」セクションを参照してください。
この例では、各オブジェクトが最後にアップデートされた時刻を追跡します。 その結果、バックアップ邦土の最後に成功した同期の後にいずれかのフィールドが更新された場合は、リカバリ操作によって新しい邦土内のオブジェクト全体が上書きされます。 これにより、このクライアントの古いデータを持つ他のクライアントが更新したフィールドが上書きされる可能性があります。 Realm オブジェクトに重要なデータを含む複数のフィールドが含まれている場合は、代わりに各フィールドの最終更新時間を追跡し、各フィールドを個別に回復することを検討してください。
データリカバリを使用して手動クライアントリセットを実行する他の方法の詳細については、「代替戦略 」セクションを参照してください。
スキーマに最終更新時間を含める
Realm オブジェクト スキーマが最後にアップデートされた時刻を追跡するには、Realm オブジェクト スキーマに新しいプロパティを追加します。 スキーマを使用して Realm オブジェクトを作成または更新するたびに、更新時間にタイムスタンプを含めます。
通常、Realm オブジェクトが最後に変更されたタイミングを検出する方法はありません。 そのため、どの変更がバックエンドに同期されたかを判断するのが困難になります。 Realm オブジェクトモデルにタイムスタンプlastUpdated
を追加し、変更が発生するたびにそのタイムスタンプを現在の時刻に更新することで、オブジェクトが変更されたタイミングを追跡できます。
const DogSchema = { name: "Dog", properties: { name: "string", age: "int?", lastUpdated: "int", }, };
手動クライアントリセットを使用するための Realm の構成
Realm のSyncConfigurationで、 clientReset
フィールドを手動モードに設定し、 error
コールバック関数を含めます。 「クライアントのリセットを処理するためのコールバックの作成 」セクションで、エラー コールバック関数を定義します。
const config = { schema: [DogSchema], sync: { user: app.currentUser, partitionValue: "MyPartitionValue", clientReset: { mode: "manual", }, error: handleSyncError, // callback function defined later }, };
別の Realm での同期の追跡
オブジェクトがいつ変更されたかを認識するだけでは、クライアントのリセット中にデータを回復するのに十分ではありません。 また、Realm が同期を最後に正常に完了したことも確認する必要があります。 この実装例では、 LastSynced
という別のRealmの
Realm が同期する最新の時間を追跡するには、LastSynced Realm を定義します。
const LastSyncedSchema = { name: "LastSynced", properties: { realmTracked: "string", timestamp: "int?", }, primaryKey: "realmTracked", }; const lastSyncedConfig = { schema: [LastSyncedSchema] }; const lastSyncedRealm = await Realm.open(lastSyncedConfig); lastSyncedRealm.write(() => { lastSyncedRealm.create("LastSynced", { realmTracked: "Dog", }); });
Do コレクションへの変更をサブスクライブするために変更リスナーを登録します。 同期セッションが接続され、すべてのローカル変更がサーバーと同期された場合にのみ、 LastSynced オブジェクトを更新します。
// Listens for changes to the Dogs collection realm.objects("Dog").addListener(async () => { // only update LastSynced if sync session is connected // and all local changes are synced if (realm.syncSession.isConnected()) { await realm.syncSession.uploadAllLocalChanges(); lastSyncedRealm.write(() => { lastSyncedRealm.create("LastSynced", { realmTracked: "Dog", timestamp: Date.now(), }); }); } });
クライアントのリセットを処理するためのコールバックの作成
アプリケーション内のすべてのオブジェクトの更新時間と、アプリケーションが最後に同期を完了した時刻を記録したので、手動回復プロセスを実装します。 この例では、2 つの主要なリカバリ操作を処理しています。
バックアップ邦土から同期されていない挿入と更新を復元
バックアップ Realm から以前に削除されたオブジェクトを新しい Realm から削除する
以下のコード サンプルでは、これらの操作の実装に従うことができます。
async function handleSyncError(_session, error) { if (error.name === "ClientReset") { const realmPath = realm.path; // realm.path will not be accessible after realm.close() realm.close(); // you must close all realms before proceeding // pass your realm app instance and realm path to initiateClientReset() Realm.App.Sync.initiateClientReset(app, realmPath); // Redownload the realm realm = await Realm.open(config); const oldRealm = await Realm.open(error.config); const lastSyncedTime = lastSyncedRealm.objectForPrimaryKey( "LastSynced", "Dog" ).timestamp; const unsyncedDogs = oldRealm .objects("Dog") .filtered(`lastUpdated > ${lastSyncedTime}`); // add unsynced dogs to synced realm realm.write(() => { unsyncedDogs.forEach((dog) => { realm.create("Dog", dog, "modified"); }); }); // delete dogs from synced realm that were deleted locally const syncedDogs = realm .objects("Dog") .filtered(`lastUpdated <= ${lastSyncedTime}`); realm.write(() => { syncedDogs.forEach((dog) => { if (!oldRealm.objectForPrimaryKey("Dog", dog._id)) { realm.delete(dog); } }); }); // make sure everything syncs and close old realm await realm.syncSession.uploadAllLocalChanges(); oldRealm.close(); } else { console.log(`Received error ${error.message}`); } }
代替戦略
代替の実装には、次のようなものがあります。
バックエンド全体をバックアップ状態 で上書きします。「最終更新時間」または「最後に同期された時間」がない場合は、バックアップ レルムのすべてのオブジェクトを新しいレルムにアップサートします。 このアプローチでは、同期されていない削除を復元する方法はありません。 このアプローチでは、最後の同期以降に他のクライアントによってバックエンドに書き込まれたすべてのデータが上書きされます。 1 人のユーザーのみが各 Realm に書込むアプリケーションに推奨されます。
フィールド で変更を追跡: すべてのオブジェクトで「最終アップデート時間」を追跡する代わりに、すべてのフィールドの「最終アップデート時間」を追跡します。 このロジックを使用してフィールドを個別に更新し、古いデータを持つ他のクライアントからのフィールド書込みを上書きしないようにします。 フィールド レベルで競合を解決する必要がある、オブジェクトごとに多数のフィールドを持つアプリケーションに推奨されます。
オブジェクト とは別にアップデートを追跡します。各オブジェクトのスキーマで「最終更新時間」を追跡する代わりに、スキーマに
Updates
という別のモデルを作成します。 オブジェクトの任意のフィールド(Updates
以外)が更新されるたびに、プライマリキー、フィールド、更新時間を記録します。 クライアント リセット中に、バックアップ Realm 内のそのフィールドの最新値を使用して、「最終同期時間」後に発生したすべてのUpdate
イベントを「再書き込み」します。 このアプローチでは、古いデータを含むフィールドを上書きすることなく、新しい Realm 内の同期されていないすべてのローカル変更が複製されます。 ただし、アプリケーションが頻繁に書込みを行う場合は、更新のコレクションを保存するとコストが高くなる可能性があります。 オブジェクトモデルへの "lastUpdated" フィールドの追加が望ましくないアプリケーションに推奨されます。