Docs Menu
Docs Home
/ /
Atlas Device SDK
/ /

クライアントリセット - Java SDK

項目一覧

  • 同期されていない変更の破棄
  • スキーマの重大な変更後に同期されていない変更を破棄する
  • 同期されていない変更の手動回復
  • クライアント リセット処理をテストします

Tip

その他の参照: クライアント リセットの詳細

クライアント リセットの原因と処理方法については、「 クライアント リセットの同期 」ページをご覧ください。

SDK は、デバイス上の Realm ファイルの読み取りと書込みを行います。 Atlas Device Sync を使用すると、このローカル Realm はアプリケーション バックエンドと同期します。 一部の条件により、Realm はバックエンドと同期できなくなる可能性があります。 こうした場合、クライアント リセット エラーが発生します。

このエラーは、クライアント アプリケーションで Realm ファイルをリセットする必要があることを意味します。 この状態のクライアントは引き続きデータを実行し、ローカルに保存できます。 クライアントがリセットされるまで、Realm はバックエンドと同期しません。

クライアント リセット エラーを処理するには、クライアント リセット戦略を選択します。 これらの戦略はRealmを同期可能な状態に復元しますが、トレードオフがあります。

  • 同期されていない変更を破棄します。 前回の同期以降のローカルの変更を破棄して同期を復元します。 変更リスナーを維持します。

  • 同期されていない変更の手動回復: . 同期できない Realm を移動し、新しいコピーをダウンロードします。 変更リスナーを無効にします。

どちらのオプションでも、ローカルの変更を回復するためのカスタム ロジックを記述できます。 どのオプションでもローカル変更を回復することはできません。

同期されていない変更を破棄するは、手動復元よりも複雑ではありません。 ただし、この戦略ではすべてのクライアントのリセット エラーを処理できません。 手動のクライアント リセット ハンドラーをフォールバックとして維持する必要があります。

バージョン10.10.0の新機能

同期されていない変更を破棄するは、SDK によって提供されるクライアント リセット戦略です。 この戦略には最小限のコードが必要です。 この戦略では、Realm を閉じたり通知を欠落したりせずにリセットが実行されます。

ただし、最後の同期が成功した以降に行われたすべてのローカル変更削除されます。 これには、すでに Realm に書き込まれたがアプリケーション バックエンドにまだ同期されていないデータが含まれます。 アプリケーションが同期されていないデータを失うことができない場合は、この戦略を使用しないでください。

同期されていない変更を破棄する と、 スキーマの重大な変更または破壊的な変更 を処理できません。 重大な変更が発生すると、SDK は手動リカバリ モードにフォールバックします。

この戦略を使用するには、 Appをインスタンス化するときに、DishardUnsyncedchangesStratey のインスタンスをデフォルトのSyncClientResetStratey ()ビルダ メソッドに渡します。 DiscardUnsyncedChangesStrategyインスタンスは次のメソッドを実装する必要があります。

  • onBeforeReset()。 SDK は、バックエンドからクライアント リセット エラーを受信したときに、このブロックを呼び出します。 これは、SDK がクライアント リセット戦略を実行する前に発生します。

  • onAfterReset()。この戦略を正常に実行すると、SDK はこのブロックを呼び出します。 このブロックは、元の Realm の固定コピーを提供します。 また、同期可能な状態で Realm のライブ インスタンスも返します。

  • onError()。 SDK は、スキーマの重大な変更中にこのメソッドを呼び出します。 defaultClientResetStrategey () と同様に動作します。

次の例では、この戦略を実装しています。

