Skip to content

Commit 8f54801

Browse files
RCORE-1980 Add unit test and changelog entry for replication bug (#7537)
* Add unit test and changelog entry for replication bug * Update changelog * Revert replication of clear instruction on empty lists * Revert tests too
1 parent ce2c0fe commit 8f54801

File tree

4 files changed

+70
-7
lines changed

4 files changed

+70
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
### Fixed
77
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
88
* Fix an assertion failure "m_lock_info && m_lock_info->m_file.get_path() == m_filename" that appears to be related to opening a Realm while the file is in the process of being closed on another thread ([Swift #8507](https://github.com/realm/realm-swift/issues/8507)).
9+
* Fixed diverging history due to a bug in the replication code when setting default null values (embedded objects included) ([#7536](https://github.com/realm/realm-core/issues/7536)).
910

1011
### Breaking changes
1112
* Updated default base URL to be `https://services.cloud.mongodb.com` to support the new domains (was `https://realm.mongodb.com`). ([PR #7534](https://github.com/realm/realm-core/pull/7534))

src/realm/list.hpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -928,11 +928,10 @@ inline LnkLst Obj::get_linklist(StringData col_name) const
928928
template <class T>
929929
void Lst<T>::clear()
930930
{
931-
auto sz = size();
932-
if (Replication* repl = Base::get_replication()) {
933-
repl->list_clear(*this);
934-
}
935-
if (sz > 0) {
931+
if (size() > 0) {
932+
if (Replication* repl = Base::get_replication()) {
933+
repl->list_clear(*this);
934+
}
936935
do_clear();
937936
bump_content_version();
938937
}

test/object-store/object.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,10 +2023,10 @@ TEST_CASE("object") {
20232023

20242024
Obj obj = object1.get_obj();
20252025
REQUIRE(obj.get<Int>("_id") == 7); // pk
2026-
REQUIRE(obj.get_linklist("array 1").size() == 1);
2026+
REQUIRE(obj.get_linklist("array 1").size() == 2);
20272027
REQUIRE(obj.get<Int>("int 1") == 1); // non-default from r1
20282028
REQUIRE(obj.get<Int>("int 2") == 2); // non-default from r2
2029-
REQUIRE(obj.get_linklist("array 2").size() == 1);
2029+
REQUIRE(obj.get_linklist("array 2").size() == 2);
20302030
}
20312031
#endif
20322032
}

test/test_transform.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3011,4 +3011,67 @@ TEST(Transform_ClearArrayVsUpdateInt)
30113011
CHECK_EQUAL(table->get_object_with_primary_key(1).get_any(col_any), 42);
30123012
}
30133013

3014+
TEST(Transform_SetNullDefaultVsSetDefault)
3015+
{
3016+
auto changeset_dump_dir_gen = get_changeset_dump_dir_generator(test_context);
3017+
auto server = Peer::create_server(test_context, changeset_dump_dir_gen.get());
3018+
auto client_1 = Peer::create_client(test_context, 2, changeset_dump_dir_gen.get());
3019+
auto client_2 = Peer::create_client(test_context, 3, changeset_dump_dir_gen.get());
3020+
3021+
// Baseline: no data
3022+
client_1->transaction([](Peer& c) {
3023+
auto& tr = *c.group;
3024+
TableRef table = tr.add_table_with_primary_key("class_Table", type_Int, "id", true);
3025+
bool is_default = true;
3026+
table->add_column(type_Mixed, "mixed", is_default);
3027+
table->add_column(type_Int, "int");
3028+
});
3029+
3030+
synchronize(server.get(), {client_1.get(), client_2.get()});
3031+
3032+
client_1->history.set_time(1);
3033+
client_2->history.set_time(2);
3034+
3035+
// Client 1 creates object with primary key '1' and sets property 'mixed' to a default value of 'null' and
3036+
// property 'int' to '42'
3037+
// {id: 1, mixed: null, int: 42}
3038+
client_1->transaction([](Peer& p) {
3039+
auto obj = p.table("class_Table")->create_object_with_primary_key(1);
3040+
auto col_mixed = p.table("class_Table")->get_column_key("mixed");
3041+
auto col_int = p.table("class_Table")->get_column_key("int");
3042+
bool is_default = true;
3043+
obj.set_null(col_mixed, is_default);
3044+
obj.set(col_int, 42, is_default);
3045+
});
3046+
3047+
// Client 2 creates object with primary key '1' and sets property 'mixed' to a default value of '-6' and property
3048+
// 'int' to '6'
3049+
// {id: 1, mixed: -6, int: 6}
3050+
client_2->transaction([](Peer& p) {
3051+
auto obj = p.table("class_Table")->create_object_with_primary_key(1);
3052+
auto col_mixed = p.table("class_Table")->get_column_key("mixed");
3053+
auto col_int = p.table("class_Table")->get_column_key("int");
3054+
bool is_default = true;
3055+
obj.set(col_mixed, Mixed(-6), is_default);
3056+
obj.set(col_int, 6, is_default);
3057+
});
3058+
3059+
synchronize(server.get(), {client_1.get(), client_2.get()});
3060+
3061+
// Result: Client 2's changes win because they happen after Client 1's changes (and is_default is true for all
3062+
// instructions)
3063+
// {id: 1, mixed: -6, int: 6}
3064+
ReadTransaction read_server(server->shared_group);
3065+
ReadTransaction read_client_1(client_1->shared_group);
3066+
ReadTransaction read_client_2(client_2->shared_group);
3067+
CHECK(compare_groups(read_server, read_client_1));
3068+
CHECK(compare_groups(read_server, read_client_2, *test_context.logger));
3069+
auto table = read_server.get_table("class_Table");
3070+
auto col_mixed = table->get_column_key("mixed");
3071+
auto col_int = table->get_column_key("int");
3072+
auto obj = table->get_object_with_primary_key(1);
3073+
CHECK_EQUAL(obj.get_any(col_mixed), Mixed(-6));
3074+
CHECK_EQUAL(obj.get_any(col_int), Mixed(6));
3075+
}
3076+
30143077
} // unnamed namespace

0 commit comments

Comments
 (0)