Skip to content

Commit 291bf9a

Browse files
authored
Fix sync replication (#7343)
1 parent c273976 commit 291bf9a

File tree

10 files changed

+62
-49
lines changed

10 files changed

+62
-49
lines changed

src/realm/collection.hpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ class DummyParent : public CollectionParent {
3535
{
3636
return {};
3737
}
38+
ColKey get_col_key() const noexcept final
39+
{
40+
return {};
41+
}
3842
void add_index(Path&, const Index&) const noexcept final {}
3943
size_t find_index(const Index&) const noexcept final
4044
{
@@ -478,7 +482,7 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
478482
}
479483

480484
// Overriding members of CollectionBase:
481-
ColKey get_col_key() const noexcept final
485+
ColKey get_col_key() const noexcept override
482486
{
483487
return m_col_key;
484488
}
@@ -576,6 +580,7 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
576580
Obj m_obj_mem;
577581
std::shared_ptr<CollectionParent> m_col_parent;
578582
CollectionParent::Index m_index;
583+
mutable size_t m_my_version = 0;
579584
ColKey m_col_key;
580585
bool m_nullable = false;
581586

@@ -618,6 +623,7 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
618623
CollectionBaseImpl(CollectionParent& parent, CollectionParent::Index index) noexcept
619624
: m_obj_mem(parent.get_object())
620625
, m_index(index)
626+
, m_col_key(parent.get_col_key())
621627
, m_parent(&parent)
622628
, m_alloc(&m_obj_mem.get_alloc())
623629
{
@@ -655,8 +661,9 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
655661

656662
if (status != UpdateStatus::Detached) {
657663
auto content_version = m_alloc->get_content_version();
658-
if (content_version != m_content_version) {
664+
if (content_version != m_content_version || m_my_version != m_parent->m_parent_version) {
659665
m_content_version = content_version;
666+
m_my_version = m_parent->m_parent_version;
660667
status = UpdateStatus::Updated;
661668
}
662669
}
@@ -673,8 +680,9 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
673680
bool changed = m_parent->update_if_needed(); // Throws if the object does not exist.
674681
auto content_version = m_alloc->get_content_version();
675682

676-
if (changed || content_version != m_content_version) {
683+
if (changed || content_version != m_content_version || m_my_version != m_parent->m_parent_version) {
677684
m_content_version = content_version;
685+
m_my_version = m_parent->m_parent_version;
678686
return true;
679687
}
680688
return false;

src/realm/collection_parent.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class CollectionParent : public std::enable_shared_from_this<CollectionParent> {
8484
virtual FullPath get_path() const = 0;
8585
// Return path from owning object
8686
virtual Path get_short_path() const = 0;
87+
// Return column of owning property
88+
virtual ColKey get_col_key() const noexcept = 0;
8789
// Return path from owning object
8890
virtual StablePath get_stable_path() const = 0;
8991
// Add a translation of Index to PathElement
@@ -105,6 +107,7 @@ class CollectionParent : public std::enable_shared_from_this<CollectionParent> {
105107
static constexpr size_t s_max_level = 100;
106108
#endif
107109
size_t m_level = 0;
110+
mutable size_t m_parent_version = 0;
108111

109112
constexpr CollectionParent(size_t level = 0)
110113
: m_level(level)

src/realm/dictionary.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -436,13 +436,17 @@ void Dictionary::insert_collection(const PathElement& path_elem, CollectionType
436436

437437
check_level();
438438
ensure_created();
439-
m_values->ensure_keys();
440-
auto [it, inserted] = insert(path_elem.get_key(), Mixed(0, dict_or_list));
441-
int64_t key = generate_key(size());
442-
while (m_values->find_key(key) != realm::not_found) {
443-
key++;
439+
Mixed new_val(0, dict_or_list);
440+
auto old_val = try_get(path_elem.get_key());
441+
if (!old_val || *old_val != new_val) {
442+
m_values->ensure_keys();
443+
auto [it, inserted] = insert(path_elem.get_key(), new_val);
444+
int64_t key = generate_key(size());
445+
while (m_values->find_key(key) != realm::not_found) {
446+
key++;
447+
}
448+
m_values->set_key(it.index(), key);
444449
}
445-
m_values->set_key(it.index(), key);
446450
}
447451

448452
DictionaryPtr Dictionary::get_dictionary(const PathElement& path_elem) const
@@ -668,6 +672,7 @@ UpdateStatus Dictionary::update_if_needed_with_status() const
668672
// the function will return false;
669673
bool attached = init_from_parent(false);
670674
Base::update_content_version();
675+
CollectionParent::m_parent_version++;
671676
return attached ? UpdateStatus::Updated : UpdateStatus::Detached;
672677
}
673678
}
@@ -681,6 +686,7 @@ void Dictionary::ensure_created()
681686
// In case of errors, an exception is thrown.
682687
constexpr bool allow_create = true;
683688
init_from_parent(allow_create); // Throws
689+
CollectionParent::m_parent_version++;
684690
Base::update_content_version();
685691
}
686692
}

src/realm/dictionary.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ class Dictionary final : public CollectionBaseImpl<DictionaryBase>, public Colle
196196
return Base::get_short_path();
197197
}
198198

199+
ColKey get_col_key() const noexcept override
200+
{
201+
return Base::get_col_key();
202+
}
203+
199204
StablePath get_stable_path() const override
200205
{
201206
return Base::get_stable_path();

src/realm/list.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ UpdateStatus Lst<Mixed>::update_if_needed_with_status() const
396396
case UpdateStatus::Updated: {
397397
bool attached = init_from_parent(false);
398398
Base::update_content_version();
399+
CollectionParent::m_parent_version++;
399400
return attached ? UpdateStatus::Updated : UpdateStatus::Detached;
400401
}
401402
}

src/realm/list.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ class Lst<Mixed> final : public CollectionBaseImpl<LstBase>, public CollectionPa
458458
constexpr bool allow_create = true;
459459
init_from_parent(allow_create); // Throws
460460
Base::update_content_version();
461+
CollectionParent::m_parent_version++;
461462
}
462463
}
463464

@@ -484,6 +485,11 @@ class Lst<Mixed> final : public CollectionBaseImpl<LstBase>, public CollectionPa
484485
return Base::get_stable_path();
485486
}
486487

