Skip to content

Commit bca6418

Browse files
authored
Reduce memory usage by applying query check sooner (#10745)
1 parent 74d80aa commit bca6418

11 files changed

+125
-44
lines changed

Firestore/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
2+
# Unreleased
3+
- [fixed] Fix a potential high memory usage issue.
4+
15
# 10.5.0
26
- [fixed] Add @discardableResult to addDocument API for easy handling unused return value. (#10640)
37

Firestore/core/src/local/leveldb_remote_document_cache.cc

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
#include "Firestore/core/src/local/leveldb_persistence.h"
2727
#include "Firestore/core/src/local/local_serializer.h"
2828
#include "Firestore/core/src/model/document_key_set.h"
29+
#include "Firestore/core/src/model/model_fwd.h"
2930
#include "Firestore/core/src/model/mutable_document.h"
31+
#include "Firestore/core/src/model/overlay.h"
3032
#include "Firestore/core/src/nanopb/message.h"
3133
#include "Firestore/core/src/nanopb/reader.h"
3234
#include "Firestore/core/src/util/background_queue.h"
@@ -171,13 +173,18 @@ MutableDocumentMap LevelDbRemoteDocumentCache::GetAll(
171173
}
172174

173175
MutableDocumentMap LevelDbRemoteDocumentCache::GetAllExisting(
174-
DocumentVersionMap&& remote_map) const {
176+
DocumentVersionMap&& remote_map,
177+
const core::Query& query,
178+
const model::OverlayByDocumentKeyMap& mutated_docs) const {
175179
BackgroundQueue tasks(executor_.get());
176180
AsyncResults<std::pair<DocumentKey, MutableDocument>> results;
177181
for (const auto& key_version : remote_map) {
178-
tasks.Execute([this, &results, key_version] {
182+
tasks.Execute([this, &results, &key_version, query, &mutated_docs] {
179183
auto document = Get(key_version.first).WithReadTime(key_version.second);
180-
if (document.is_found_document()) {
184+
if (document.is_found_document() &&
185+
// Either the document matches the given query, or it is mutated.
186+
(query.Matches(document) ||
187+
mutated_docs.find(key_version.first) != mutated_docs.end())) {
181188
results.Insert(std::make_pair(key_version.first, std::move(document)));
182189
}
183190
});
@@ -206,24 +213,27 @@ MutableDocumentMap LevelDbRemoteDocumentCache::GetAll(
206213
MutableDocumentMap result;
207214
for (auto path = collections.cbegin();
208215
path != collections.cend() && result.size() < limit; path++) {
209-
const auto remote_docs = GetAll(*path, offset, limit - result.size());
216+
const auto remote_docs =
217+
GetDocumentsMatchingQuery(Query(*path), offset, limit - result.size());
210218
for (const auto& doc : remote_docs) {
211219
result = result.insert(doc.first, doc.second);
212220
}
213221
}
214222
return result;
215223
}
216224

217-
MutableDocumentMap LevelDbRemoteDocumentCache::GetAll(
218-
const model::ResourcePath& path,
225+
MutableDocumentMap LevelDbRemoteDocumentCache::GetDocumentsMatchingQuery(
226+
const core::Query& query,
219227
const model::IndexOffset& offset,
220-
const absl::optional<size_t> limit) const {
228+
absl::optional<size_t> limit,
229+
const model::OverlayByDocumentKeyMap& mutated_docs) const {
221230
// Use the query path as a prefix for testing if a document matches the query.
222231

223232
// Execute an index-free query and filter by read time. This is safe since
224233
// all document changes to queries that have a
225234
// last_limbo_free_snapshot_version (`since_read_time`) have a read time
226235
// set.
236+
auto path = query.path();
227237
std::string start_key =
228238
LevelDbRemoteDocumentReadTimeKey::KeyPrefix(path, offset.read_time());
229239
auto it = db_->current_transaction()->NewIterator();
@@ -252,7 +262,8 @@ MutableDocumentMap LevelDbRemoteDocumentCache::GetAll(
252262
}
253263
}
254264

255-
return LevelDbRemoteDocumentCache::GetAllExisting(std::move(remote_map));
265+
return LevelDbRemoteDocumentCache::GetAllExisting(std::move(remote_map),
266+
query, mutated_docs);
256267
}
257268

258269
MutableDocument LevelDbRemoteDocumentCache::DecodeMaybeDocument(

Firestore/core/src/local/leveldb_remote_document_cache.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
#include <thread> // NOLINT(build/c++11)
2323
#include <vector>
2424

25+
#include "Firestore/core/src/core/query.h"
2526
#include "Firestore/core/src/local/leveldb_index_manager.h"
2627
#include "Firestore/core/src/local/remote_document_cache.h"
2728
#include "Firestore/core/src/model/model_fwd.h"
29+
#include "Firestore/core/src/model/overlay.h"
2830
#include "Firestore/core/src/model/types.h"
2931
#include "absl/strings/string_view.h"
32+
#include "absl/types/optional.h"
3033

3134
namespace firebase {
3235
namespace firestore {
@@ -62,10 +65,11 @@ class LevelDbRemoteDocumentCache : public RemoteDocumentCache {
6265
model::MutableDocumentMap GetAll(const std::string& collection_group,
6366
const model::IndexOffset& offset,
6467
size_t limit) const override;
65-
model::MutableDocumentMap GetAll(
66-
const model::ResourcePath& path,
68+
model::MutableDocumentMap GetDocumentsMatchingQuery(
69+
const core::Query& query,
6770
const model::IndexOffset& offset,
68-
absl::optional<size_t> limit = absl::nullopt) const override;
71+
absl::optional<size_t> limit = absl::nullopt,
72+
const model::OverlayByDocumentKeyMap& mutated_docs = {}) const override;
6973

7074
void SetIndexManager(IndexManager* manager) override;
7175

@@ -75,7 +79,9 @@ class LevelDbRemoteDocumentCache : public RemoteDocumentCache {
7579
* Type::Document together with its SnapshotVersion.
7680
*/
7781
model::MutableDocumentMap GetAllExisting(
78-
model::DocumentVersionMap&& remote_map) const;
82+
model::DocumentVersionMap&& remote_map,
83+
const core::Query& query,
84+
const model::OverlayByDocumentKeyMap& mutated_docs = {}) const;
7985

8086
model::MutableDocument DecodeMaybeDocument(
8187
absl::string_view encoded, const model::DocumentKey& key) const;

Firestore/core/src/local/local_documents_view.cc

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ using model::OverlayByDocumentKeyMap;
6464
using model::ResourcePath;
6565
using model::SnapshotVersion;
6666

67-
using OverlayByDocumentKeyMap = std::
68-
unordered_map<model::DocumentKey, model::Overlay, model::DocumentKeyHash>;
69-
7067
Document LocalDocumentsView::GetDocument(
7168
const DocumentKey& key, const std::vector<MutationBatch>& batches) {
7269
MutableDocument document = remote_document_cache_->Get(key);
@@ -157,11 +154,12 @@ LocalWriteResult LocalDocumentsView::GetNextDocuments(
157154

158155
DocumentMap LocalDocumentsView::GetDocumentsMatchingCollectionQuery(
159156
const Query& query, const IndexOffset& offset) {
160-
MutableDocumentMap remote_documents =
161-
remote_document_cache_->GetAll(query.path(), offset);
162-
// Get locally persisted mutation batches.
157+
// Get locally mutated documents
163158
OverlayByDocumentKeyMap overlays = document_overlay_cache_->GetOverlays(
164159
query.path(), offset.largest_batch_id());
160+
MutableDocumentMap remote_documents =
161+
remote_document_cache_->GetDocumentsMatchingQuery(
162+
query, offset, absl::nullopt, overlays);
165163

166164
// As documents might match the query because of their overlay we need to
167165
// include documents for all overlays in the initial document set.

Firestore/core/src/local/memory_remote_document_cache.cc

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "Firestore/core/src/local/memory_persistence.h"
2222
#include "Firestore/core/src/local/sizer.h"
2323
#include "Firestore/core/src/model/document.h"
24+
#include "Firestore/core/src/model/overlay.h"
2425
#include "Firestore/core/src/util/hard_assert.h"
2526

2627
namespace firebase {
@@ -83,14 +84,16 @@ MutableDocumentMap MemoryRemoteDocumentCache::GetAll(const std::string&,
8384
"getAll(String, IndexOffset, int) is not supported.");
8485
}
8586

86-
MutableDocumentMap MemoryRemoteDocumentCache::GetAll(
87-
const model::ResourcePath& path,
87+
MutableDocumentMap MemoryRemoteDocumentCache::GetDocumentsMatchingQuery(
88+
const core::Query& query,
8889
const model::IndexOffset& offset,
89-
const absl::optional<size_t>) const {
90+
absl::optional<size_t>,
91+
const model::OverlayByDocumentKeyMap& mutated_docs) const {
9092
MutableDocumentMap results;
9193

9294
// Documents are ordered by key, so we can use a prefix scan to narrow down
9395
// the documents we need to match the query against.
96+
auto path = query.path();
9497
DocumentKey prefix{path.Append("")};
9598
size_t immediate_children_path_length = path.size() + 1;
9699
for (auto it = docs_.lower_bound(prefix); it != docs_.end(); ++it) {
@@ -110,6 +113,11 @@ MutableDocumentMap MemoryRemoteDocumentCache::GetAll(
110113
continue;
111114
}
112115

116+
if (mutated_docs.find(document.key()) == mutated_docs.end() &&
117+
!query.Matches(document)) {
118+
continue;
119+
}
120+
113121
// Note: We create an explicit copy to prevent modifications on the backing
114122
// data.
115123
results = results.insert(key, document.Clone());

Firestore/core/src/local/memory_remote_document_cache.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "Firestore/core/src/model/document_key.h"
2828
#include "Firestore/core/src/model/model_fwd.h"
2929
#include "Firestore/core/src/model/mutable_document.h"
30+
#include "Firestore/core/src/model/overlay.h"
3031
#include "Firestore/core/src/model/types.h"
3132

3233
namespace firebase {
@@ -51,9 +52,12 @@ class MemoryRemoteDocumentCache : public RemoteDocumentCache {
5152
model::MutableDocumentMap GetAll(const std::string&,
5253
const model::IndexOffset&,
5354
size_t) const override;
54-
model::MutableDocumentMap GetAll(const model::ResourcePath& path,
55-
const model::IndexOffset& offset,
56-
absl::optional<size_t>) const override;
55+
model::MutableDocumentMap GetDocumentsMatchingQuery(
56+
const core::Query& query,
57+
const model::IndexOffset& offset,
58+
absl::optional<size_t> limit = absl::nullopt,
59+
const model::OverlayByDocumentKeyMap& mutated_docs = {}) const override;
60+
5761
void SetIndexManager(IndexManager* manager) override;
5862

5963
std::vector<model::DocumentKey> RemoveOrphanedDocuments(

Firestore/core/src/local/remote_document_cache.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
#include <string>
2121

22+
#include "Firestore/core/src/model/document_key.h"
2223
#include "Firestore/core/src/model/model_fwd.h"
24+
#include "Firestore/core/src/model/overlay.h"
2325

2426
namespace firebase {
2527
namespace firestore {
@@ -100,17 +102,20 @@ class RemoteDocumentCache {
100102
*
101103
* Cached DeletedDocument entries have no bearing on query results.
102104
*
103-
* @param path The collection path to match documents against.
105+
* @param query The query to match documents against.
104106
* @param offset The read time and document key to start scanning at
105107
* (exclusive).
106108
* @param limit The maximum number of results to return.
107109
* If the limit is not defined, returns all matching documents.
110+
* @param mutated_docs The documents with local mutations, they are read
111+
* regardless if the remote version matches the given query.
108112
* @return The set of matching documents.
109113
*/
110-
virtual model::MutableDocumentMap GetAll(
111-
const model::ResourcePath& path,
114+
virtual model::MutableDocumentMap GetDocumentsMatchingQuery(
115+
const core::Query& query,
112116
const model::IndexOffset& offset,
113-
absl::optional<size_t> limit = absl::nullopt) const = 0;
117+
absl::optional<size_t> limit = absl::nullopt,
118+
const model::OverlayByDocumentKeyMap& mutated_docs = {}) const = 0;
114119

115120
/**
116121
* Sets the index manager used by remote document cache.

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,13 @@ model::MutableDocumentMap WrappedRemoteDocumentCache::GetAll(
185185
return result;
186186
}
187187

188-
model::MutableDocumentMap WrappedRemoteDocumentCache::GetAll(
189-
const model::ResourcePath& path,
188+
model::MutableDocumentMap WrappedRemoteDocumentCache::GetDocumentsMatchingQuery(
189+
const core::Query& query,
190190
const model::IndexOffset& offset,
191-
absl::optional<size_t>) const {
192-
auto result = subject_->GetAll(path, offset);
191+
absl::optional<size_t> limit,
192+
const model::OverlayByDocumentKeyMap& mutated_docs) const {
193+
auto result =
194+
subject_->GetDocumentsMatchingQuery(query, offset, limit, mutated_docs);
193195
query_engine_->documents_read_by_query_ += result.size();
194196
return result;
195197
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,11 @@ class WrappedRemoteDocumentCache : public RemoteDocumentCache {
196196
const model::IndexOffset& offset,
197197
size_t limit) const override;
198198

199-
model::MutableDocumentMap GetAll(const model::ResourcePath& path,
200-
const model::IndexOffset& offset,
201-
absl::optional<size_t>) const override;
199+
model::MutableDocumentMap GetDocumentsMatchingQuery(
200+
const core::Query& query,
201+
const model::IndexOffset& offset,
202+
absl::optional<size_t>,
203+
const model::OverlayByDocumentKeyMap& mutated_docs) const override;
202204

203205
void SetIndexManager(IndexManager* manager) override {
204206
index_manager_ = NOT_NULL(manager);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,10 +1036,10 @@ TEST_P(LocalStoreTest, UsesTargetMappingToExecuteQueries) {
10361036
AcknowledgeMutationWithVersion(10);
10371037
AcknowledgeMutationWithVersion(10);
10381038

1039-
// Execute the query, but note that we read all existing documents from the
1039+
// Execute the query, but note that we read matching documents from the
10401040
// RemoteDocumentCache since we do not yet have target mapping.
10411041
ExecuteQuery(query);
1042-
FSTAssertRemoteDocumentsRead(/* by_key */ 0, /* by_query= */ 3);
1042+
FSTAssertRemoteDocumentsRead(/* by_key */ 0, /* by_query= */ 2);
10431043

10441044
// Issue a RemoteEvent to persist the target mapping.
10451045
ApplyRemoteEvent(AddedRemoteEvent({Doc("foo/a", 10, Map("matches", true)),

0 commit comments

Comments
 (0)