Skip to content

Commit

Permalink
Workaround issue when initiating sync on a copied realm file (#4878)
Browse files Browse the repository at this point in the history
The problem is that the server does not know that the client has already
integrated some server versions, and therefore it sends down 0 as
progress/upload/last_integrated_server_version. On the client this number
may be bigger and in this case that number will not be overwritten with 0.

On the server side, the following behavior is expected: If the IDENT message
indicates that the newly created client has already integraded some server
changesets, adjust the upload threshold accordingly- If the threshold is ahead
of the upload cursor stored in the history, use the threshold values.
  • Loading branch information
jedelbo authored Sep 15, 2021
1 parent 1f08269 commit fb12944
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 12 deletions.
4 changes: 2 additions & 2 deletions src/realm/exec/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ target_link_libraries(RealmTrawler Storage)

add_executable(RealmBrowser EXCLUDE_FROM_ALL realm_browser.cpp )
set_target_properties(RealmBrowser PROPERTIES
OUTPUT_NAME "realm-browser-6"
OUTPUT_NAME "realm-browser-10"
DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}
)
target_link_libraries(RealmBrowser Storage)

add_executable(Realm2JSON realm2json.cpp )
set_target_properties(Realm2JSON PROPERTIES
OUTPUT_NAME "realm2json-6"
OUTPUT_NAME "realm2json-10"
DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}
)
target_link_libraries(Realm2JSON Storage)
Expand Down
12 changes: 8 additions & 4 deletions src/realm/sync/noinst/client_history_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,8 +811,10 @@ void ClientHistoryImpl::update_sync_progress(const SyncProgress& progress,
version_type(root.get_as_ref_or_tagged(s_progress_download_client_version_iip).get_as_int()));
REALM_ASSERT(progress.upload.client_version >=
version_type(root.get_as_ref_or_tagged(s_progress_upload_client_version_iip).get_as_int()));
REALM_ASSERT(progress.upload.last_integrated_server_version >=
version_type(root.get_as_ref_or_tagged(s_progress_upload_server_version_iip).get_as_int()));
if (progress.upload.last_integrated_server_version > 0) {
REALM_ASSERT(progress.upload.last_integrated_server_version >=
version_type(root.get_as_ref_or_tagged(s_progress_upload_server_version_iip).get_as_int()));
}