488+
ColKey get_col_key() const noexcept override
489+
{
490+
return Base::get_col_key();
491+
}
492+
487493
void add_index(Path& path, const Index& ndx) const final;
488494
size_t find_index(const Index& ndx) const final;
489495

src/realm/obj.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ bool Obj::update() const
385385
if (changes) {
386386
m_mem = new_obj.m_mem;
387387
m_row_ndx = new_obj.m_row_ndx;
388+
CollectionParent::m_parent_version++;
388389
}
389390
// Always update versions
390391
m_storage_version = new_obj.m_storage_version;
@@ -422,6 +423,7 @@ UpdateStatus Obj::update_if_needed_with_status() const
422423
if ((m_mem.get_addr() != state.mem.get_addr()) || (m_row_ndx != state.index)) {
423424
m_mem = state.mem;
424425
m_row_ndx = state.index;
426+
CollectionParent::m_parent_version++;
425427
return UpdateStatus::Updated;
426428
}
427429
}
@@ -1087,6 +1089,11 @@ Path Obj::get_short_path() const noexcept
10871089
return {};
10881090
}
10891091

1092+
ColKey Obj::get_col_key() const noexcept
1093+
{
1094+
return {};
1095+
}
1096+
10901097
StablePath Obj::get_stable_path() const noexcept
10911098
{
10921099
return {};

src/realm/obj.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class Obj : public CollectionParent {
7676
FullPath get_path() const final;
7777
std::string get_id() const;
7878
Path get_short_path() const noexcept final;
79+
ColKey get_col_key() const noexcept final;
7980
StablePath get_stable_path() const noexcept final;
8081
void add_index(Path& path, const Index& ndx) const final;
8182
size_t find_index(const Index&) const final

test/test_list.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -693,10 +693,13 @@ TEST(List_Nested_InMixed)
693693
*/
694694

695695
tr->promote_to_write();
696-
dict2->insert_collection("List", CollectionType::List);
696+
dict->insert_collection("Dict", CollectionType::Dictionary); // Idempotent, but updates dict accessor
697+
dict2->insert_collection("List", CollectionType::List); // dict2 should update
697698
{
698699
auto list = dict2->get_list("List");
700+
CHECK_EQUAL(dict2->get_col_key(), col_any);
699701
CHECK(list->is_empty());
702+
CHECK_EQUAL(list->get_col_key(), col_any);
700703
list->add(8);
701704
list->add(9);
702705
}
@@ -1060,7 +1063,10 @@ TEST(List_Nested_Replication)
10601063
StablePath expected_path;
10611064
} parser(test_context);
10621065

1066+
auto dict2_index = dict->build_index("level1");
10631067
parser.expected_path.push_back(StableIndex());
1064-
parser.expected_path.push_back(dict->build_index("level1"));
1068+
parser.expected_path.push_back(dict2_index);
10651069
tr->advance_read(&parser);
1070+
Dictionary dict3(*dict, dict2_index);
1071+
CHECK_EQUAL(dict3.get_col_key(), col_any);
10661072
}

test/test_sync.cpp

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6140,6 +6140,8 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
61406140
dict->insert_collection("list", CollectionType::List);
61416141
auto l = dict->get_list("list");
61426142
l->add(5);
6143+
l->insert_collection(1, CollectionType::List);
6144+
l->get_list(1)->add(7);
61436145

61446146
auto bar = table->create_object_with_primary_key(456);
61456147

