Skip to content

Commit 8ecb791

Browse files
Clear incomplete bootstraps when the connection is established
1 parent c729fc8 commit 8ecb791

File tree

4 files changed

+50
-12
lines changed

4 files changed

+50
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
### Fixed
88
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
9-
* None.
9+
* If the client disconnects while a bootstrap is in progresss, stale data from the previous bootstrap may be included when the client reconnects and the bootstrap is restarted. This can lead to objects stored in the database that do not match the actual state of the server and potentially leading to compensating writes. ([#7707](https://github.com/realm/realm-core/issues/7707), since v12.0.0)
1010

1111
### Breaking changes
1212
* None.

src/realm/sync/noinst/client_impl_base.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,7 @@ Session* Connection::find_and_validate_session(session_ident_type session_ident,
12961296
logger.error("Bad session identifier in %1 message, session_ident = %2", message, session_ident);
12971297
close_due_to_protocol_error(
12981298
{ErrorCodes::SyncProtocolInvariantFailed,
1299-
util::format("Received message %1 for session iden %2 when that session never existed", message,
1299+
util::format("Received message %1 for session ident %2 when that session never existed", message,
13001300
session_ident)});
13011301
}
13021302
else {
@@ -1709,16 +1709,6 @@ void Session::activate()
17091709
REALM_ASSERT(!m_suspended);
17101710
m_conn.one_more_active_unsuspended_session(); // Throws
17111711

1712-
try {
1713-
process_pending_flx_bootstrap(); // throws
1714-
}
1715-
catch (const IntegrationException& error) {
1716-
on_integration_failure(error);
1717-
}
1718-
catch (...) {
1719-
on_integration_failure(IntegrationException(exception_to_status()));
1720-
}
1721-
17221712
// Checks if there is a pending client reset
17231713
handle_pending_client_reset_acknowledgement();
17241714
}

src/realm/sync/noinst/client_impl_base.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,16 @@ inline void ClientImpl::Session::connection_established(bool fast_reconnect)
13981398
// the bind messsage
13991399
call_debug_hook(SyncClientHookEvent::SessionConnected);
14001400

1401+
try {
1402+
process_pending_flx_bootstrap(); // throws
1403+
}
1404+
catch (const IntegrationException& error) {
1405+
on_integration_failure(error);
1406+
}
1407+
catch (...) {
1408+
on_integration_failure(IntegrationException(exception_to_status()));
1409+
}
1410+
14011411
if (!m_suspended) {
14021412
// Ready to send BIND message
14031413
enlist_to_send(); // Throws

test/object-store/sync/flx_sync.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5174,6 +5174,44 @@ TEST_CASE("flx: no upload during bootstraps", "[sync][flx][bootstrap][baas]") {
51745174
wait_for_download(*realm);
51755175
}
51765176

5177+
TEST_CASE("flx: bootstrap is properly applied when the connection is reestablished", "[sync][flx][bootstrap][baas]") {
5178+
FLXSyncTestHarness harness("flx_bootstrap", {g_large_array_schema, {"queryable_int_field"}});
5179+
auto& app_session = harness.session().app_session();
5180+
REQUIRE(app_session.admin_api.patch_app_settings(app_session.server_app_id,
5181+
{{"sync", {{"num_objects_before_bootstrap_flush", 1}}}}));
5182+
REQUIRE(app_session.admin_api.patch_app_settings(
5183+
app_session.server_app_id, {{"sync", {{"qbs_download_changeset_soft_max_byte_size", 1000}}}}));
5184+
5185+
fill_large_array_schema(harness);
5186+
auto config = harness.make_test_file();
5187+
bool once = false;
5188+
config.sync_config->on_sync_client_event_hook = [&once](std::weak_ptr<SyncSession>,
5189+
const SyncClientHookData& data) {
5190+
if (data.query_version != 1) {
5191+
return SyncClientHookAction::NoAction;
5192+
}
5193+
if (data.event == SyncClientHookEvent::BootstrapMessageProcessed && !once) {
5194+
once = true;
5195+
return SyncClientHookAction::TriggerReconnect;
5196+
}
5197+
// The batch of changesets added to the PendingBoostrapStore before disconnect
5198+
// were removed when the connection was reestablished.
5199+
// There are 5 changesets in 5 download messages and one additional download message
5200+
// with an empty changeset (as per server design).
5201+
if (data.event == SyncClientHookEvent::BootstrapProcessed) {
5202+
CHECK(data.num_changesets == 6);
5203+
}
5204+
return SyncClientHookAction::NoAction;
5205+
};
5206+
5207+
auto realm = Realm::get_shared_realm(config);
5208+
auto table = realm->read_group().get_table("class_TopLevel");
5209+
auto new_subs = realm->get_latest_subscription_set().make_mutable_copy();
5210+
new_subs.insert_or_assign(Query(table));
5211+
auto subs = new_subs.commit();
5212+
subs.get_state_change_notification(sync::SubscriptionSet::State::Complete).get();
5213+
}
5214+
51775215
} // namespace realm::app
51785216

51795217
#endif // REALM_ENABLE_AUTH_TESTS

0 commit comments

Comments
 (0)