Skip to content

Commit f452609

Browse files
authored
Improve performance of LevelDbDocumentOverlayCache::GetOverlays(absl::string_view) (#9420)
1 parent 0b3b2c1 commit f452609

11 files changed

+382
-65
lines changed

Firestore/core/src/local/document_overlay_cache.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#define FIRESTORE_CORE_SRC_LOCAL_DOCUMENT_OVERLAY_CACHE_H_
1919

2020
#include <cstdlib>
21-
#include <string>
2221
#include <unordered_map>
2322

2423
#include "Firestore/core/src/model/document_key.h"
@@ -101,7 +100,7 @@ class DocumentOverlayCache {
101100
* overlay.
102101
*/
103102
virtual OverlayByDocumentKeyMap GetOverlays(
104-
const std::string& collection_group,
103+
absl::string_view collection_group,
105104
int since_batch_id,
106105
std::size_t count) const = 0;
107106

Firestore/core/src/local/leveldb_document_overlay_cache.cc

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
#include "Firestore/core/src/local/leveldb_document_overlay_cache.h"
1818

19-
#include <map>
2019
#include <string>
21-
#include <unordered_set>
2220
#include <utility>
2321

2422
#include "Firestore/core/src/credentials/user.h"
@@ -99,46 +97,28 @@ LevelDbDocumentOverlayCache::GetOverlays(const ResourcePath& collection,
9997
}
10098

10199
DocumentOverlayCache::OverlayByDocumentKeyMap
102-
LevelDbDocumentOverlayCache::GetOverlays(const std::string& collection_group,
100+
LevelDbDocumentOverlayCache::GetOverlays(absl::string_view collection_group,
103101
int since_batch_id,
104102
std::size_t count) const {
105-
// TODO(dconeybe) Implement an index so that this query can be performed
106-
// without requiring a full table scan.
107-
108-
// Load ALL overlays for the given `collection_group` whose largest_batch_id
109-
// are greater than the given `since_batch_id`. By using a `std::map` keyed
110-
// by largest_batch_id, the loop below can iterate over it ordered by
111-
// largest_batch_id.
112-
std::map<int, std::unordered_set<Overlay, OverlayHash>> overlays_by_batch_id;
113-
ForEachOverlay(
114-
[&](LevelDbDocumentOverlayKey&& key, absl::string_view encoded_mutation) {
115-
if (key.largest_batch_id() <= since_batch_id) {
116-
return;
117-
}
118-
if (key.document_key().HasCollectionId(collection_group)) {
119-
overlays_by_batch_id[key.largest_batch_id()].emplace(
120-
ParseOverlay(key, encoded_mutation));
121-
}
122-
});
123-
124-
// Trim down the overlays loaded above to respect the given `count`, and
125-
// return them.
126-
//
127-
// Note that, as documented, all overlays for the largest_batch_id that pushes
128-
// the size of the result set above the given `count` will be returned, even
129-
// though this likely means that the size of the result set will be strictly
130-
// greater than the given `count`.
103+
absl::optional<int> current_batch_id;
131104
OverlayByDocumentKeyMap result;
132-
for (auto& overlays_by_batch_id_entry : overlays_by_batch_id) {
133-
for (auto& overlay : overlays_by_batch_id_entry.second) {
134-
DocumentKey document_key = overlay.key();
135-
result[document_key] = std::move(overlay);
136-
}
137-
if (result.size() >= count) {
138-
break;
139-
}
140-
}
105+
ForEachKeyInCollectionGroup(
106+
collection_group, since_batch_id,
107+
[&](LevelDbDocumentOverlayKey&& key) -> ForEachKeyAction {
108+
if (!current_batch_id.has_value()) {
109+
current_batch_id = key.largest_batch_id();
110+
} else if (current_batch_id.value() != key.largest_batch_id()) {
111+
if (result.size() >= count) {
112+
return ForEachKeyAction::kStop;
113+
}
114+
current_batch_id = key.largest_batch_id();
115+
}
141116

117+
absl::optional<Overlay> overlay = GetOverlay(key);
118+
HARD_ASSERT(overlay.has_value());
119+
result[std::move(key).document_key()] = std::move(overlay).value();
120+
return ForEachKeyAction::kKeepGoing;
121+
});
142122
return result;
143123
}
144124

@@ -157,6 +137,11 @@ int LevelDbDocumentOverlayCache::GetCollectionIndexEntryCount() const {
157137
LevelDbDocumentOverlayCollectionIndexKey::KeyPrefix(user_id_));
158138
}
159139

140+
int LevelDbDocumentOverlayCache::GetCollectionGroupIndexEntryCount() const {
141+
return CountEntriesWithKeyPrefix(
142+
LevelDbDocumentOverlayCollectionGroupIndexKey::KeyPrefix(user_id_));
143+
}
144+
160145
int LevelDbDocumentOverlayCache::CountEntriesWithKeyPrefix(
161146
const std::string& key_prefix) const {
162147
int count = 0;
@@ -193,6 +178,12 @@ void LevelDbDocumentOverlayCache::SaveOverlay(int largest_batch_id,
193178
transaction->Put(key.Encode(), serializer_->EncodeMutation(mutation));
194179
transaction->Put(LevelDbDocumentOverlayLargestBatchIdIndexKey::Key(key), "");
195180
transaction->Put(LevelDbDocumentOverlayCollectionIndexKey::Key(key), "");
181+
182+
absl::optional<std::string> collection_group_index_key =
183+
LevelDbDocumentOverlayCollectionGroupIndexKey::Key(key);
184+
if (collection_group_index_key.has_value()) {
185+
transaction->Put(std::move(collection_group_index_key).value(), "");
186+
}
196187
}
197188

198189
void LevelDbDocumentOverlayCache::DeleteOverlay(
@@ -219,19 +210,11 @@ void LevelDbDocumentOverlayCache::DeleteOverlay(
219210
transaction->Delete(key.Encode());
220211
transaction->Delete(LevelDbDocumentOverlayLargestBatchIdIndexKey::Key(key));
221212
transaction->Delete(LevelDbDocumentOverlayCollectionIndexKey::Key(key));
222-
}
223213

224-
void LevelDbDocumentOverlayCache::ForEachOverlay(
225-
std::function<void((LevelDbDocumentOverlayKey && key,
226-
absl::string_view encoded_mutation))> callback) const {
227-
auto it = db_->current_transaction()->NewIterator();
228-
const std::string user_key = LevelDbDocumentOverlayKey::KeyPrefix(user_id_);
229-
230-
for (it->Seek(user_key); it->Valid() && absl::StartsWith(it->key(), user_key);
231-
it->Next()) {
232-
LevelDbDocumentOverlayKey key;
233-
HARD_ASSERT(key.Decode(it->key()));
234-
callback(std::move(key), it->value());
214+
absl::optional<std::string> collection_group_index_key =
215+
LevelDbDocumentOverlayCollectionGroupIndexKey::Key(key);
216+
if (collection_group_index_key.has_value()) {
217+
transaction->Delete(std::move(collection_group_index_key).value());
235218
}
236219
}
237220

@@ -273,6 +256,36 @@ void LevelDbDocumentOverlayCache::ForEachKeyInCollection(
273256
}
274257
}
275258

259+
void LevelDbDocumentOverlayCache::ForEachKeyInCollectionGroup(
260+
absl::string_view collection_group,
261+
int since_batch_id,
262+
std::function<ForEachKeyAction(LevelDbDocumentOverlayKey&&)> callback)
263+
const {
264+
const std::string index_start_key =
265+
LevelDbDocumentOverlayCollectionGroupIndexKey::KeyPrefix(
266+
user_id_, collection_group, since_batch_id + 1);
267+
const std::string index_key_prefix =
268+
LevelDbDocumentOverlayCollectionGroupIndexKey::KeyPrefix(
269+
user_id_, collection_group);
270+
271+
auto it = db_->current_transaction()->NewIterator();
272+
for (it->Seek(index_start_key);
273+
it->Valid() && absl::StartsWith(it->key(), index_key_prefix);
274+
it->Next()) {
275+
LevelDbDocumentOverlayCollectionGroupIndexKey key;
276+
HARD_ASSERT(key.Decode(it->key()));
277+
if (key.collection_group() != collection_group) {
278+
break;
279+
}
280+
const ForEachKeyAction action =
281+
callback(std::move(key).ToLevelDbDocumentOverlayKey());
282+
if (action == ForEachKeyAction::kStop) {
283+
break;
284+
}
285+
HARD_ASSERT(action == ForEachKeyAction::kKeepGoing);
286+
}
287+
}
288+
276289
absl::optional<Overlay> LevelDbDocumentOverlayCache::GetOverlay(
277290
const LevelDbDocumentOverlayKey& key) const {
278291
auto it = db_->current_transaction()->NewIterator();

Firestore/core/src/local/leveldb_document_overlay_cache.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class LevelDbDocumentOverlayCache final : public DocumentOverlayCache {
6363
OverlayByDocumentKeyMap GetOverlays(const model::ResourcePath& collection,
6464
int since_batch_id) const override;
6565

66-
OverlayByDocumentKeyMap GetOverlays(const std::string& collection_group,
66+
OverlayByDocumentKeyMap GetOverlays(absl::string_view collection_group,
6767
int since_batch_id,
6868
std::size_t count) const override;
6969

@@ -74,10 +74,16 @@ class LevelDbDocumentOverlayCache final : public DocumentOverlayCache {
7474
// These methods exist for unit testing only.
7575
int GetLargestBatchIdIndexEntryCount() const;
7676
int GetCollectionIndexEntryCount() const;
77+
int GetCollectionGroupIndexEntryCount() const;
7778

7879
int GetOverlayCount() const override;
7980
int CountEntriesWithKeyPrefix(const std::string& key_prefix) const;
8081

82+
enum class ForEachKeyAction {
83+
kKeepGoing,
84+
kStop,
85+
};
86+
8187
model::mutation::Overlay ParseOverlay(
8288
const LevelDbDocumentOverlayKey& key,
8389
absl::string_view encoded_mutation) const;
@@ -90,10 +96,6 @@ class LevelDbDocumentOverlayCache final : public DocumentOverlayCache {
9096

9197
void DeleteOverlay(const LevelDbDocumentOverlayKey&);
9298

93-
void ForEachOverlay(
94-
std::function<void(LevelDbDocumentOverlayKey&&,
95-
absl::string_view encoded_mutation)>) const;
96-
9799
void ForEachKeyWithLargestBatchId(
98100
int largest_batch_id,
99101
std::function<void(LevelDbDocumentOverlayKey&&)>) const;
@@ -103,6 +105,11 @@ class LevelDbDocumentOverlayCache final : public DocumentOverlayCache {
103105
int since_batch_id,
104106
std::function<void(LevelDbDocumentOverlayKey&&)>) const;
105107

108+
void ForEachKeyInCollectionGroup(
109+
absl::string_view collection_group,
110+
int since_batch_id,
111+
std::function<ForEachKeyAction(LevelDbDocumentOverlayKey&&)>) const;
112+
106113
absl::optional<model::mutation::Overlay> GetOverlay(
107114
const LevelDbDocumentOverlayKey& decoded_key) const;
108115

Firestore/core/src/local/leveldb_key.cc

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121

2222
#include "Firestore/core/src/local/leveldb_util.h"
2323
#include "Firestore/core/src/model/mutation_batch.h"
24+
#include "Firestore/core/src/util/hard_assert.h"
2425
#include "Firestore/core/src/util/ordered_code.h"
2526
#include "absl/base/attributes.h"
2627
#include "absl/strings/escaping.h"
2728
#include "absl/strings/str_cat.h"
29+
#include "absl/types/optional.h"
2830

2931
using firebase::firestore::model::DocumentKey;
3032
using firebase::firestore::model::ResourcePath;
@@ -58,6 +60,8 @@ const char* kDocumentOverlaysLargestBatchIdIndexTable =
5860
"document_overlays_largest_batch_id_index";
5961
const char* kDocumentOverlaysCollectionIndexTable =
6062
"document_overlays_collection_index";
63+
const char* kDocumentOverlaysCollectionGroupIndexTable =
64+
"document_overlays_collection_group_index";
6165

6266
/**
6367
* Labels for the components of keys. These serve to make keys self-describing.
@@ -1401,6 +1405,72 @@ bool LevelDbDocumentOverlayCollectionIndexKey::Decode(absl::string_view key) {
14011405
return reader.ok();
14021406
}
14031407

1408+
std::string LevelDbDocumentOverlayCollectionGroupIndexKey::KeyPrefix(
1409+
absl::string_view user_id) {
1410+
Writer writer;
1411+
writer.WriteTableName(kDocumentOverlaysCollectionGroupIndexTable);
1412+
writer.WriteUserId(user_id);
1413+
return writer.result();
1414+
}
1415+
1416+
std::string LevelDbDocumentOverlayCollectionGroupIndexKey::KeyPrefix(
1417+
absl::string_view user_id, absl::string_view collection_group) {
1418+
Writer writer;
1419+
writer.WriteTableName(kDocumentOverlaysCollectionGroupIndexTable);
1420+
writer.WriteUserId(user_id);
1421+
writer.WriteCollectionGroup(collection_group);
1422+
return writer.result();
1423+
}
1424+
1425+
std::string LevelDbDocumentOverlayCollectionGroupIndexKey::KeyPrefix(
1426+
absl::string_view user_id,
1427+
absl::string_view collection_group,
1428+
model::BatchId largest_batch_id) {
1429+
Writer writer;
1430+
writer.WriteTableName(kDocumentOverlaysCollectionGroupIndexTable);
1431+
writer.WriteUserId(user_id);
1432+
writer.WriteCollectionGroup(collection_group);
1433+
writer.WriteBatchId(largest_batch_id);
1434+
return writer.result();
1435+
}
1436+
1437+
std::string LevelDbDocumentOverlayCollectionGroupIndexKey::Key(
1438+
absl::string_view user_id,
1439+
absl::string_view collection_group,
1440+
model::BatchId largest_batch_id,
1441+
const model::DocumentKey& document_key) {
1442+
Writer writer;
1443+
writer.WriteTableName(kDocumentOverlaysCollectionGroupIndexTable);
1444+
writer.WriteUserId(user_id);
1445+
writer.WriteCollectionGroup(collection_group);
1446+
writer.WriteBatchId(largest_batch_id);
1447+
writer.WriteResourcePath(document_key.path());
1448+
writer.WriteTerminator();
1449+
return writer.result();
1450+
}
1451+
1452+
std::string LevelDbDocumentOverlayCollectionGroupIndexKey::Key(
1453+
const LevelDbDocumentOverlayKey& key) {
1454+
const absl::optional<std::string> collection_group =
1455+
key.document_key().GetCollectionId();
1456+
HARD_ASSERT(collection_group.has_value());
1457+
return Key(key.user_id(), collection_group.value(), key.largest_batch_id(),
1458+
key.document_key());
1459+
}
1460+
1461+
bool LevelDbDocumentOverlayCollectionGroupIndexKey::Decode(
1462+
absl::string_view key) {
1463+
Reader reader{key};
1464+
reader.ReadTableNameMatching(kDocumentOverlaysCollectionGroupIndexTable);
1465+
auto user_id = reader.ReadUserId();
1466+
collection_group_ = reader.ReadCollectionGroup();
1467+
auto largest_batch_id = reader.ReadBatchId();
1468+
auto document_key = reader.ReadDocumentKey();
1469+
reader.ReadTerminator();
1470+
Reset(std::move(user_id), largest_batch_id, std::move(document_key));
1471+
return reader.ok();
1472+
}
1473+
14041474
} // namespace local
14051475
} // namespace firestore
14061476
} // namespace firebase

0 commit comments

Comments
 (0)