@@ -6148,15 +6150,6 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
61486150
auto list = bar.get_list_ptr<Mixed>(col_any);
61496151
list->add("John");
61506152
list->insert(0, 5);
6151-
6152-
auto foobar = table->create_object_with_primary_key(789);
6153-
6154-
// Create set in Mixed property
6155-
foobar.set_collection(col_any, CollectionType::Set);
6156-
auto set = foobar.get_set_ptr<Mixed>(col_any);
6157-
set->insert(1);
6158-
set->insert(2);
6159-
set->insert(5);
61606153
});
61616154

61626155
session_1.wait_for_upload_complete_or_client_stopped();
@@ -6165,7 +6158,7 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
61656158
write_transaction(db_2, [&](WriteTransaction& tr) {
61666159
auto table = tr.get_table("class_Table");
61676160
auto col_any = table->get_column_key("any");
6168-
CHECK_EQUAL(table->size(), 3);
6161+
CHECK_EQUAL(table->size(), 2);
61696162

61706163
auto obj = table->get_object_with_primary_key(123);
61716164
auto dict = obj.get_dictionary_ptr(col_any);
@@ -6195,15 +6188,6 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
61956188
list->set(1, "Paul");
61966189
// Erase list element
61976190
list->remove(0);
6198-
6199-
obj = table->get_object_with_primary_key(789);
6200-
auto set = obj.get_set_ptr<Mixed>(col_any);
6201-
// Check that values are replicated
6202-
CHECK_NOT_EQUAL(set->find(1), realm::npos);
6203-
CHECK_NOT_EQUAL(set->find(2), realm::npos);
6204-
CHECK_NOT_EQUAL(set->find(5), realm::npos);
6205-
// Erase set element
6206-
set->erase(2);
62076191
});
62086192

62096193
session_2.wait_for_upload_complete_or_client_stopped();
@@ -6212,7 +6196,7 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
62126196
write_transaction(db_1, [&](WriteTransaction& tr) {
62136197
auto table = tr.get_table("class_Table");
62146198
auto col_any = table->get_column_key("any");
6215-
CHECK_EQUAL(table->size(), 3);
6199+
CHECK_EQUAL(table->size(), 2);
62166200

62176201
auto obj = table->get_object_with_primary_key(123);
62186202
auto dict = obj.get_dictionary(col_any);
@@ -6232,11 +6216,6 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
62326216
CHECK_EQUAL(list->get(0).get_string(), "Paul");
62336217
// List clear
62346218
list->clear();
6235-
6236-
obj = table->get_object_with_primary_key(789);
6237-
auto set = obj.get_set_ptr<Mixed>(col_any);
6238-
CHECK_EQUAL(set->size(), 2);
6239-
set->clear();
62406219
});
62416220

62426221
session_1.wait_for_upload_complete_or_client_stopped();
@@ -6246,7 +6225,7 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
62466225
auto table = tr.get_table("class_Table");
62476226
auto col_any = table->get_column_key("any");
62486227

6249-
CHECK_EQUAL(table->size(), 3);
6228+
CHECK_EQUAL(table->size(), 2);
62506229

62516230
auto obj = table->get_object_with_primary_key(123);
62526231
auto dict = obj.get_dictionary(col_any);
@@ -6258,14 +6237,9 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
62586237
obj = table->get_object_with_primary_key(456);
62596238
auto list = obj.get_list<Mixed>(col_any);
62606239
CHECK_EQUAL(list.size(), 0);
6261-
// Replace list with set on property
6262-
obj.set_collection(col_any, CollectionType::Set);
6263-
6264-
obj = table->get_object_with_primary_key(789);
6265-
auto set = obj.get_set<Mixed>(col_any);
6266-
CHECK_EQUAL(set.size(), 0);
6267-
// Replace set with dictionary on property
6240+
// Replace list with Dictionary on property
62686241
obj.set_collection(col_any, CollectionType::Dictionary);
6242+
62696243
});
62706244

62716245
session_2.wait_for_upload_complete_or_client_stopped();
@@ -6278,17 +6252,13 @@ TEST_IF(Sync_CollectionInMixed, sync::SYNC_SUPPORTS_NESTED_COLLECTIONS)
62786252
auto table = read_2.get_table("class_Table");
62796253
auto col_any = table->get_column_key("any");
62806254

6281-
CHECK_EQUAL(table->size(), 3);
6255+
CHECK_EQUAL(table->size(), 2);
62826256

62836257
auto obj = table->get_object_with_primary_key(123);
62846258
auto list = obj.get_list<Mixed>(col_any);
62856259
CHECK_EQUAL(list.size(), 0);
62866260

62876261
obj = table->get_object_with_primary_key(456);
6288-
auto set = obj.get_set<Mixed>(col_any);
6289-
CHECK_EQUAL(set.size(), 0);
6290-
6291-
obj = table->get_object_with_primary_key(789);
62926262
auto dict = obj.get_dictionary(col_any);
62936263
CHECK_EQUAL(dict.size(), 0);
62946264

0 commit comments

Comments
 (0)