클라이언트 재설정 - Java SDK
SDK는 기기의 Realm 파일을 읽고 씁니다. Atlas Device Sync를 사용하면 이 Realm Mobile Sync이 애플리케이션 백엔드와 동기화됩니다. 일부 조건으로 인해 영역이 백엔드와 동기화되지 않을 수 있습니다. 이 경우 클라이언트 재설정 오류가 발생 합니다.
이 오류는 클라이언트 애플리케이션에서 Realm 파일을 재설정해야 함을 의미합니다. 이 상태의 클라이언트는 계속 실행하고 로컬에서 데이터를 저장할 수 있습니다. 클라이언트 재설정을 수행할 때까지 영역은 백엔드와 동기화되지 않습니다.
클라이언트 재설정 오류를 처리하려면 클라이언트 재설정 전략 을 선택하세요. 이러한 전략은 영역을 동기화 가능한 상태로 복원하지만 장단점이 있습니다.
동기화되지 않은 변경 사항 삭제. 마지막 동기화 이후 로컬 변경 사항을 삭제하여 동기화를 복원합니다. 변경 리스너를 유지 관리합니다.
동기화되지 않은 변경 사항 수동 복구:. 동기화할 수 없는 영역 을 이동하고 새 사본을 다운로드 합니다. 변경 리스너를 무효화합니다.
두 옵션 모두 사용자 지정 로직을 작성하여 로컬 변경 사항을 복구할 수 있습니다. 어떤 옵션도 로컬 변경 사항을 복구할 수 없습니다.
동기화되지 않은 변경 사항 삭제는 수동 복구에 비해 덜 복잡한 대안입니다. 그러나 이 전략으로 모든 클라이언트 재설정 오류를 처리할 수는 없습니다. 수동 클라이언트 재설정 처리기를 대체 수단으로 유지해야 합니다.
동기화되지 않은 변경 내용 삭제
버전 10.10.0의 새로운 기능.
동기화되지 않은 변경 사항 삭제 는 SDK에서 제공하는 클라이언트 재설정 전략입니다. 이 전략에는 최소한의 코드만 필요합니다. 이 전략은 영역을 닫거나 알림이 누락되지 않고 재설정을 수행합니다.
마지막으로 성공한 동기화 이후의 모든 로컬 변경 사항 은 삭제됩니다. 여기에는 영역에 이미 기록되었지만 아직 애플리케이션 백엔드에 동기화되지 않은 모든 데이터가 포함됩니다. 애플리케이션이 동기화되지 않은 데이터를 잃을 수 없는 경우에는 이 전략을 사용하지 마세요.
동기화되지 않은 변경 사항 폐기는 손상적이 거나 파괴적인 스키마 변경 사항을 처리하다 할 수 없습니다. 호환성이 손상되는 변경이 발생하면 SDK는 수동 복구 모드 로 돌아갑니다.
이 전략을 사용하려면 App
을(를) 인스턴스화할 때 DiscardUnsyncedChangesStrategy 인스턴스를 defaultSyncClientResetStrategy() 빌더 메서드에 전달하세요. DiscardUnsyncedChangesStrategy
인스턴스는 다음 메서드를 구현해야 합니다.
onBeforeReset()
. SDK는 백엔드에서 클라이언트 재설정 오류를 수신하면 이 차단을 호출합니다. 이는 SDK가 클라이언트 재설정 전략을 실행하기 전에 발생합니다.onAfterReset()
. SDK는 이 전략을 성공적으로 실행한 후 이 차단을 호출합니다. 이 차단은 원본 영역의 동결된 사본을 제공합니다. 또한 동기화 가능한 상태의 영역 인스턴스를 반환합니다.onError()
. SDK는 손상적인 스키마 변경 중에 이 메서드를 호출합니다. defaultClientResetStrategy()와 유사하게 동작합니다.
다음 예제에서는 이 전략을 구현합니다:
String appID = YOUR_APP_ID; // replace this with your App ID App app = new App(new AppConfiguration.Builder(appID) .defaultSyncClientResetStrategy(new DiscardUnsyncedChangesStrategy() { public void onBeforeReset(Realm realm) { Log.w("EXAMPLE", "Beginning client reset for " + realm.getPath()); } public void onAfterReset(Realm before, Realm after) { Log.w("EXAMPLE", "Finished client reset for " + before.getPath()); } 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() { public void onBeforeReset(Realm realm) { Log.w("EXAMPLE", "Beginning client reset for " + realm.getPath()); } public void onAfterReset(Realm before, Realm after) { Log.w("EXAMPLE", "Finished client reset for " + before.getPath()); } 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() )
동기화되지 않은 변경 사항 수동 복구
팁
수동 복구는 더 이상 사용되지 않는 SyncSession.ClientResetHandler
을(를) 대체합니다. Clients using the deprecated handler can update to manual recovery with no logic changes.
수동 클라이언트 재설정 복구는 권장하지 않습니다. 다음이 필요합니다.
상당한 양의 코드
스키마 양보
복잡한 충돌 해결 로직.
클라이언트 재설정 처리 테스트
Device Sync를 종료했다가 다시 활성화하여 애플리케이션의 클라이언트 재설정 처리를 수동으로 테스트할 수 있습니다.
동기화를 종료했다가 다시 허용하면 이전에 동기화로 연결한 클라이언트는 클라이언트 재설정을 수행할 때까지 연결할 수 없습니다. 동기화를 종료하면 클라이언트의 동기화를 허용하는 메타데이터가 서버에서 삭제됩니다. 클라이언트는 서버에서 영역의 새 복사본을 다운로드해야 합니다. 서버는 이러한 클라이언트에 클라이언트 재설정 오류를 보냅니다. 따라서 동기화를 종료하면 trigger 클라이언트 재설정 조건이 됩니다.
클라이언트 재설정 처리를 테스트하려면 다음을 수행합니다.
클라이언트 애플리케이션에서 데이터를 쓰고 동기화될 때까지 기다립니다.
Realm Mobile Sync를 종료했다가 다시 활성화합니다.
클라이언트 앱을 다시 실행합니다. 앱이 서버에 연결하려고 할 때 클라이언트 재설정 오류가 발생해야 합니다.
경고
클라이언트 애플리케이션에서 클라이언트 재설정 처리를 반복하는 동안 동기화를 종료했다가 다시 활성화해야 할 수 있습니다. 동기화를 종료했다가 다시 활성화하면 클라이언트 재설정을 완료할 때까지 기존의 모든 클라이언트를 동기화할 수 없습니다. 프로덕션 환경에서 이를 방지하려면 개발 환경에서 클라이언트 재설정 처리를 테스트하세요.