String appID = YOUR_APP_ID; // replace this with your App ID
App app = new App(new AppConfiguration.Builder(appID)
.defaultSyncClientResetStrategy(new DiscardUnsyncedChangesStrategy() {
@Override
public void onBeforeReset(Realm realm) {
Log.w("EXAMPLE", "Beginning client reset for " + realm.getPath());
}
@Override
public void onAfterReset(Realm before, Realm after) {
Log.w("EXAMPLE", "Finished client reset for " + before.getPath());
}
@Override
public void onError(SyncSession session, ClientResetRequiredError error) {
Log.e("EXAMPLE", "Couldn't handle the client reset automatically." +
" Falling back to manual recovery: " + error.getErrorMessage());
handleManualReset(session.getUser().getApp(), session, error);
}
})
.build());
val appID: String = YOUR_APP_ID // replace this with your App ID
val app = App(
AppConfiguration.Builder(appID)
.defaultSyncClientResetStrategy(object : DiscardUnsyncedChangesStrategy {
override fun onBeforeReset(realm: Realm) {
Log.w("EXAMPLE", "Beginning client reset for " + realm.path)
}
override fun onAfterReset(before: Realm, after: Realm) {
Log.w("EXAMPLE", "Finished client reset for " + before.path)
}
override fun onError(session: SyncSession, error: ClientResetRequiredError) {
Log.e(
"EXAMPLE", "Couldn't handle the client reset automatically." +
" Falling back to manual recovery: " + error.errorMessage
)
handleManualReset(session.user.app, session, error)
}
})
.build()
)

重要

スキーマの重大な変更にはアプリスキーマの更新が必要

スキーマの重大な変更後:

  • すべてのクライアントがクライアント リセットを実行する必要があります。

  • スキーマの重大な変更の影響を受けるクライアントモデルを更新する必要があります。

同期されていない変更の破棄戦略では重大な変更を処理できません。 onError()メソッドでクライアント リセットを手動で処理する必要があります。 この例では、同期されていない変更を手動で破棄し、クライアントのリセットを処理します。

