diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d0f451240..22deba5880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Fixed conflict resolution bug related to ArrayErase and Clear instructions, which could sometimes cause an "Invalid prior_size" exception to prevent synchronization ([#7893](https://github.com/realm/realm-core/issues/7893), since v14.8.0). * Fixed bug which would prevent eventual consistency during conflict resolution. Affected clients would experience data divergence and potentially consistency errors as a result. ([PR #7955](https://github.com/realm/realm-core/pull/7955), since v14.8.0) * Fixed issues loading the native Realm libraries on Linux ARMv7 systems when they linked against our bundled OpenSSL resulting in errors like `unexpected reloc type 0x03`. ([#7947](https://github.com/realm/realm-core/issues/7947), since v14.1.0) +* `Realm::convert()` would sometimes incorrectly throw an exception claiming that there were unuploaded local changes when the source Realm is a synchronized Realm ([#7966](https://github.com/realm/realm-core/issues/7966), since v10.7.0). ### Breaking changes * None. diff --git a/src/realm/sync/noinst/client_history_impl.cpp b/src/realm/sync/noinst/client_history_impl.cpp index 3d00f64353..fbd51d8890 100644 --- a/src/realm/sync/noinst/client_history_impl.cpp +++ b/src/realm/sync/noinst/client_history_impl.cpp @@ -1064,7 +1064,12 @@ void ClientHistory::trim_sync_history() bool ClientHistory::no_pending_local_changes(version_type version) const { ensure_updated(version); - for (size_t i = 0; i < sync_history_size(); i++) { + size_t base_version = 0; + auto upload_client_version = + version_type(m_arrays->root.get_as_ref_or_tagged(s_progress_upload_client_version_iip).get_as_int()); + if (upload_client_version > m_sync_history_base_version) + base_version = size_t(upload_client_version - m_sync_history_base_version); + for (size_t i = base_version; i < sync_history_size(); i++) { if (m_arrays->origin_file_idents.get(i) == 0) { std::size_t pos = 0; BinaryData chunk = m_arrays->changesets.get_at(i, pos); diff --git a/test/object-store/realm.cpp b/test/object-store/realm.cpp index bfe434f856..8228be8ec8 100644 --- a/test/object-store/realm.cpp +++ b/test/object-store/realm.cpp @@ -1647,6 +1647,43 @@ TEST_CASE("SharedRealm: convert", "[sync][pbs][convert]") { // Check that the data also exists in the new realm REQUIRE(local_realm2->read_group().get_table("class_object")->size() == 1); } + + SECTION("synced realm must be fully uploaded") { + auto realm = Realm::get_shared_realm(sync_config1); + realm->sync_session()->pause(); + realm->begin_transaction(); + realm->read_group().get_table("class_object")->create_object_with_primary_key(0); + realm->commit_transaction(); + + SyncTestFile sync_config2(tsm, "default"); + sync_config2.schema = schema; + REQUIRE_EXCEPTION(realm->convert(sync_config2), IllegalOperation, + "All client changes must be integrated in server before writing copy"); + + realm->sync_session()->resume(); + wait_for_upload(*realm); + REQUIRE_NOTHROW(realm->convert(sync_config2)); + } + + SECTION("can convert synced realm from within upload complete callback") { + auto realm = Realm::get_shared_realm(sync_config1); + realm->sync_session()->pause(); + realm->begin_transaction(); + realm->read_group().get_table("class_object")->create_object_with_primary_key(0); + realm->commit_transaction(); + + SyncTestFile sync_config2(tsm, "default"); + sync_config2.schema = schema; + auto pf = util::make_promise_future(); + realm->sync_session()->wait_for_upload_completion([&](Status) { + sync_config1.scheduler = util::Scheduler::make_dummy(); + auto realm = Realm::get_shared_realm(sync_config1); + REQUIRE_NOTHROW(realm->convert(sync_config2)); + pf.promise.emplace_value(); + }); + realm->sync_session()->resume(); + pf.future.get(); + } } TEST_CASE("SharedRealm: convert - embedded objects", "[sync][pbs][convert][embedded objects]") {