auto uploaded_bytes = std::uint_fast64_t(root.get_as_ref_or_tagged(s_progress_uploaded_bytes_iip).get_as_int());
auto previous_upload_client_version =
Expand All @@ -829,8 +831,10 @@ void ClientHistoryImpl::update_sync_progress(const SyncProgress& progress,
RefOrTagged::make_tagged(progress.latest_server_version.salt)); // Throws
root.set(s_progress_upload_client_version_iip,
RefOrTagged::make_tagged(progress.upload.client_version)); // Throws
root.set(s_progress_upload_server_version_iip,
RefOrTagged::make_tagged(progress.upload.last_integrated_server_version)); // Throws
if (progress.upload.last_integrated_server_version > 0) {
root.set(s_progress_upload_server_version_iip,
RefOrTagged::make_tagged(progress.upload.last_integrated_server_version)); // Throws
}
if (downloadable_bytes) {
root.set(s_progress_downloadable_bytes_iip,
RefOrTagged::make_tagged(*downloadable_bytes)); // Throws
Expand Down
16 changes: 13 additions & 3 deletions src/realm/sync/noinst/server_history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,8 +850,16 @@ auto ServerHistory::do_bootstrap_client_session(SaltedFileIdent client_file_iden
if (download_progress.server_version > current_server_version)
return BootstrapError::bad_download_server_version;
auto last_integrated_client_version = version_type(m_acc->cf_client_versions.get(client_file_index));
if (download_progress.last_integrated_client_version > last_integrated_client_version)
return BootstrapError::bad_download_client_version;
if (download_progress.last_integrated_client_version > last_integrated_client_version) {
if (last_integrated_client_version == 0) {
// We assume we are booting on a pre-downloaded client file
last_integrated_client_version = download_progress.last_integrated_client_version;
recip_hist_base_version = download_progress.server_version;
}
else {
return BootstrapError::bad_download_client_version;
}
}

// Validate `server_version`
{
Expand Down Expand Up @@ -1014,7 +1022,9 @@ bool ServerHistory::fetch_download_info(file_ident_type client_file_ident, Downl
download_progress = download_progress_2;
cumulative_byte_size_current = std::uint_fast64_t(cumulative_byte_size_current_2);
cumulative_byte_size_total = std::uint_fast64_t(cumulative_byte_size_total_2);
upload_progress = UploadCursor{upload_client_version, upload_server_version};
if (upload_client_version > upload_progress.client_version) {
upload_progress = UploadCursor{upload_client_version, upload_server_version};
}

return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/realm/sync/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3637,7 +3637,7 @@ class Session final : private FileIdentReceiver {
bool body_is_compressed = false;
version_type end_version = last_server_version.version;
DownloadCursor download_progress;
UploadCursor upload_progress = {0, 0};
UploadCursor upload_progress = m_upload_threshold;
std::uint_fast64_t downloadable_bytes = 0;
std::size_t num_changesets;
std::size_t accum_original_size;
Expand Down
10 changes: 8 additions & 2 deletions test/object-store/realm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,13 @@ TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
std::lock_guard<std::mutex> lock(mutex);
return bool(realm_ref);
});
// Write some data
SharedRealm realm = Realm::get_shared_realm(std::move(realm_ref));
realm->begin_transaction();
realm->read_group().get_table("class_object")->create_object_with_primary_key(2);
realm->commit_transaction();
wait_for_upload(*realm);
wait_for_download(*realm);
client_file_id = realm->read_group().get_sync_file_id();
realm->write_copy(config3.path, BinaryData());
}
Expand All @@ -582,7 +588,7 @@ TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
wait_for_download(*realm);
// Make sure we have got a new client file id
REQUIRE(realm->read_group().get_sync_file_id() != client_file_id);
REQUIRE(realm->read_group().get_table("class_object")->size() == 2);
REQUIRE(realm->read_group().get_table("class_object")->size() == 3);

// Check that we can continue committing to this realm
realm->begin_transaction();
Expand All @@ -593,7 +599,7 @@ TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
// Check that this change is now in the original realm
wait_for_download(*origin);
origin->refresh();
REQUIRE(origin->read_group().get_table("class_object")->size() == 3);
REQUIRE(origin->read_group().get_table("class_object")->size() == 4);
}

SECTION("downloads Realms which exist on the server") {
Expand Down
96 changes: 96 additions & 0 deletions test/object-store/sync/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <realm/object-store/sync/mongo_database.hpp>
#include <realm/object-store/sync/mongo_collection.hpp>
#include <realm/object-store/sync/sync_session.hpp>
#include <realm/object-store/thread_safe_reference.hpp>

#include "collection_fixtures.hpp"
#include "util/baas_admin_api.hpp"
Expand Down Expand Up @@ -1777,6 +1778,101 @@ TEST_CASE("app: set new embedded object", "[sync][app]") {
}
}

TEST_CASE("app: make distributable client file", "[sync][app]") {
auto config = get_integration_config();
auto base_path = util::make_temp_dir();
util::try_remove_dir_recursive(base_path);
util::try_make_dir(base_path);
util::try_make_dir(base_path + "/orig");
util::try_make_dir(base_path + "/copy");

// Create realm file without client file id
{
TestSyncManager sync_manager(TestSyncManager::Config(config), {});
auto app = sync_manager.app();
app->log_in_with_credentials(AppCredentials::anonymous(),
[&](std::shared_ptr<SyncUser> user, Optional<app::AppError> error) {
REQUIRE(!error);
REQUIRE(user);
});

ThreadSafeReference realm_ref;

realm::Realm::Config realm_config;
realm_config.sync_config = std::make_shared<realm::SyncConfig>(app->current_user(), bson::Bson("foo"));
realm_config.schema_version = 1;
realm_config.path = base_path + "/orig/default.realm";

std::mutex mutex;
auto task = realm::Realm::get_synchronized_realm(realm_config);
task->start([&](ThreadSafeReference ref, std::exception_ptr error) {
std::lock_guard<std::mutex> lock(mutex);
REQUIRE(!error);
realm_ref = std::move(ref);
});
util::EventLoop::main().run_until([&] {
std::lock_guard<std::mutex> lock(mutex);
return bool(realm_ref);
});
SharedRealm realm = Realm::get_shared_realm(std::move(realm_ref));

// Write some data
realm->begin_transaction();
CppContext c;
Object::create(c, realm, "Person",
util::Any(realm::AnyDict{{"_id", util::Any(ObjectId::gen())},
{"age", INT64_C(64)},
{"firstName", std::string("Paul")},
{"lastName", std::string("McCartney")}}));
realm->commit_transaction();
wait_for_upload(*realm);
wait_for_download(*realm);

realm->write_copy(base_path + "/copy/default.realm", BinaryData());

// Write some additional data
realm->begin_transaction();
Object::create(c, realm, "Dog",
util::Any(realm::AnyDict{{"_id", util::Any(ObjectId::gen())},
{"breed", std::string("stabyhoun")},
{"name", std::string("albert")},
{"realm_id", std::string("foo")}}));
realm->commit_transaction();
wait_for_upload(*realm);
}
// Starting a new session based on the copy
{
TestSyncManager sync_manager(TestSyncManager::Config(config), {});
auto app = sync_manager.app();
app->log_in_with_credentials(AppCredentials::anonymous(),
[&](std::shared_ptr<SyncUser> user, Optional<app::AppError> error) {
REQUIRE(!error);
REQUIRE(user);
});

ThreadSafeReference realm_ref;

realm::Realm::Config realm_config;
realm_config.sync_config = std::make_shared<realm::SyncConfig>(app->current_user(), bson::Bson("foo"));
realm_config.schema_version = 1;
realm_config.path = base_path + "/copy/default.realm";

SharedRealm realm = realm::Realm::get_shared_realm(realm_config);
wait_for_download(*realm);

// Check that we can continue committing to this realm
realm->begin_transaction();
CppContext c;
Object::create(c, realm, "Dog",
util::Any(realm::AnyDict{{"_id", util::Any(ObjectId::gen())},
{"breed", std::string("bulldog")},
{"name", std::string("fido")},
{"realm_id", std::string("foo")}}));
realm->commit_transaction();
wait_for_upload(*realm);
}
}

TEST_CASE("app: sync integration", "[sync][app]") {
const auto schema = Schema{{"Dog",
{{"_id", PropertyType::ObjectId | PropertyType::Nullable, true},
Expand Down
5 changes: 5 additions & 0 deletions tools/run_baas_docker_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash

PROJECT_DIR=$(git rev-parse --show-toplevel)
MDBREALM_TEST_SERVER_TAG=$(grep MDBREALM_TEST_SERVER_TAG ${PROJECT_DIR}/dependencies.list |cut -f 2 -d=)
docker run --rm -p 9090:9090 -v ~/.aws/credentials:/root/.aws/credentials -it docker.pkg.github.com/realm/ci/mongodb-realm-test-server:${MDBREALM_TEST_SERVER_TAG}

0 comments on commit fb12944

Please sign in to comment.