String appID = YOUR_APP_ID; // replace this with your App ID
App app = null;
AtomicReference<App> globalApp = new AtomicReference<>(app);
// accessing the app from within the lambda below requires an effectively final object
app = new App(new AppConfiguration.Builder(appID)
.defaultSyncClientResetStrategy(new DiscardUnsyncedChangesStrategy() {
@Override
public void onBeforeReset(Realm realm) {
Log.w("EXAMPLE", "Beginning client reset for " + realm.getPath());
}
@Override
public void onAfterReset(Realm before, Realm after) {
Log.w("EXAMPLE", "Finished client reset for " + before.getPath());
}
@Override
public void onError(SyncSession session, ClientResetRequiredError error) {
Log.e("EXAMPLE", "Couldn't handle the client reset automatically." +
" Falling back to manual client reset execution: "
+ error.getErrorMessage());
// close all instances of your realm -- this application only uses one
globalRealm.close();
try {
Log.w("EXAMPLE", "About to execute the client reset.");
// execute the client reset, moving the current realm to a backup file
error.executeClientReset();
Log.w("EXAMPLE", "Executed the client reset.");
} catch (IllegalStateException e) {
Log.e("EXAMPLE", "Failed to execute the client reset: " + e.getMessage());
// The client reset can only proceed if there are no open realms.
// if execution failed, ask the user to restart the app, and we'll client reset
// when we first open the app connection.
AlertDialog restartDialog = new AlertDialog.Builder(activity)
.setMessage("Sync error. Restart the application to resume sync.")
.setTitle("Restart to Continue")
.create();
restartDialog.show();
}
// open a new instance of the realm. This initializes a new file for the new realm
// and downloads the backend state. Do this in a background thread so we can wait
// for server changes to fully download.
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
Realm newRealm = Realm.getInstance(globalConfig);
// ensure that the backend state is fully downloaded before proceeding
try {
globalApp.get().getSync().getSession(globalConfig).downloadAllServerChanges(10000,
TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.w("EXAMPLE",
"Downloaded server changes for a fresh instance of the realm.");
newRealm.close();
});
// execute the recovery logic on a background thread
try {
executor.awaitTermination(20000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
})
.build());
globalApp.set(app);
val appID = YOUR_APP_ID // replace this with your App ID
var app: App? = null
app = App(
AppConfiguration.Builder(appID)
.defaultSyncClientResetStrategy(object : DiscardUnsyncedChangesStrategy {
override fun onBeforeReset(realm: Realm) {
Log.w("EXAMPLE", "Beginning client reset for " + realm.path)
}
override fun onAfterReset(before: Realm, after: Realm) {
Log.w("EXAMPLE", "Finished client reset for " + before.path)
}
override fun onError(session: SyncSession, error: ClientResetRequiredError) {
Log.e(
"EXAMPLE", "Couldn't handle the client reset automatically." +
" Falling back to manual client reset execution: "
+ error.errorMessage
)
// close all instances of your realm -- this application only uses one
globalRealm!!.close()
try {
Log.w("EXAMPLE", "About to execute the client reset.")
// execute the client reset, moving the current realm to a backup file
error.executeClientReset()
Log.w("EXAMPLE", "Executed the client reset.")
} catch (e: java.lang.IllegalStateException) {
Log.e("EXAMPLE", "Failed to execute the client reset: " + e.message)
// The client reset can only proceed if there are no open realms.
// if execution failed, ask the user to restart the app, and we'll client reset
// when we first open the app connection.
val restartDialog = AlertDialog.Builder(activity)
.setMessage("Sync error. Restart the application to resume sync.")
.setTitle("Restart to Continue")
.create()
restartDialog.show()
}
// open a new instance of the realm. This initializes a new file for the new realm
// and downloads the backend state. Do this in a background thread so we can wait
// for server changes to fully download.
val executor = Executors.newSingleThreadExecutor()
executor.execute {
val newRealm = Realm.getInstance(globalConfig)
// ensure that the backend state is fully downloaded before proceeding
try {
app!!.sync.getSession(globalConfig)
.downloadAllServerChanges(
10000,
TimeUnit.MILLISECONDS
)
} catch (e: InterruptedException) {
e.printStackTrace()
}
Log.w(
"EXAMPLE",
"Downloaded server changes for a fresh instance of the realm."
)
newRealm.close()
}
// execute the recovery logic on a background thread
try {
executor.awaitTermination(20000, TimeUnit.MILLISECONDS)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
})
.build()
)

Tip

手動復元は、非推奨のSyncSession.ClientResetHandlerを置き換えます。 非推奨の ハンドラーを使用するクライアントは、ロジックを変更せずに手動リカバリに更新できます。

手動クライアントリセットリカバリは推奨されません。 そのためには以下が必要です。

  • 実質的な量のコード

  • スキーマの権限

  • 複雑な競合解決ロジック。

詳細については、 「 手動クライアントリセットデータリカバリに関する高度なガイド 」を参照してください。

Device Sync を終了して再度有効にすることで、アプリケーションのクライアント リセット処理を手動でテストできます。

同期を終了して再度有効にすると、以前に同期に接続したことのあるクライアントは、クライアント リセットを実行するまで接続できなくなります。 同期を終了すると、クライアントが同期できるようにするメタデータがサーバーから削除されます。 クライアントは、サーバーから Realm の新しいコピーをダウンロードする必要があります。 サーバーはこれらのクライアントにクライアント リセット エラーを送信します。 したがって、同期を終了すると、クライアントのリセット条件がtriggerされます。

クライアント リセット処理をテストするには:

  1. クライアント アプリケーションからデータを書き込み、同期されるまで待ちます。

  2. Device Sync を終了して再度有効にします。

  3. クライアント アプリを再度実行します。 アプリは、サーバーに接続しようとすると、クライアント リセット エラーを取得します。

警告

クライアント アプリケーションでクライアント リセット処理を反復処理している間に、同期を繰り返し終了して再度有効にする必要がある場合があります。 同期を終了して再度有効にすると、既存のすべてのクライアントは、クライアントのリセットが完了するまで同期できなくなります。 本番環境でこれを回避するには、開発環境でクライアントのリセット処理をテストします。

戻る

同期エラーの処理