同期エラーの処理 - React Native SDK
項目一覧
Realm アプリで Atlas Device Sync を使用すると、新しいクラスのエラーが発生する可能性があります: 同期エラー。
Realm React Native SDKを使用すると、同期エラーを検出して処理できます。 たとえば、特定のエラーに応答する独自の同期エラー ハンドラーを記述できます。 また、クライアント アプリがクライアント リセットを処理する方法を定義することもできます。
同期エラー ハンドラー
Atlas Device Sync を使用するアプリのエラー ハンドラーを設定する必要があります。 汎用エラー ハンドラーは、失敗した同期関連の API 呼び出しを検出し、応答します。
汎用同期エラー ハンドラーを追加する
一般的な同期エラー ハンドラーは、同期エラーを追跡するのに適した方法です。 FlexibleSyncConfigurationを使用して、エラー処理の動作を定義できます。
一般的な同期エラー ハンドラーを追加するには、次の手順に従います。
エラー ハンドラー関数を記述します。
RealmProvider
のFlexibleSyncConfiguration
オブジェクトを作成します。エラー ハンドラーを
FlexibleSyncConfiguration
オブジェクトのonError
プロパティに渡します。
const syncConfigWithErrorHandling = { flexible: true, onError: (_session, error) => { console.log(error); }, }; function RealmWithErrorHandling() { return ( <RealmProvider sync={syncConfigWithErrorHandling}> <RestOfApp /> </RealmProvider> ); }
Tip
一般的な Device Sync エラーのリストとその処理方法については、App Services Device Sync ドキュメントの「同期エラー 」を参照してください。
書込みエラーの処理
アプリに合った方法で、書込みエラーを補填する場合、同期エラー ハンドラーを具体的に指定する必要がある場合があります。 CompassingWriteErrorクラスは、カスタム エラー ハンドラーの書込み (write) エラーを識別し、 Reactが埋め込まれるようにするのに役立ちます。
書込みエラーの修正を処理するには、次の手順に従います。
CompensatingWriteError
を使用して書き込みエラーを識別するエラー ハンドラー関数を記述します。RealmProvider
のFlexibleSyncConfiguration
オブジェクトを作成します。エラー ハンドラーを
FlexibleSyncConfiguration
オブジェクトのonError
プロパティに渡します。
export const CompensatingWriteErrorHandling = () => { const [error, setError] = useState<CompensatingWriteError | undefined>( undefined, ); // Create a callback for sync error handling using CompensatingWriteError const errorCallback: ErrorCallback = (_session, error) => { if (error instanceof CompensatingWriteError) { // Handle the compensating write error as needed console.debug({ code: error.code, name: error.name, category: error.category, message: error.message, url: error.logUrl, writes: error.writes, }); setError(error); } }; return ( <AppProvider id={APP_ID}> <UserProvider fallback={LogIn}> <RealmProvider schema={[Person, Turtle]} sync={{ flexible: true, onError: errorCallback, }}> <CompensatingWriteErrorHandler error={error} /> </RealmProvider> </UserProvider> </AppProvider> ); };
クライアント リセット エラーの処理
クライアント リセット エラーとは、クライアント Realm が Atlas App Services バックエンドとデータを同期できないタイプの同期エラーです。 この状態のクライアントは引き続きデータを実行してローカルに保存できますが、クライアントがリセットされるまで同期の変更セットを送信または受信することはできません。
クライアント リセットの原因とクライアント リセットの処理モードの詳細については、App Services ドキュメントの「 Device Sync クライアント リセット」を参照してください。
クライアント リセット モード
アプリが Realm を同期可能な状態に復元するために使用するクライアント リセット モードを指定できます。
同期されていない変更モードを回復: このモードを選択すると、クライアントは同期されていない変更の回復を試みます。 同期されていない変更を破棄するために失敗したくない場合は、このモードを選択します。
同期されていない変更モードの回復または破棄: クライアントは最初に、まだ同期されていない変更の回復を試みます。 クライアントが同期されていないデータを復元できない場合、同期されていない変更を破棄しますが、クライアントのリセットの自動実行は続行されます。 同期されていない変更を破棄するためにバックアップする自動クライアントリカバリを有効にする場合は、このモードを選択します。
同期されていない変更を破棄モード: 前回の同期以降に行われた変更を破棄して、Realm を同期可能な状態に復元します。
手動リカバリ モード: Realm の新しいコピーをダウンロードし、同期できない Realm をバックアップに移動します。 Realm のバックアップ コピーから新しい同期可能なコピーに同期されていないデータを移行します。
自動クライアントリセットと手動クライアントリセット
Realm SDK は、ほとんどのクライアント リセット エラーを自動的に処理するクライアント リセット モードを提供します。
自動クライアント リセット モードでは、Realm を閉じたり通知が欠落したりすることなく、ローカル Realm ファイルが同期可能な状態に復元されます。 次のクライアント リセット モードは自動クライアント リセットをサポートします。
同期されていない変更モードを回復する
同期されていない変更モードを回復または破棄する
同期されていない変更モードを破棄する
これらのモード間の違いは、バックエンドにまだ同期されていないデバイス上の変更をどのように処理するかに基づいています。 手動リカバリ モードのみでは、自動クライアントリセットは実行されません。
ほとんどのクライアントリセットシナリオを自動的に処理するには、 同期されていない変更を回復する モードを選択します。 これは、クライアントがリセットしたときに同期されていない変更を回復しようとします。
自動的に処理できない特定のクライアントリセットロジックがアプリに必要な場合は、自動クライアントリセットモードに手動クライアントリセットハンドラーを追加することをお勧めします。または追加する必要がある場合があります。
回復によるクライアントリセット
バージョンrealm@10.23.0
の新機能。
クライアントリカバリは、Device Sync を構成するとデフォルトで有効になる機能です。 クライアントリカバリが有効になっている場合、Realm はほとんどの場合、クライアントのリセットプロセスを自動的に管理します。 クライアントは、スキーマ変更がない場合、またはスキーマの重大でない変更がない場合に、同期されていない変更を回復できます。
クライアントリカバリを使用するには、次のいずれかのクライアントリセットモードを使用して Realm を構成します。
同期されていない変更モードを回復する
同期されていない変更を回復または破棄する
クライアントリカバリが有効になっている場合、バックエンドとクライアントの両方が同じオブジェクトに変更を加えた場合に競合が解決される方法など、オブジェクトの統合方法がこれらのルールによって決まります。
クライアントがリセットされる前に同期されなかったローカルで作成されたオブジェクトが同期されます。
オブジェクトがサーバー上で削除されたが、復元されたクライアントで変更された場合は、削除が優先され、クライアントは更新を破棄します。
リカバリ クライアントでオブジェクトが削除されたが、サーバーでは削除された場合、クライアントはサーバーの削除指示を適用します。
同じフィールドへの更新が競合する場合は、クライアント更新が適用されます。
クライアントリカバリの構成の詳細については、App Services ドキュメントの「クライアントリカバリ 」を参照してください。
アプリがスキーマに重大な変更を加えた場合、クライアント回復は成功しません。 重大な変更とは、処理するために追加のアクションが必要になる、サーバー側のスキーマで行うことができる変更です。 このシナリオでは、クライアント リセットは手動エラー クライアント リセット フォールバックにフォールバックします。
スキーマの重大な変更と重大でない変更の詳細については、App Services ドキュメントの「 重大な変更と重大でない変更のクイック リファレンス」を参照してください。
同期されていない変更モードを回復する
同期されていない変更モードを選択すると、クライアントは クライアントリカバリ を使用して同期されていない変更を回復しようとします。 同期されていない変更を破棄するために失敗したくない場合は、このモードを選択します。
同期されていない変更モードを使用してクライアントのリセットを処理するには、clientReset
FlexibleSyncConfiguration の フィールドに ClientResetConfiguration を渡します。これらのプロパティをClientResetConfiguration
に含めます。
mode
:"recoverUnsyncedChanges"
に設定します。onBefore
: 任意。 SDK がバックエンドからクライアント リセット エラーを受信したときに、SDK がこのモードを実行する前に呼び出されるコールバック 関数。 Realm のコピーを提供します。onAfter
: 任意。 SDK がこのモードを正常に実行した後に呼び出されるコールバック関数。 クライアントのリセットの前後でRealmのインスタンスを提供します。onFallback
: 任意。 自動リカバリが失敗した場合にのみ SDK が呼び出すコールバック関数。 詳細については、「手動クライアントリセットフォールバック 」セクションを参照してください。
次の例では、同期されていない変更のリカバリ モードを実装しています。
const syncConfigWithRecoverClientReset = { flexible: true, clientReset: { mode: 'recoverUnsyncedChanges', onBefore: realm => { // This block could be used for custom recovery, reporting, debugging etc. }, onAfter: (beforeRealm, afterRealm) => { // This block could be used for custom recovery, reporting, debugging etc. }, onFallback: (session, path) => { // See below "Manual Client Reset Fallback" section for example }, }, }; function RealmWithRecoverUnsyncedChangesClientReset() { return ( <RealmProvider sync={syncConfigWithRecoverClientReset}> <RestOfApp /> </RealmProvider> ); }
const syncConfigWithRecoverClientReset = { flexible: true, clientReset: { mode: Realm.ClientResetMode.RecoverUnsyncedChanges, onBefore: realm => { // This block could be used for custom recovery, reporting, debugging etc. }, onAfter: (beforeRealm, afterRealm) => { // This block could be used for custom recovery, reporting, debugging etc. }, onFallback: (session, path) => { // See below "Manual Client Reset Fallback" section for example }, }, }; function RealmWithRecoverUnsyncedChangesClientReset() { return ( <RealmProvider sync={syncConfigWithRecoverClientReset}> <RestOfApp /> </RealmProvider> ); }
同期されていない変更モードの回復または破棄
同期されていない変更を回復または破棄するモードでは、クライアントは最初に、まだ同期されていない変更の回復を試みます。 クライアントが同期されていないデータを復元できない場合、同期されていない変更を破棄しますが、クライアントのリセットの自動実行は続行されます。 同期されていない変更を破棄するためにバックアップする自動クライアントリカバリを有効にする場合は、このモードを選択します。
バックエンドにまだ同期されていないローカル データをアプリケーションが失うことができない場合は、同期されていない変更モードを回復または破棄しないでください。
リカバリ または 同期されていない変更モードを使用してクライアントのリセットを処理するには、clientReset
FlexibleSyncConfiguration の フィールドに ClientResetConfiguration を渡します。これらのプロパティを に含めます。ClientResetConfiguration
mode
:"recoverOrDiscardUnsyncedChanges"
に設定します。onBefore
: 任意。 SDK がバックエンドからクライアント リセット エラーを受信したときに、SDK がこのモードを実行する前に呼び出されるコールバック 関数。 Realm のコピーを提供します。onAfter
: 任意。 SDK がこのモードを正常に実行した後に呼び出されるコールバック関数。 クライアントのリセットの前後でRealmのインスタンスを提供します。onFallback()
: 任意。 自動復元と変更の破棄の両方が失敗した場合にのみ SDK が呼び出すコールバック関数。 詳細については、「手動クライアントリセットフォールバック 」セクションを参照してください。
次の例では、同期されていない変更のリカバリ モードを実装しています。
const syncConfigWithRecoverDiscardClientReset = { flexible: true, clientReset: { mode: 'recoverOrDiscardUnsyncedChanges', onBefore: realm => { // This block could be used for custom recovery, reporting, debugging etc. }, onAfter: (beforeRealm, afterRealm) => { // This block could be used for custom recovery, reporting, debugging etc. }, onFallback: (session, path) => { // See below "Manual Client Reset Fallback" section for example }, }, }; function RealmWithRecoverOrDiscardUnsyncedChangesClientReset() { return ( <RealmProvider sync={syncConfigWithRecoverDiscardClientReset}> <RestOfApp /> </RealmProvider> ); }
const syncConfigWithRecoverDiscardClientReset = { flexible: true, clientReset: { mode: Realm.ClientResetMode.RecoverOrDiscardUnsyncedChanges, onBefore: realm => { // This block could be used for custom recovery, reporting, debugging etc. }, onAfter: (beforeRealm, afterRealm) => { // This block could be used for custom recovery, reporting, debugging etc. }, onFallback: (session, path) => { // See below "Manual Client Reset Fallback" section for example }, }, }; function RealmWithRecoverOrDiscardUnsyncedChangesClientReset() { return ( <RealmProvider sync={syncConfigWithRecoverDiscardClientReset}> <RestOfApp /> </RealmProvider> ); }
手動クライアントリセットフォールバック
スキーマの重大な変更が発生した場合など、リカバリを使用したクライアントのリセットが自動的に完了しない場合、クライアントのリセットプロセスは手動のエラーハンドラーに降格します。 これは、リカバリ モードでのクライアント リセット、同期されていない変更の回復、および同期されていない変更の回復または破棄のいずれかで発生する可能性があります。
SyncConfiguration.onFallback()
コールバックで手動クライアント リセットの実装を指定する必要があります。 onFallback()
は 2 つの引数を取ります。
session
: Device Sync セッションの状態を表すセッションオブジェクト。path
: 現在の Realm ファイルへのパスを含む string。
次の例では、同期されていない変更をすべて破棄して、このエラーを手動で処理する方法を示しています。
let realm; // value assigned in <RestOfApp> with useRealm() const syncConfigWithClientResetFallback = { flexible: true, clientReset: { mode: 'recoverOrDiscardUnsyncedChanges', // or "recoverUnsyncedChanges" // can also include `onBefore` and `onAfter` callbacks onFallback: (_session, path) => { try { // 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. const didUserConfirmReset = showUserAConfirmationDialog(); if (didUserConfirmReset) { // Close and delete old realm from device realm.close(); Realm.deleteFile(path); // Perform client reset Realm.App.Sync.initiateClientReset(app, path); // Navigate the user back to the main page or reopen the // the Realm and reinitialize the current page } } catch (err) { // Reset failed. Notify user that they'll need to // update the app } }, }, }; function RealmWithManualClientResetFallback() { return ( <RealmProvider sync={syncConfigWithClientResetFallback}> <RestOfApp /> </RealmProvider> ); } function RestOfApp() { // Assigning variable defined above to a realm. realm = useRealm(); return <>{/* Other components in rest of app */}</>; }
let realm; // value assigned in <RestOfApp> with useRealm() const syncConfigWithClientResetFallback = { flexible: true, clientReset: { mode: Realm.ClientResetMode.RecoverOrDiscardUnsyncedChanges, // or "recoverUnsyncedChanges" // can also include `onBefore` and `onAfter` callbacks onFallback: (_session, path) => { try { // 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. const didUserConfirmReset = showUserAConfirmationDialog(); if (didUserConfirmReset) { // Close and delete old realm from device realm.close(); Realm.deleteFile(path); // Perform client reset Realm.App.Sync.initiateClientReset(app, path); // Navigate the user back to the main page or reopen the // the Realm and reinitialize the current page } } catch (err) { // Reset failed. Notify user that they'll need to // update the app } }, }, }; function RealmWithManualClientResetFallback() { return ( <RealmProvider sync={syncConfigWithClientResetFallback}> <RestOfApp /> </RealmProvider> ); } function RestOfApp() { // Assigning variable defined above to a realm. realm = useRealm(); return <>{/* Other components in rest of app */}</>; }
同期されていない変更モードを破棄する
バージョンrealm@10.11.0
の新機能。
バージョンrealm@10.23.0
での変更: モードの名前が「discardLocal」から「discardUnsyncedchanges」に名前が変更されました。 現在はどちらも動作していますが、将来のバージョンでは "discardLocal" が削除される予定です。 "clientResetBefore" と "clientResetAfter" コールバックの名前が、それぞれ "onBefore" と "onAfter" に変更されました。
同期されていない変更を破棄するモードでは、最後の同期が成功した以降に行われたすべてのローカルの変更が永続的に削除されます。 アプリが自動 クライアント回復と整合しないクライアント回復ロジックを必要とする場合、または同期されていないデータを回復したくない場合に、このモードを使用することがあります。
バックエンドにまだ同期されていないローカル データをアプリケーションが失うことができない場合は、同期されていない変更モードを使用しないでください。
同期されていない変更を破棄するモードでクライアントのリセットを処理するには、 FlexibleSyncConfiguration の フィールドに ClientResetConfiguration clientReset
を渡します。これらのプロパティを に含めます。ClientResetConfiguration
mode
:"discardUnsyncedChanges"
に設定します。onBefore
: 任意。 SDK がバックエンドからクライアント リセット エラーを受信したときに、SDK がこのモードを実行する前に呼び出されるコールバック 関数。 Realm のコピーを提供します。onAfter
: 任意。 SDK がこのモードを正常に実行した後に呼び出されるコールバック関数。 クライアントのリセットの前後でRealmのインスタンスを提供します。
次の例では、同期されていない変更を破棄するモードを実装しています。
const syncConfigWithDiscardClientReset = { flexible: true, clientReset: { mode: 'discardUnsyncedChanges', onBefore: realm => { console.log('Beginning client reset for ', realm.path); }, onAfter: (beforeRealm, afterRealm) => { console.log('Finished client reset for', beforeRealm.path); console.log('New realm path', afterRealm.path); }, }, }; function RealmWitDiscardUnsyncedChangesClientReset() { return ( <RealmProvider sync={syncConfigWithDiscardClientReset}> <RestOfApp /> </RealmProvider> ); }
const syncConfigWithDiscardClientReset = { flexible: true, clientReset: { mode: Realm.ClientResetMode.DiscardUnsyncedChanges, onBefore: realm => { console.log('Beginning client reset for ', realm.path); }, onAfter: (beforeRealm, afterRealm) => { console.log('Finished client reset for', beforeRealm.path); console.log('New realm path', afterRealm.path); }, }, }; function RealmWitDiscardUnsyncedChangesClientReset() { return ( <RealmProvider sync={syncConfigWithDiscardClientReset}> <RestOfApp /> </RealmProvider> ); }
スキーマの重大な変更後に同期されていない変更を破棄する
アプリケーションに重大なスキーマ変更が発生した場合、 同期されていない変更を破棄する モードでは、結果として発生するクライアントのリセットを自動的に処理できません。 代わりに、SyncConfiguration error()
コールバックで手動クライアント リセットの実装を指定する必要があります。 次の例では、同期されていない変更をすべて破棄して、このエラーを手動で処理する方法を示しています。
// Once you have opened your Realm, you will have to keep a reference to it. // In the error handler, this reference is called `realm` async function handleSyncError(session, syncError) { if (syncError.name == 'ClientReset') { console.log(syncError); try { console.log('error type is ClientReset....'); const path = realm.path; // realm.path will not be accessible after realm.close() realm.close(); Realm.App.Sync.initiateClientReset(app, path); // Download Realm from the server. // Ensure that the backend state is fully downloaded before proceeding, // which is the default behavior. realm = await Realm.open(config); realm.close(); } catch (err) { console.error(err); } } else { // ...handle other error types } } const syncConfigWithDiscardAfterBreakingSchemaChanges = { flexible: true, clientReset: { mode: 'discardUnsyncedChanges', onBefore: realm => { // NOT used with destructive schema changes console.log('Beginning client reset for ', realm.path); }, onAfter: (beforeRealm, afterRealm) => { // Destructive schema changes do not hit this function. // Instead, they go through the error handler. console.log('Finished client reset for', beforeRealm.path); console.log('New realm path', afterRealm.path); }, }, onError: handleSyncError, // invoked with destructive schema changes }; function RealmWitDiscardAfterBreakingSchemaChangesClientReset() { return ( <RealmProvider sync={syncConfigWithDiscardAfterBreakingSchemaChanges}> <RestOfApp /> </RealmProvider> ); }
// Once you have opened your Realm, you will have to keep a reference to it. // In the error handler, this reference is called `realm` async function handleSyncError(session, syncError) { if (syncError.name == 'ClientReset') { console.log(syncError); try { console.log('error type is ClientReset....'); const path = realm.path; // realm.path will not be accessible after realm.close() realm.close(); Realm.App.Sync.initiateClientReset(app, path); // Download Realm from the server. // Ensure that the backend state is fully downloaded before proceeding, // which is the default behavior. realm = await Realm.open(config); realm.close(); } catch (err) { console.error(err); } } else { // ...handle other error types } } const syncConfigWithDiscardAfterBreakingSchemaChanges = { flexible: true, clientReset: { mode: Realm.ClientResetMode.DiscardUnsyncedChanges, onBefore: realm => { // NOT used with destructive schema changes console.log('Beginning client reset for ', realm.path); }, onAfter: (beforeRealm, afterRealm) => { // Destructive schema changes do not hit this function. // Instead, they go through the error handler. console.log('Finished client reset for', beforeRealm.path); console.log('New realm path', afterRealm.path); }, }, onError: handleSyncError, // invoked with destructive schema changes }; function RealmWitDiscardAfterBreakingSchemaChangesClientReset() { return ( <RealmProvider sync={syncConfigWithDiscardAfterBreakingSchemaChanges}> <RestOfApp /> </RealmProvider> ); }
手動モード
バージョンrealm@10.23.0
で変更: onMany コールバックが追加されました
手動モードでは、独自のクライアント リセット ハンドラーを定義します。 自動リカバリ ロジックがアプリで機能せず、同期されていないローカル データを破棄できない場合は、手動のクライアント リセット ハンドラーを使用することをお勧めします。
手動モードでクライアントのリセットを処理するには、 ClientResetConfigurationをFlexibleSyncConfigurationのclientReset
フィールドに渡します。これらのプロパティをClientResetConfiguration
に含めます。
mode
:"manual"
に設定します。onManual
: 任意。 クライアントがリセットされたときに呼び出されるコールバック関数。 同期セッションと現在の Realm へのパスに関する情報を提供します。onManual
エラー ハンドラーを設定しない場合、クライアント リセット エラーは一般的な同期エラー ハンドラーにフォールバックします。
const syncConfigWithManualClientReset = { flexible: true, clientReset: { mode: 'manual', onManual: (session, path) => { // handle manual client reset here }, }, }; function RealmWitManualClientReset() { return ( <RealmProvider sync={syncConfigWithManualClientReset}> <RestOfApp /> </RealmProvider> ); }
const syncConfigWithManualClientReset = { flexible: true, clientReset: { mode: 'manual', onManual: (session, path) => { // handle manual client reset here }, }, }; function RealmWitManualClientReset() { return ( <RealmProvider sync={syncConfigWithManualClientReset}> <RestOfApp /> </RealmProvider> ); }
手動データリカバリ
手動クライアントリセットからデータを回復するには、大量のコード、スキーマ認可、カスタム競合解決ロジックが必要です。 独自のカスタム クライアント リセット ロジックを実装する必要がある場合は、「手動クライアント リセット データリカバリに関する詳細ガイド 」を参照してください。
クライアント リセット処理をテストします
Device Sync を終了して再度有効にすることで、アプリケーションのクライアント リセット処理を手動でテストできます。
同期を終了して再度有効にすると、以前に同期に接続したことのあるクライアントは、クライアント リセットを実行するまで接続できなくなります。 同期を終了すると、クライアントが同期できるようにするメタデータがサーバーから削除されます。 クライアントは、サーバーから Realm の新しいコピーをダウンロードする必要があります。 サーバーはこれらのクライアントにクライアント リセット エラーを送信します。 したがって、同期を終了すると、クライアントのリセット条件がtriggerされます。
クライアント リセット処理をテストするには:
クライアント アプリケーションからデータを書き込み、同期されるまで待ちます。
Device Sync を終了して再度有効にします。
クライアント アプリを再度実行します。 アプリは、サーバーに接続しようとすると、クライアント リセット エラーを取得します。
警告
クライアント アプリケーションでクライアント リセット処理を反復処理している間に、同期を繰り返し終了して再度有効にする必要がある場合があります。 同期を終了して再度有効にすると、既存のすべてのクライアントは、クライアントのリセットが完了するまで同期できなくなります。 本番環境でこれを回避するには、開発環境でクライアントのリセット処理をテストします。