Skip to content

Commit b5d22d4

Browse files
authored
firestore::local::LevelDbDocumentOverlayCache added. (#9345)
1 parent 497e6ae commit b5d22d4

13 files changed

+487
-24
lines changed

Firestore/core/src/local/leveldb_document_overlay_cache.cc

Lines changed: 163 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,52 +16,200 @@
1616

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

19+
#include <map>
20+
#include <string>
21+
#include <unordered_set>
22+
#include <utility>
23+
24+
#include "Firestore/core/src/credentials/user.h"
25+
#include "Firestore/core/src/local/leveldb_key.h"
26+
#include "Firestore/core/src/local/leveldb_persistence.h"
27+
#include "Firestore/core/src/local/local_serializer.h"
28+
#include "Firestore/core/src/nanopb/message.h"
29+
#include "Firestore/core/src/nanopb/reader.h"
30+
#include "Firestore/core/src/util/hard_assert.h"
31+
#include "absl/strings/match.h"
32+
#include "absl/strings/string_view.h"
33+
#include "absl/types/optional.h"
34+
1935
namespace firebase {
2036
namespace firestore {
2137
namespace local {
2238

39+
using credentials::User;
2340
using model::DocumentKey;
2441
using model::Mutation;
2542
using model::ResourcePath;
2643
using model::mutation::Overlay;
44+
using model::mutation::OverlayHash;
45+
using nanopb::Message;
46+
using nanopb::StringReader;
2747

28-
// TODO(dconeybe) Implement these methods.
29-
30-
LevelDbDocumentOverlayCache::LevelDbDocumentOverlayCache() {
48+
LevelDbDocumentOverlayCache::LevelDbDocumentOverlayCache(
49+
const User& user, LevelDbPersistence* db, LocalSerializer* serializer)
50+
: db_(NOT_NULL(db)),
51+
serializer_(NOT_NULL(serializer)),
52+
user_id_(user.is_authenticated() ? user.uid() : "") {
3153
}
3254

3355
absl::optional<Overlay> LevelDbDocumentOverlayCache::GetOverlay(
3456
const DocumentKey& key) const {
35-
(void)key;
36-
return absl::nullopt;
57+
const std::string leveldb_key_prefix =
58+
LevelDbDocumentOverlayKey::KeyPrefix(user_id_, key);
59+
60+
auto it = db_->current_transaction()->NewIterator();
61+
it->Seek(leveldb_key_prefix);
62+
63+
if (!(it->Valid() && absl::StartsWith(it->key(), leveldb_key_prefix))) {
64+
return absl::nullopt;
65+
}
66+
67+
LevelDbDocumentOverlayKey decoded_key;
68+
HARD_ASSERT(decoded_key.Decode(it->key()));
69+
return ParseOverlay(decoded_key, it->value());
3770
}
3871

3972
void LevelDbDocumentOverlayCache::SaveOverlays(
4073
int largest_batch_id, const MutationByDocumentKeyMap& overlays) {
41-
(void)largest_batch_id;
42-
(void)overlays;
74+
for (const auto& overlays_entry : overlays) {
75+
SaveOverlay(largest_batch_id, overlays_entry.first, overlays_entry.second);
76+
}
4377
}
4478

4579
void LevelDbDocumentOverlayCache::RemoveOverlaysForBatchId(int batch_id) {
46-
(void)batch_id;
80+
// TODO(dconeybe) Implement an index so that this query can be performed
81+
// without requiring a full table scan.
82+
83+
ForEachOverlay([&](absl::string_view encoded_key,
84+
const LevelDbDocumentOverlayKey& decoded_key,
85+
absl::string_view) {
86+
if (decoded_key.largest_batch_id() == batch_id) {
87+
db_->current_transaction()->Delete(encoded_key);
88+
}
89+
});
4790
}
4891

4992
DocumentOverlayCache::OverlayByDocumentKeyMap
5093
LevelDbDocumentOverlayCache::GetOverlays(const ResourcePath& collection,
5194
int since_batch_id) const {
52-
(void)collection;
53-
(void)since_batch_id;
54-
return {};
95+
// TODO(dconeybe) Implement an index so that this query can be performed
96+
// without requiring a full table scan.
97+
98+
OverlayByDocumentKeyMap result;
99+
100+
const size_t immediate_children_path_length{collection.size() + 1};
101+
102+
ForEachOverlay([&](absl::string_view,
103+
const LevelDbDocumentOverlayKey& decoded_key,
104+
absl::string_view encoded_mutation) {
105+
const DocumentKey key = decoded_key.document_key();
106+
if (!collection.IsPrefixOf(key.path())) {
107+
return;
108+
}
109+
// Documents from sub-collections
110+
if (key.path().size() != immediate_children_path_length) {
111+
return;
112+
}
113+
114+
if (decoded_key.largest_batch_id() > since_batch_id) {
115+
result[key] = ParseOverlay(decoded_key, encoded_mutation);
116+
}
117+
});
118+
119+
return result;
55120
}
56121

57122
DocumentOverlayCache::OverlayByDocumentKeyMap
58123
LevelDbDocumentOverlayCache::GetOverlays(const std::string& collection_group,
59124
int since_batch_id,
60125
std::size_t count) const {
61-
(void)collection_group;
62-
(void)since_batch_id;
63-
(void)count;
64-
return {};
126+
// TODO(dconeybe) Implement an index so that this query can be performed
127+
// without requiring a full table scan.
128+
129+
// Load ALL overlays for the given `collection_group` whose largest_batch_id
130+
// are greater than the given `since_batch_id`. By using a `std::map` keyed
131+
// by largest_batch_id, the loop below can iterate over it ordered by
132+
// largest_batch_id.
133+
std::map<int, std::unordered_set<Overlay, OverlayHash>> overlays_by_batch_id;
134+
ForEachOverlay([&](absl::string_view,
135+
const LevelDbDocumentOverlayKey& decoded_key,
136+
absl::string_view encoded_mutation) {
137+
if (decoded_key.largest_batch_id() <= since_batch_id) {
138+
return;
139+
}
140+
if (decoded_key.document_key().HasCollectionId(collection_group)) {
141+
overlays_by_batch_id[decoded_key.largest_batch_id()].emplace(
142+
ParseOverlay(decoded_key, encoded_mutation));
143+
}
144+
});
145+
146+
// Trim down the overlays loaded above to respect the given `count`, and
147+
// return them.
148+
//
149+
// Note that, as documented, all overlays for the largest_batch_id that pushes
150+
// the size of the result set above the given `count` will be returned, even
151+
// though this likely means that the size of the result set will be strictly
152+
// greater than the given `count`.
153+
OverlayByDocumentKeyMap result;
154+
for (auto& overlays_by_batch_id_entry : overlays_by_batch_id) {
155+
for (auto& overlay : overlays_by_batch_id_entry.second) {
156+
DocumentKey key = overlay.key();
157+
result[key] = std::move(overlay);
158+
}
159+
if (result.size() >= count) {
160+
break;
161+
}
162+
}
163+
164+
return result;
165+
}
166+
167+
Overlay LevelDbDocumentOverlayCache::ParseOverlay(
168+
const LevelDbDocumentOverlayKey& key,
169+
absl::string_view encoded_mutation) const {
170+
StringReader reader{encoded_mutation};
171+
auto maybe_message = Message<google_firestore_v1_Write>::TryParse(&reader);
172+
Mutation mutation = serializer_->DecodeMutation(&reader, *maybe_message);
173+
if (!reader.ok()) {
174+
HARD_FAIL("Mutation proto failed to parse: %s", reader.status().ToString());
175+
}
176+
return Overlay(key.largest_batch_id(), std::move(mutation));
177+
}
178+
179+
void LevelDbDocumentOverlayCache::SaveOverlay(int largest_batch_id,
180+
const DocumentKey& key,
181+
const Mutation& mutation) {
182+
DeleteOverlay(key);
183+
const std::string leveldb_key =
184+
LevelDbDocumentOverlayKey::Key(user_id_, key, largest_batch_id);
185+
auto serialized_mutation = serializer_->EncodeMutation(mutation);
186+
db_->current_transaction()->Put(leveldb_key, serialized_mutation);
187+
}
188+
189+
void LevelDbDocumentOverlayCache::DeleteOverlay(const model::DocumentKey& key) {
190+
const std::string leveldb_key_prefix =
191+
LevelDbDocumentOverlayKey::KeyPrefix(user_id_, key);
192+
auto it = db_->current_transaction()->NewIterator();
193+
for (it->Seek(leveldb_key_prefix);
194+
it->Valid() && absl::StartsWith(it->key(), leveldb_key_prefix);
195+
it->Next()) {
196+
db_->current_transaction()->Delete(it->key());
197+
}
198+
}
199+
200+
void LevelDbDocumentOverlayCache::ForEachOverlay(
201+
std::function<void(absl::string_view encoded_key,
202+
const LevelDbDocumentOverlayKey& decoded_key,
203+
absl::string_view encoded_mutation)> callback) const {
204+
auto it = db_->current_transaction()->NewIterator();
205+
const std::string user_key = LevelDbDocumentOverlayKey::KeyPrefix(user_id_);
206+
207+
for (it->Seek(user_key); it->Valid() && absl::StartsWith(it->key(), user_key);
208+
it->Next()) {
209+
LevelDbDocumentOverlayKey decoded_key;
210+
HARD_ASSERT(decoded_key.Decode(it->key()));
211+
callback(it->key(), decoded_key, it->value());
212+
}
65213
}
66214

67215
} // namespace local

Firestore/core/src/local/leveldb_document_overlay_cache.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,30 @@
1818
#define FIRESTORE_CORE_SRC_LOCAL_LEVELDB_DOCUMENT_OVERLAY_CACHE_H_
1919

2020
#include <cstdlib>
21+
#include <functional>
2122
#include <string>
2223

2324
#include "Firestore/core/src/local/document_overlay_cache.h"
25+
#include "absl/strings/string_view.h"
2426

2527
namespace firebase {
2628
namespace firestore {
29+
30+
namespace credentials {
31+
class User;
32+
} // namespace credentials
33+
2734
namespace local {
2835

36+
class LevelDbDocumentOverlayKey;
37+
class LevelDbPersistence;
38+
class LocalSerializer;
39+
2940
class LevelDbDocumentOverlayCache final : public DocumentOverlayCache {
3041
public:
31-
LevelDbDocumentOverlayCache();
42+
LevelDbDocumentOverlayCache(const credentials::User& user,
43+
LevelDbPersistence* db,
44+
LocalSerializer* serializer);
3245

3346
LevelDbDocumentOverlayCache(const LevelDbDocumentOverlayCache&) = delete;
3447
LevelDbDocumentOverlayCache& operator=(const LevelDbDocumentOverlayCache&) =
@@ -52,6 +65,34 @@ class LevelDbDocumentOverlayCache final : public DocumentOverlayCache {
5265
OverlayByDocumentKeyMap GetOverlays(const std::string& collection_group,
5366
int since_batch_id,
5467
std::size_t count) const override;
68+
69+
private:
70+
model::mutation::Overlay ParseOverlay(
71+
const LevelDbDocumentOverlayKey& key,
72+
absl::string_view encoded_mutation) const;
73+
74+
void SaveOverlay(int largest_batch_id,
75+
const model::DocumentKey& key,
76+
const model::Mutation& mutation);
77+
78+
void DeleteOverlay(const model::DocumentKey& key);
79+
80+
void ForEachOverlay(
81+
std::function<void(absl::string_view encoded_key,
82+
const LevelDbDocumentOverlayKey& decoded_key,
83+
absl::string_view encoded_mutation)>) const;
84+
85+
// The LevelDbDocumentOverlayCache instance is owned by LevelDbPersistence.
86+
LevelDbPersistence* db_;
87+
88+
// Owned by LevelDbPersistence.
89+
LocalSerializer* serializer_ = nullptr;
90+
91+
/**
92+
* The normalized user_id (i.e. after converting null to empty) as used in our
93+
* LevelDB keys.
94+
*/
95+
std::string user_id_;
5596
};
5697

5798
} // namespace local

Firestore/core/src/local/leveldb_key.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const char* kNamedQueriesTable = "named_queries";
5353
const char* kIndexConfigurationTable = "index_configuration";
5454
const char* kIndexStateTable = "index_state";
5555
const char* kIndexEntriesTable = "index_entries";
56+
const char* kDocumentOverlaysTable = "document_overlays";
5657

5758
/**
5859
* Labels for the components of keys. These serve to make keys self-describing.
@@ -1259,6 +1260,50 @@ bool LevelDbIndexEntryKey::Decode(absl::string_view key) {
12591260
return reader.ok();
12601261
}
12611262

1263+
std::string LevelDbDocumentOverlayKey::KeyPrefix() {
1264+
Writer writer;
1265+
writer.WriteTableName(kDocumentOverlaysTable);
1266+
return writer.result();
1267+
}
1268+
1269+
std::string LevelDbDocumentOverlayKey::KeyPrefix(absl::string_view user_id) {
1270+
Writer writer;
1271+
writer.WriteTableName(kDocumentOverlaysTable);
1272+
writer.WriteUserId(user_id);
1273+
return writer.result();
1274+
}
1275+
1276+
std::string LevelDbDocumentOverlayKey::KeyPrefix(
1277+
absl::string_view user_id, const DocumentKey& document_key) {
1278+
Writer writer;
1279+
writer.WriteTableName(kDocumentOverlaysTable);
1280+
writer.WriteUserId(user_id);
1281+
writer.WriteResourcePath(document_key.path());
1282+
return writer.result();
1283+
}
1284+
1285+
std::string LevelDbDocumentOverlayKey::Key(absl::string_view user_id,
1286+
const DocumentKey& document_key,
1287+
model::BatchId largest_batch_id) {
1288+
Writer writer;
1289+
writer.WriteTableName(kDocumentOverlaysTable);
1290+
writer.WriteUserId(user_id);
1291+
writer.WriteResourcePath(document_key.path());
1292+
writer.WriteBatchId(largest_batch_id);
1293+
writer.WriteTerminator();
1294+
return writer.result();
1295+
}
1296+
1297+
bool LevelDbDocumentOverlayKey::Decode(absl::string_view key) {
1298+
Reader reader{key};
1299+
reader.ReadTableNameMatching(kDocumentOverlaysTable);
1300+
user_id_ = reader.ReadUserId();
1301+
document_key_ = reader.ReadDocumentKey();
1302+
largest_batch_id_ = reader.ReadBatchId();
1303+
reader.ReadTerminator();
1304+
return reader.ok();
1305+
}
1306+
12621307
} // namespace local
12631308
} // namespace firestore
12641309
} // namespace firebase

0 commit comments

Comments
 (0)