Docs Menu
Docs Home
/ /
Atlas Device SDK
/ /

클라이언트 재설정 - Java SDK

이 페이지의 내용

  • 동기화되지 않은 변경 내용 삭제
  • 손상적인 스키마 변경 후 동기화되지 않은 변경 사항 삭제
  • 동기화되지 않은 변경 사항 수동 복구
  • 클라이언트 재설정 처리 테스트

클라이언트 재설정에 대해 자세히 알아보기

클라이언트 재설정의 원인과 처리 전략에 학습 보려면 클라이언트 재설정 동기화 페이지를 확인하세요.

SDK는 기기의 Realm 파일을 읽고 씁니다. Atlas Device Sync를 사용하면 이 Realm Mobile Sync이 애플리케이션 백엔드와 동기화됩니다. 일부 조건으로 인해 영역이 백엔드와 동기화되지 않을 수 있습니다. 이 경우 클라이언트 재설정 오류가 발생 합니다.

이 오류는 클라이언트 애플리케이션에서 Realm 파일을 재설정해야 함을 의미합니다. 이 상태의 클라이언트는 계속 실행하고 로컬에서 데이터를 저장할 수 있습니다. 클라이언트 재설정을 수행할 때까지 영역은 백엔드와 동기화되지 않습니다.

클라이언트 재설정 오류를 처리하려면 클라이언트 재설정 전략 을 선택하세요. 이러한 전략은 영역을 동기화 가능한 상태로 복원하지만 장단점이 있습니다.

  • 동기화되지 않은 변경 사항 삭제. 마지막 동기화 이후 로컬 변경 사항을 삭제하여 동기화를 복원합니다. 변경 리스너를 유지 관리합니다.

  • 동기화되지 않은 변경 사항 수동 복구:. 동기화할 수 없는 영역 을 이동하고 새 사본을 다운로드 합니다. 변경 리스너를 무효화합니다.

두 옵션 모두 사용자 지정 로직을 작성하여 로컬 변경 사항을 복구할 수 있습니다. 어떤 옵션도 로컬 변경 사항을 복구할 수 없습니다.

동기화되지 않은 변경 사항 삭제는 수동 복구에 비해 덜 복잡한 대안입니다. 그러나 이 전략으로 모든 클라이언트 재설정 오류를 처리할 수는 없습니다. 수동 클라이언트 재설정 처리기를 대체 수단으로 유지해야 합니다.

버전 10.10.0의 새로운 기능.

동기화되지 않은 변경 사항 삭제 는 SDK에서 제공하는 클라이언트 재설정 전략입니다. 이 전략에는 최소한의 코드만 필요합니다. 이 전략은 영역을 닫거나 알림이 누락되지 않고 재설정을 수행합니다.

마지막으로 성공한 동기화 이후의 모든 로컬 변경 사항 삭제됩니다. 여기에는 영역에 이미 기록되었지만 아직 애플리케이션 백엔드에 동기화되지 않은 모든 데이터가 포함됩니다. 애플리케이션이 동기화되지 않은 데이터를 잃을 수 없는 경우에는 이 전략을 사용하지 마세요.

동기화되지 않은 변경 사항 폐기는 손상적이 거나 파괴적인 스키마 변경 사항을 처리하다 할 수 없습니다. 호환성이 손상되는 변경이 발생하면 SDK는 수동 복구 모드 로 돌아갑니다.

이 전략을 사용하려면 App 을(를) 인스턴스화할 때 DiscardUnsyncedChangesStrategy 인스턴스를 defaultSyncClientResetStrategy() 빌더 메서드에 전달하세요. DiscardUnsyncedChangesStrategy 인스턴스는 다음 메서드를 구현해야 합니다.

다음 예제에서는 이 전략을 구현합니다:

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()
)

수동 복구는 더 이상 사용되지 않는 SyncSession.ClientResetHandler 을(를) 대체합니다. Clients using the deprecated handler can update to manual recovery with no logic changes.

수동 클라이언트 재설정 복구는 권장하지 않습니다. 다음이 필요합니다.

  • 상당한 양의 코드

  • 스키마 양보

  • 복잡한 충돌 해결 로직.

자세한 내용은 수동 클라이언트 재설정 데이터 복구 고급 가이드를 참조하세요.

Device Sync를 종료했다가 다시 활성화하여 애플리케이션의 클라이언트 재설정 처리를 수동으로 테스트할 수 있습니다.

동기화를 종료했다가 다시 허용하면 이전에 동기화로 연결한 클라이언트는 클라이언트 재설정을 수행할 때까지 연결할 수 없습니다. 동기화를 종료하면 클라이언트의 동기화를 허용하는 메타데이터가 서버에서 삭제됩니다. 클라이언트는 서버에서 영역의 새 복사본을 다운로드해야 합니다. 서버는 이러한 클라이언트에 클라이언트 재설정 오류를 보냅니다. 따라서 동기화를 종료하면 trigger 클라이언트 재설정 조건이 됩니다.

클라이언트 재설정 처리를 테스트하려면 다음을 수행합니다.

  1. 클라이언트 애플리케이션에서 데이터를 쓰고 동기화될 때까지 기다립니다.

  2. Realm Mobile Sync를 종료했다가 다시 활성화합니다.

  3. 클라이언트 앱을 다시 실행합니다. 앱이 서버에 연결하려고 할 때 클라이언트 재설정 오류가 발생해야 합니다.

경고

클라이언트 애플리케이션에서 클라이언트 재설정 처리를 반복하는 동안 동기화를 종료했다가 다시 활성화해야 할 수 있습니다. 동기화를 종료했다가 다시 활성화하면 클라이언트 재설정을 완료할 때까지 기존의 모든 클라이언트를 동기화할 수 없습니다. 프로덕션 환경에서 이를 방지하려면 개발 환경에서 클라이언트 재설정 처리를 테스트하세요.

돌아가기

동기화 오류 처리