Skip to content

Commit 96e30e6

Browse files
authored
Fix overlay patch mutation bug (#10579)
* Fix overlay patch mutation bug * Add change log * Add include * Feedback
1 parent 391d9f2 commit 96e30e6

File tree

9 files changed

+94
-5
lines changed

9 files changed

+94
-5
lines changed

Firestore/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Unreleased
2+
- [fixed] Fix an issue that stops some performance optimization being applied.
3+
14
# 10.3.0
25
- [feature] Add MultiDb support.
36
- [fixed] Fix App crashed when there are nested data structures inside IN

Firestore/core/src/local/local_documents_view.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ model::OverlayedDocumentMap LocalDocumentsView::ComputeViews(
266266
{doc->key(), overlay_it->second.mutation().field_mask()});
267267
overlay_it->second.mutation().ApplyToLocalView(*doc, absl::nullopt,
268268
Timestamp::Now());
269+
} else { // No overlay for this document
270+
// Using empty mask to indicate there is no overlay for the document.
271+
mutated_fields.emplace(doc->key(), FieldMask{});
269272
}
270273
}
271274

Firestore/core/src/model/overlayed_document.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ namespace firebase {
2626
namespace firestore {
2727
namespace model {
2828

29-
/** Represents a local view (overlay) of a document, and the fields that are
30-
* locally mutated. */
29+
/**
30+
* Represents a local view (overlay) of a document, and the fields that are
31+
* locally mutated.
32+
*/
3133
class OverlayedDocument {
3234
public:
3335
OverlayedDocument(model::Document document,
@@ -44,6 +46,13 @@ class OverlayedDocument {
4446
return std::move(document_);
4547
}
4648

49+
/**
50+
* The fields that are locally mutated by patch mutations.
51+
*
52+
* If the overlayed document is from set or delete mutations, returns
53+
* `nullopt`. If there is no overlay (mutation) for the document, returns
54+
* empty `FieldMask`.
55+
*/
4756
const absl::optional<model::FieldMask>& mutated_fields() const {
4857
return mutated_fields_;
4958
}

Firestore/core/test/unit/local/counting_query_engine.cc

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ void CountingQueryEngine::ResetCounts() {
6060
overlays_read_by_key_ = 0;
6161
overlays_read_by_collection_ = 0;
6262
overlays_read_by_collection_group_ = 0;
63+
overlay_types_.clear();
6364
}
6465

6566
// MARK: - WrappedMutationQueue
@@ -198,7 +199,13 @@ model::MutableDocumentMap WrappedRemoteDocumentCache::GetAll(
198199
absl::optional<model::Overlay> WrappedDocumentOverlayCache::GetOverlay(
199200
const model::DocumentKey& key) const {
200201
++query_engine_->overlays_read_by_key_;
201-
return subject_->GetOverlay(key);
202+
auto result = subject_->GetOverlay(key);
203+
if (result.has_value()) {
204+
query_engine_->overlay_types_.emplace(key,
205+
result.value().mutation().type());
206+
}
207+
208+
return result;
202209
}
203210

204211
void WrappedDocumentOverlayCache::SaveOverlays(
@@ -214,6 +221,10 @@ OverlayByDocumentKeyMap WrappedDocumentOverlayCache::GetOverlays(
214221
const model::ResourcePath& collection, int since_batch_id) const {
215222
auto result = subject_->GetOverlays(collection, since_batch_id);
216223
query_engine_->overlays_read_by_collection_ += result.size();
224+
for (const auto& r : result) {
225+
query_engine_->overlay_types_.emplace(r.first, r.second.mutation().type());
226+
}
227+
217228
return result;
218229
}
219230

@@ -223,6 +234,10 @@ OverlayByDocumentKeyMap WrappedDocumentOverlayCache::GetOverlays(
223234
std::size_t count) const {
224235
auto result = subject_->GetOverlays(collection_group, since_batch_id, count);
225236
query_engine_->overlays_read_by_collection_group_ += result.size();
237+
for (const auto& r : result) {
238+
query_engine_->overlay_types_.emplace(r.first, r.second.mutation().type());
239+
}
240+
226241
return result;
227242
}
228243

Firestore/core/test/unit/local/counting_query_engine.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919

2020
#include <memory>
2121
#include <string>
22+
#include <unordered_map>
2223
#include <utility>
2324
#include <vector>
2425

2526
#include "Firestore/core/src/local/document_overlay_cache.h"
2627
#include "Firestore/core/src/local/mutation_queue.h"
2728
#include "Firestore/core/src/local/query_engine.h"
2829
#include "Firestore/core/src/local/remote_document_cache.h"
30+
#include "Firestore/core/src/model/document_key.h"
2931
#include "Firestore/core/src/model/model_fwd.h"
3032
#include "Firestore/core/src/util/hard_assert.h"
3133

@@ -90,6 +92,13 @@ class CountingQueryEngine : public QueryEngine {
9092
return overlays_read_by_key_;
9193
}
9294

95+
std::unordered_map<model::DocumentKey,
96+
model::Mutation::Type,
97+
model::DocumentKeyHash>
98+
overlay_types() const {
99+
return overlay_types_;
100+
}
101+
93102
private:
94103
friend class WrappedDocumentOverlayCache;
95104
friend class WrappedMutationQueue;
@@ -107,6 +116,10 @@ class CountingQueryEngine : public QueryEngine {
107116
size_t overlays_read_by_key_ = 0;
108117
size_t overlays_read_by_collection_ = 0;
109118
size_t overlays_read_by_collection_group_ = 0;
119+
std::unordered_map<model::DocumentKey,
120+
model::Mutation::Type,
121+
model::DocumentKeyHash>
122+
overlay_types_;
110123
};
111124

112125
/** A MutationQueue that counts document reads. */

Firestore/core/test/unit/local/leveldb_local_store_test.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ using testutil::Key;
4545
using testutil::MakeFieldIndex;
4646
using testutil::Map;
4747
using testutil::OrderBy;
48+
using testutil::OverlayTypeMap;
4849
using testutil::SetMutation;
4950
using testutil::UpdateRemoteEvent;
5051
using testutil::Vector;
@@ -238,6 +239,10 @@ TEST_F(LevelDbLocalStoreTest, UsesPartiallyIndexedOverlaysWhenAvailable) {
238239
testutil::Query("coll").AddingFilter(Filter("matches", "==", true));
239240
ExecuteQuery(query);
240241
FSTAssertOverlaysRead(/* byKey= */ 1, /* byCollection= */ 1);
242+
FSTAssertOverlayTypes(
243+
OverlayTypeMap({{Key("coll/a"), model::Mutation::Type::Set},
244+
{Key("coll/b"), model::Mutation::Type::Set}}));
245+
241246
FSTAssertQueryReturned("coll/a", "coll/b");
242247
}
243248

@@ -265,6 +270,9 @@ TEST_F(LevelDbLocalStoreTest, DoesNotUseLimitWhenIndexIsOutdated) {
265270
// query without limit.
266271
FSTAssertRemoteDocumentsRead(/* byKey= */ 5, /* byCollection= */ 0);
267272
FSTAssertOverlaysRead(/* byKey= */ 5, /* byCollection= */ 1);
273+
FSTAssertOverlayTypes(
274+
OverlayTypeMap({{Key("coll/b"), model::Mutation::Type::Delete}}));
275+
268276
FSTAssertQueryReturned("coll/a", "coll/c");
269277
}
270278

@@ -289,6 +297,8 @@ TEST_F(LevelDbLocalStoreTest, UsesIndexForLimitQueryWhenIndexIsUpdated) {
289297
ExecuteQuery(query);
290298
FSTAssertRemoteDocumentsRead(/* byKey= */ 2, /* byCollection= */ 0);
291299
FSTAssertOverlaysRead(/* byKey= */ 2, /* byCollection= */ 0);
300+
FSTAssertOverlayTypes(OverlayTypeMap({}));
301+
292302
FSTAssertQueryReturned("coll/a", "coll/c");
293303
}
294304

@@ -306,6 +316,9 @@ TEST_F(LevelDbLocalStoreTest, IndexesServerTimestamps) {
306316

307317
ExecuteQuery(query);
308318
FSTAssertOverlaysRead(/* byKey= */ 1, /* byCollection= */ 0);
319+
FSTAssertOverlayTypes(
320+
OverlayTypeMap({{Key("coll/a"), model::Mutation::Type::Set}}));
321+
309322
FSTAssertQueryReturned("coll/a");
310323
}
311324

Firestore/core/test/unit/local/local_store_test.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "Firestore/core/test/unit/local/local_store_test.h"
1818

19+
#include <unordered_map>
1920
#include <utility>
2021
#include <vector>
2122

@@ -31,8 +32,10 @@
3132
#include "Firestore/core/src/local/target_data.h"
3233
#include "Firestore/core/src/model/delete_mutation.h"
3334
#include "Firestore/core/src/model/document.h"
35+
#include "Firestore/core/src/model/document_key.h"
3436
#include "Firestore/core/src/model/field_index.h"
3537
#include "Firestore/core/src/model/mutable_document.h"
38+
#include "Firestore/core/src/model/mutation.h"
3639
#include "Firestore/core/src/model/mutation_batch_result.h"
3740
#include "Firestore/core/src/model/patch_mutation.h"
3841
#include "Firestore/core/src/model/set_mutation.h"
@@ -87,6 +90,7 @@ using testutil::DeletedDoc;
8790
using testutil::Doc;
8891
using testutil::Key;
8992
using testutil::Map;
93+
using testutil::OverlayTypeMap;
9094
using testutil::Query;
9195
using testutil::UnknownDoc;
9296
using testutil::UpdateRemoteEvent;
@@ -906,6 +910,8 @@ TEST_P(LocalStoreTest, ReadsAllDocumentsForInitialCollectionQueries) {
906910

907911
FSTAssertRemoteDocumentsRead(/* by_key= */ 0, /* by_query= */ 2);
908912
FSTAssertOverlaysRead(/* by_key= */ 0, /* by_query= */ 1);
913+
FSTAssertOverlayTypes(
914+
OverlayTypeMap({{Key("foo/bonk"), model::Mutation::Type::Set}}));
909915
}
910916

911917
TEST_P(LocalStoreTest, PersistsResumeTokens) {
@@ -1663,6 +1669,21 @@ TEST_P(LocalStoreTest, MultipleFieldPatchesOnLocalDocs) {
16631669
Doc("foo/bar", 0, Map("likes", 1, "stars", 2)).SetHasLocalMutations());
16641670
}
16651671

1672+
TEST_P(LocalStoreTest, PatchMutationLeadsToPatchOverlay) {
1673+
AllocateQuery(Query("foo"));
1674+
ApplyRemoteEvent(UpdateRemoteEvent(Doc("foo/baz", 10, Map("a", 1)), {2}, {}));
1675+
ApplyRemoteEvent(UpdateRemoteEvent(Doc("foo/bar", 20, Map()), {2}, {}));
1676+
WriteMutation(testutil::PatchMutation("foo/baz", Map("b", 2)));
1677+
1678+
ResetPersistenceStats();
1679+
1680+
ExecuteQuery(Query("foo"));
1681+
FSTAssertRemoteDocumentsRead(0, 2);
1682+
FSTAssertOverlaysRead(0, 1);
1683+
FSTAssertOverlayTypes(
1684+
OverlayTypeMap({{Key("foo/baz"), model::Mutation::Type::Patch}}));
1685+
}
1686+
16661687
} // namespace local
16671688
} // namespace firestore
16681689
} // namespace firebase

Firestore/core/test/unit/local/local_store_test.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,15 @@ class LocalStoreTest : public LocalStoreTestBase,
214214
#define FSTAssertOverlaysRead(by_key, by_query) \
215215
do { \
216216
ASSERT_EQ(query_engine_.overlays_read_by_key(), (by_key)) \
217-
<< "Mutations read (by key)"; \
217+
<< "Overlays read (by key)"; \
218218
ASSERT_EQ(query_engine_.overlays_read_by_collection(), (by_query)) \
219-
<< "Mutations read (by query)"; \
219+
<< "Overlays read (by query)"; \
220+
} while (0)
221+
222+
#define FSTAssertOverlayTypes(expected_types) \
223+
do { \
224+
ASSERT_EQ(query_engine_.overlay_types(), expected_types) \
225+
<< "Overlay types read"; \
220226
} while (0)
221227

222228
/**

Firestore/core/test/unit/testutil/testutil.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <algorithm>
2121
#include <memory>
2222
#include <string>
23+
#include <unordered_map>
2324
#include <utility>
2425
#include <vector>
2526

@@ -30,6 +31,7 @@
3031
#include "Firestore/core/src/model/document_set.h"
3132
#include "Firestore/core/src/model/field_index.h"
3233
#include "Firestore/core/src/model/model_fwd.h"
34+
#include "Firestore/core/src/model/mutation.h"
3335
#include "Firestore/core/src/model/precondition.h"
3436
#include "Firestore/core/src/model/value_util.h"
3537
#include "Firestore/core/src/nanopb/byte_string.h"
@@ -158,6 +160,10 @@ nanopb::Message<google_firestore_v1_Value> Value(
158160
nanopb::Message<google_firestore_v1_Value> Value(
159161
const model::ObjectValue& value);
160162

163+
using OverlayTypeMap = std::unordered_map<model::DocumentKey,
164+
model::Mutation::Type,
165+
model::DocumentKeyHash>;
166+
161167
namespace details {
162168

163169
/**

0 commit comments

Comments
 (0)