处理同步错误 — Kotlin SDK
在此页面上
在开发使用 Device Sync的应用程序时,应设立错误处理程序。 此错误处理程序将检测并响应任何失败的同步相关API调用。
处理同步错误
通过SyncConfiguration.errorHandler设置错误处理程序 创建同步 Realm 时的属性。 发生错误时,Kotlin SDK 会使用错误对象和发生错误的SyncSession来调用错误处理程序。
如果不指定错误处理程序,则默认行为是将同步错误打印到控制台。
val syncErrorHandler = SyncSession.ErrorHandler { session, error -> Log.e("Error message" + error.message.toString()) } runBlocking { val user = app.login(credentials) val config = SyncConfiguration.Builder(user, setOf(Toad::class)) .initialSubscriptions { realm -> add(realm.query<Toad>(), "subscription name") } .errorHandler(syncErrorHandler) // Specify a sync error handler .build() // Proceed to open a realm... }
有关设置客户端日志级别或自定义记录器的信息,请参阅设置客户端日志级别 - Kotlin SDK。
同步异常
SyncException是AppException的子类。 当 Device Sync 失败时,会出现 SyncException
。
有关应用程序异常的更多信息,请参阅处理应用程序错误。
不可恢复的同步错误
当 Device Sync 灾难性失败时,会出现UnrecoverableSyncException 。 这通常意味着客户端或连接的应用程序中存在错误。
当发生不可恢复的同步错误时,您应向最终用户说明问题。 让他们知道,在问题解决之前,Device Sync 将无法正常工作。 最好向自己发送警报,以便检查后端应用日志并尽快解决问题。
错误的同步类型错误
当客户端和应用程序使用不同的同步协议时,会出现WongSyncTypeException 。
SDK 支持两种同步:灵活同步和基于分区的同步。 当客户端使用与应用的同步类型不匹配的同步类型连接到应用时,会出现错误的同步类型错误。
要从错误的同步类型错误中恢复,请更新客户端以使用与后端匹配的同步类型。 这很可能会要求用户将应用更新到包含修复程序的新版本。
错误的Flexible Sync查询错误
当您尝试订阅应用程序后端不支持的 Flexible同步查询时,会出现BadFlexibleSyncQueryException 异常。 当您执行以下操作时,可能会发生这种情况:
查询在 Flexible Sync 配置中未指定为可查询字段的字段
使用 Flexible Sync 不支持的操作符创建 Flexible Sync 查询
要从错误的Flexible Sync查询错误中恢复,请更新客户端以使用与应用配置兼容的查询。这很可能会要求用户将应用更新到包含修复程序的新版本。
处理客户端重置错误
使用Device Sync时,客户端重置是一项错误恢复任务,当 Device Sync 服务器无法再与客户端 Realm 同步时,客户端应用程序必须执行该任务。 在这种情况下,客户端必须将其 Realm 重置为与服务器匹配的状态,才能恢复同步能力。
发生这种情况时,客户端上的不可同步域可能包含尚未同步到服务器的数据。Realm SDK 可以尝试在客户端重置过程中恢复或丢弃该数据。
有关可能导致客户端重置的原因的更多信息,请Go Atlas App Services文档中的 客户端重置 。
客户端重置策略
Realm SDK 提供了可自动处理大多数客户端重置错误的客户端重置策略以及手动恢复策略。
自动与手动客户端重置
自动重置策略将本地域文件恢复到可同步状态,而无需关闭域或丢失通知。 这些差异取决于它们如何处理设备上尚未同步到后端的更改。 以下策略实现了AutomaticClientResetStrategy接口并支持客户端自动重置:
恢复或丢弃未同步的更改(默认):客户端重置处理程序首先尝试恢复未同步的更改。 如果恢复失败,此处理程序将改用丢弃未同步的更改策略,该策略将永久删除所有未同步的本地更改。 如果丢弃未同步更改策略失败,处理程序将改用手动恢复。
恢复未同步的更改:客户端重置处理程序首先尝试恢复未同步的更改。 如果恢复失败,此处理程序将改用手动恢复。
放弃未同步的更改:此策略会永久删除自上次成功同步以来在本地所做的所有未同步更改。 如果丢弃失败,此处理程序将转为手动恢复。 建议使用此模式来处理任何手动数据恢复。
如果您的应用需要无法自动处理的特定客户端重置逻辑,您可能希望或需要使用ManuallyRecoverUnsyncedChangesStrategy接口添加手动客户端重置处理程序:
手动恢复未同步的更改:允许您实现自己的手动恢复策略。 手动恢复是唯一不执行自动客户端重置的策略。 此模式仅允许备份您的域 。 如果可能,我们建议使用丢弃未同步更改策略来处理手动恢复。
客户端重置与恢复
客户端恢复是配置 Device Sync 时默认启用的功能。
要使用客户端恢复,请使用恢复未同步更改或恢复或丢弃未同步更改策略配置您的域 , Realm在大多数情况下会自动管理客户端重置进程:
当没有模式更改或有非中断性模式更改时,客户端可以恢复未同步的更改。
当您的应用程序进行中断性模式更改时,无法进行客户端自动恢复。 重大更改是您可以在服务器端模式中进行的需要执行额外操作来处理的更改。 在这种情况下,客户端恢复将回退到手动错误客户端重置回退。
有关中断性与非中断性模式更改的信息,请参阅 Atlas App Services文档中的 中断性与非中断性更改快速参考 。
客户端恢复规则
启用客户端恢复后,这些规则将决定如何集成对象,包括当后端和客户端都对同一对象进行更改时如何解决冲突:
同步在客户端重置之前未同步的本地创建的对象。
如果一个对象在服务器上被删除,但在恢复的客户端上被修改,则删除优先,客户端丢弃更新。
如果在正在恢复的客户端上删除了对象,而不是在服务器上删除了对象,则客户端将应用服务器的删除指令。
如果对同一字段的更新发生冲突,则应用客户端更新。
指定客户端重置策略
配置同步域时,您可以在SyncConfiguration.syncClientResetStrategy属性中指定客户端重置策略。
// Specify your client reset strategy in the SyncConfiguration // If you don't specify, defaults to RecoverOrDiscardUnsyncedChangesStrategy val config = SyncConfiguration.Builder(user, setOf(Toad::class)) .initialSubscriptions { realm -> add(realm.query<Toad>(), "subscription name") } .syncClientResetStrategy(clientResetStrategy) // Set your client reset strategy .build()
以下部分介绍如何使用这些客户端重置策略。
恢复或放弃未同步的更改
恢复或放弃未同步的更改策略会尝试在客户端重置期间自动恢复所有未同步的本地更改。 要恢复未同步的更改,必须在App Services App中启用客户端恢复(默认启用)。
如果自动恢复过程失败,则会转而采用丢弃未同步更改的策略。 如果该进程失败,则会再次使用手动重置策略。
此策略提供最稳健的恢复过程。 如果您未指定客户端重置策略,则这是默认的客户端重置行为。
重要
如果您的应用程序不能丢失任何尚未同步到后端的本地数据,则不要使用恢复或丢弃未同步的更改策略。
要自定义恢复或丢弃未同步更改策略的使用,请定义一个实现RecoverOrDiscardUnsyncedChangesStrategy接口的类。
该接口提供以下回调方法:
onBeforeReset :在客户端重置之前调用。 提供重置前域的实例。 您可以使用此回调在客户端重置开始之前通知用户。
onAfterRecovery :当且仅当自动重置成功完成时调用。 提供 Realm 的只读前实例和最终 Realm 的可变实例。 您可以使用此回调通知用户客户端重置已完成。
onAfterDiscard :仅在客户端自动重置失败且丢弃本地策略成功时调用。 如果丢弃策略失败,则不会调用此回调。 提供 Realm 的只读前实例和最终 Realm 的可变实例。 您可以使用此回调通知用户重置已完成。
onManualResetFallback :仅在自动恢复和丢弃策略失败时调用。 放弃本地更改并允许您创建本地 Realm 的备份。 实现此回调以处理重置失败,如手动恢复回退部分中所述。
以下示例显示了RecoverOrDiscardUnsyncedChangesStrategy
及其每个回调:
val clientResetStrategy = object : RecoverOrDiscardUnsyncedChangesStrategy { override fun onBeforeReset(realm: TypedRealm) { Log.i("Client reset: attempting to automatically recover unsynced changes") } // Executed before the client reset begins. // Can be used to notify the user that a reset will happen. override fun onAfterRecovery(before: TypedRealm, after: MutableRealm) { Log.i("Client reset: successfully recovered all unsynced changes") } // Executed if and only if the automatic recovery has succeeded. override fun onAfterDiscard(before: TypedRealm, after: MutableRealm) { Log.i("Client reset: recovery unsuccessful, attempting to manually recover any changes") // ... Try to manually recover any unsynced data manuallyRecoverUnsyncedData(before, after) } // Executed if the automatic recovery has failed, // but the discard unsynced changes fallback has completed successfully. override fun onManualResetFallback( session: SyncSession, exception: ClientResetRequiredException ) { Log.i("Client reset: manual reset required") // ... Handle the reset manually here } // Automatic reset failed. }
恢复未同步的更改
恢复未同步的更改策略尝试在客户端重置期间自动恢复所有未同步的本地更改。 要恢复未同步的更改,必须在 App Services App 中启用客户端恢复(默认启用)。
但是,与恢复并丢弃未同步更改策略不同,如果自动恢复失败,它不会回退到丢弃本地更改。 相反,它会转而手动恢复更改。 如果您的应用不能丢失未同步的数据,则可以选择此客户端重置策略。
要使用恢复未同步更改策略,请定义一个实现RecoverUnsyncedChangesStrategy接口的处理程序。
该接口提供以下回调方法:
onBeforeReset :在客户端重置之前调用。 提供重置前域的实例。 您可以使用此回调在客户端重置开始之前通知用户。
onAfterReset :当且仅当自动重置成功完成时调用。 提供域的只读 before实例和 final 域的可变实例。 您可以使用此回调通知用户客户端重置已完成。
onManualResetFallback :仅在自动恢复失败时调用。 放弃本地更改并允许您创建本地域的备份。 实现此回调以处理重置失败,如手动恢复回退部分中所述。
以下示例显示了RecoverUnsyncedChangesStrategy
及其每个回调:
val clientResetStrategy = object : RecoverUnsyncedChangesStrategy { override fun onBeforeReset(realm: TypedRealm) { Log.i("Client reset: attempting to automatically recover unsynced changes") } // Executed before the client reset begins. // Can be used to notify the user that a reset will happen. override fun onAfterReset(before: TypedRealm, after: MutableRealm) { Log.i("Client reset: successfully recovered all unsynced changes") } // Executed after the client reset is complete. // Can be used to notify the user that the reset is done. override fun onManualResetFallback( session: SyncSession, exception: ClientResetRequiredException ) { Log.i("Client reset: manual reset required") // ... Handle the reset manually here } // Automatic reset failed. }
丢弃未同步的更改
丢弃未同步更改策略会永久删除自上次成功同步以来所做的所有本地未同步更改。 此策略可将本地 Realm 文件恢复到可同步状态,而无需关闭 Realm,同时保持通知正常运行。 如果此过程失败,则会改用手动重置策略。
这是处理任何手动数据恢复的推荐策略。
当您的应用需要与 Device Sync客户端恢复规则不一致的客户端恢复逻辑或者您不想恢复未同步的数据时,您可以选择此策略。
重要
如果您的应用程序不能丢失任何尚未同步到后端的本地数据,则不要使用丢弃未同步的更改策略。
要使用丢弃未同步更改策略,请定义一个实现DiscardUnsyncedChangesStrategy接口的处理程序。
该接口提供以下回调方法:
onBeforeReset :在客户端重置之前调用。 提供重置前域的实例。 您可以使用此回调在客户端重置开始之前通知用户。
onAfterReset :当且仅当重置完成时调用。 提供 Realm 的只读前实例和最终 Realm 的可变实例。 您可以使用此回调通知用户重置已完成。
onManualResetFallback :仅在自动恢复和丢弃策略失败时调用。 放弃本地更改并允许您创建本地 Realm 的备份。 实现此回调以处理重置失败,如手动恢复回退部分中所述。
以下示例显示了DiscardUnsyncedChangesStrategy
及其每个回调:
val clientResetStrategy = object : DiscardUnsyncedChangesStrategy { override fun onBeforeReset(realm: TypedRealm) { Log.i("Client reset: attempting to discard any unsynced changes") } // Executed before the client reset begins. // Can be used to notify the user that a reset will happen. override fun onAfterReset(before: TypedRealm, after: MutableRealm) { Log.i("Client reset: attempting to manually recover any unsynced changes") // ...Try to manually recover any unsynced data manuallyRecoverUnsyncedData(before, after) } // Executed after the client reset is complete. // Can be used to notify the user that the reset is done. override fun onManualResetFallback( session: SyncSession, exception: ClientResetRequiredException ) { Log.i("Client reset: manual reset required") // ... Handle the reset manually here } // Automatic reset failed. override fun onError( session: SyncSession, exception: ClientResetRequiredException ) { // No-op } // Deprecated. onManualResetFallback() used instead. }
手动恢复回退
如果客户端重置无法自动完成(例如在发生中断性模式更改时),则客户端重置过程将由手动错误处理程序完成。
这可能会发生在任何客户端重置策略中:
恢复未同步的更改
恢复或丢弃未同步的更改
丢弃未同步的更改
您必须在这些策略的客户端重置处理程序的onManualResetFallback
回调中提供手动客户端重置实施。
手动重置回退会丢弃本地更改,并允许您使用ClientResetRequiredException.executeClientReset方法创建本地域的备份。
override fun onManualResetFallback( session: SyncSession, exception: ClientResetRequiredException ) { Log.i("Client reset: manual reset required") // You *MUST* close any open Realm instance closeAllRealmInstances(); // `executeClientReset()` creates a backup exception.executeClientReset(); // (Optional) Send backup for analysis handleBackup(recoveryFilePath); // ... Restore the App state by reopening the realm // or restarting the app }
手动恢复未同步的更改
对于需要自定义数据恢复过程的极少数情况,请使用手动恢复未同步的更改策略。 我们建议尽可能使用“丢弃未同步更改”策略来处理手动数据恢复。 仅当自动恢复逻辑不适合您的应用并且您无法丢弃未同步的本地数据时,才应选择手动恢复未同步的更改策略。
要使用手动恢复策略,请使用ManuallyRecoverUnsyncedChangesStrategy接口定义自己的客户端重置处理程序。
在使用此方法之前,您必须关闭正在重置的 域 的所有实例。在手动恢复回调之后和执行客户端重置之前对 Realm 文件进行的任何写入都不会同步。 我们还建议您创建文件备份并报告异常情况。
使用ClientResetRequiredException.executeClientReset()
启动客户端重置。
如果未手动执行客户端重置,则下次关闭并重新打开所有 Realm 实例时(通常是重新启动应用程序时),将自动执行客户端重置。
测试客户端重置处理
您可以通过终止并重新启用 Device Sync 来手动测试应用程序的客户端重置处理。
当您终止并重新启用 Sync 时,之前使用 Sync 连接的客户端在执行客户端重置之前无法进行连接。 终止同步会从服务器中删除允许客户端同步的元数据。 客户端必须从服务器下载 Realm 的新副本。 服务器向这些客户端发送客户端重置错误。 因此,当您终止同步时,就会trigger客户端重置条件。
要测试客户端重置处理,请执行以下操作:
从客户端应用程序写入数据并等待其同步。
终止并重新启用 Device Sync。
再次运行客户端应用程序。 当应用尝试连接到服务器时,应该会出现客户端重置错误。
警告
当您在客户端应用程序中迭代进行客户端重置处理时,您可能需要反复终止并重新启用 Sync。 终止并重新启用同步会导致所有现有客户端在完成客户端重置之前无法进行同步。 为了避免在生产中出现这种情况,请在开发环境中测试客户端重置处理。