-
Notifications
You must be signed in to change notification settings - Fork 167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix crash when opening FLX realm after client reset failure #6671
Changes from 5 commits
cf58a7e
3bce4a0
dfc3b65
31e4a81
161b76b
538fdab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ | |
#include "realm/sync/client_base.hpp" | ||
#include "realm/sync/config.hpp" | ||
#include "realm/sync/noinst/client_history_impl.hpp" | ||
#include <realm/sync/noinst/client_reset_operation.hpp> | ||
#include "realm/sync/noinst/pending_bootstrap_store.hpp" | ||
#include "realm/sync/noinst/server/access_token.hpp" | ||
#include "realm/sync/protocol.hpp" | ||
|
@@ -282,6 +283,11 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { | |
{"list_of_ints_field", PropertyType::Int | PropertyType::Array}, | ||
{"sum_of_list_field", PropertyType::Int}, | ||
}}, | ||
{"TopLevel2", | ||
{ | ||
{"_id", PropertyType::ObjectId, Property::IsPrimary{true}}, | ||
{"queryable_str_field", PropertyType::String | PropertyType::Nullable}, | ||
}}, | ||
}; | ||
|
||
// some of these tests make additive schema changes which is only allowed in dev mode | ||
|
@@ -432,6 +438,56 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { | |
->run(); | ||
} | ||
|
||
SECTION("Recover: subscription and offline writes after client reset failure") { | ||
config_local.sync_config->client_resync_mode = ClientResyncMode::Recover; | ||
auto&& [error_future, error_handler] = make_error_handler(); | ||
config_local.sync_config->error_handler = error_handler; | ||
|
||
std::string fresh_path = realm::_impl::ClientResetOperation::get_fresh_path_for(config_local.path); | ||
// create a non-empty directory that we'll fail to delete | ||
util::make_dir(fresh_path); | ||
util::File(util::File::resolve("file", fresh_path), util::File::mode_Write); | ||
|
||
auto test_reset = reset_utils::make_baas_flx_client_reset(config_local, config_remote, harness.session()); | ||
test_reset | ||
->make_local_changes([&](SharedRealm local_realm) { | ||
auto mut_sub = local_realm->get_latest_subscription_set().make_mutable_copy(); | ||
auto table = local_realm->read_group().get_table("class_TopLevel2"); | ||
mut_sub.insert_or_assign(Query(table)); | ||
mut_sub.commit(); | ||
|
||
CppContext c(local_realm); | ||
local_realm->begin_transaction(); | ||
Object::create(c, local_realm, "TopLevel2", | ||
std::any(AnyDict{{"_id"s, ObjectId::gen()}, {"queryable_str_field"s, "foo"s}})); | ||
local_realm->commit_transaction(); | ||
}) | ||
->on_post_reset([](SharedRealm local_realm) { | ||
// Verify offline subscription was not removed. | ||
auto subs = local_realm->get_latest_subscription_set(); | ||
auto table = local_realm->read_group().get_table("class_TopLevel2"); | ||
REQUIRE(subs.find(Query(table))); | ||
}) | ||
->run(); | ||
|
||
// Remove the folder preventing the completion of a client reset. | ||
util::try_remove_dir_recursive(fresh_path); | ||
|
||
auto config_copy = config_local; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this is working, but I think you could strengthen this test by using |
||
config_local.sync_config->error_handler = nullptr; | ||
|
||
// Attempt to open the realm again. | ||
// This time the client reset succeesds and the offline subscription and writes are recovered. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo on "succeeds" |
||
auto realm = Realm::get_shared_realm(config_copy); | ||
wait_for_download(*realm); | ||
wait_for_upload(*realm); | ||
|
||
auto table = realm->read_group().get_table("class_TopLevel2"); | ||
auto str_col = table->get_column_key("queryable_str_field"); | ||
REQUIRE(table->size() == 1); | ||
REQUIRE(table->get_object(0).get<String>(str_col) == "foo"); | ||
} | ||
|
||
SECTION("Recover: offline writes and subscriptions (multiple subscriptions)") { | ||
config_local.sync_config->client_resync_mode = ClientResyncMode::Recover; | ||
auto&& [reset_future, reset_handler] = make_client_reset_handler(); | ||
|
@@ -811,6 +867,36 @@ TEST_CASE("flx: client reset", "[sync][flx][app][client reset]") { | |
}) | ||
->run(); | ||
} | ||
|
||
SECTION("DiscardLocal: open realm after client reset failure") { | ||
config_local.sync_config->client_resync_mode = ClientResyncMode::DiscardLocal; | ||
auto&& [error_future, error_handler] = make_error_handler(); | ||
config_local.sync_config->error_handler = error_handler; | ||
|
||
std::string fresh_path = realm::_impl::ClientResetOperation::get_fresh_path_for(config_local.path); | ||
// create a non-empty directory that we'll fail to delete | ||
util::make_dir(fresh_path); | ||
util::File(util::File::resolve("file", fresh_path), util::File::mode_Write); | ||
|
||
auto test_reset = reset_utils::make_baas_flx_client_reset(config_local, config_remote, harness.session()); | ||
test_reset->run(); | ||
|
||
// Client reset fails due to sync client not being able to create the fresh realm. | ||
auto sync_error = wait_for_future(std::move(error_future)).get(); | ||
CHECK(sync_error.get_system_error() == sync::make_error_code(sync::ClientError::auto_client_reset_failure)); | ||
|
||
// Open the realm again. This should not crash. | ||
{ | ||
auto config_copy = config_local; | ||
auto&& [err_future, err_handler] = make_error_handler(); | ||
config_local.sync_config->error_handler = err_handler; | ||
|
||
auto realm_post_reset = Realm::get_shared_realm(config_copy); | ||
auto sync_error = wait_for_future(std::move(err_future)).get(); | ||
CHECK(sync_error.get_system_error() == | ||
sync::make_error_code(sync::ClientError::auto_client_reset_failure)); | ||
} | ||
} | ||
} | ||
|
||
TEST_CASE("flx: creating an object on a class with no subscription throws", "[sync][flx][app]") { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