Skip to content

Commit 2bce70d

Browse files
author
Greg Soltis
authored
Port leveldb remote document cache to C++ (#2186)
* Port leveldb remote document cache
1 parent f963bfb commit 2bce70d

File tree

4 files changed

+217
-84
lines changed

4 files changed

+217
-84
lines changed

Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm

Lines changed: 10 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@
2626
#import "Firestore/Source/Model/FSTDocumentSet.h"
2727

2828
#include "Firestore/core/src/firebase/firestore/local/leveldb_key.h"
29+
#include "Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.h"
2930
#include "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h"
3031
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
3132
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
33+
#include "absl/memory/memory.h"
3234
#include "leveldb/db.h"
3335
#include "leveldb/write_batch.h"
3436

3537
NS_ASSUME_NONNULL_BEGIN
3638

39+
using firebase::firestore::local::LevelDbRemoteDocumentCache;
3740
using firebase::firestore::local::LevelDbRemoteDocumentKey;
3841
using firebase::firestore::local::LevelDbTransaction;
3942
using firebase::firestore::model::DocumentKey;
@@ -43,110 +46,35 @@
4346
using leveldb::DB;
4447
using leveldb::Status;
4548

46-
@interface FSTLevelDBRemoteDocumentCache ()
47-
48-
@property(nonatomic, strong, readonly) FSTLocalSerializer *serializer;
49-
50-
@end
51-
5249
@implementation FSTLevelDBRemoteDocumentCache {
53-
FSTLevelDB *_db;
50+
std::unique_ptr<LevelDbRemoteDocumentCache> _cache;
5451
}
5552

5653
- (instancetype)initWithDB:(FSTLevelDB *)db serializer:(FSTLocalSerializer *)serializer {
5754
if (self = [super init]) {
58-
_db = db;
59-
_serializer = serializer;
55+
_cache = absl::make_unique<LevelDbRemoteDocumentCache>(db, serializer);
6056
}
6157
return self;
6258
}
6359

6460
- (void)addEntry:(FSTMaybeDocument *)document {
65-
std::string key = [self remoteDocumentKey:document.key];
66-
_db.currentTransaction->Put(key, [self.serializer encodedMaybeDocument:document]);
61+
_cache->AddEntry(document);
6762
}
6863

6964
- (void)removeEntryForKey:(const DocumentKey &)documentKey {
70-
std::string key = [self remoteDocumentKey:documentKey];
71-
_db.currentTransaction->Delete(key);
65+
_cache->RemoveEntry(documentKey);
7266
}
7367

7468
- (nullable FSTMaybeDocument *)entryForKey:(const DocumentKey &)documentKey {
75-
std::string key = LevelDbRemoteDocumentKey::Key(documentKey);
76-
std::string value;
77-
Status status = _db.currentTransaction->Get(key, &value);
78-
if (status.IsNotFound()) {
79-
return nil;
80-
} else if (status.ok()) {
81-
return [self decodeMaybeDocument:value withKey:documentKey];
82-
} else {
83-
HARD_FAIL("Fetch document for key (%s) failed with status: %s", documentKey.ToString(),
84-
status.ToString());
85-
}
69+
return _cache->Get(documentKey);
8670
}
8771

8872
- (MaybeDocumentMap)entriesForKeys:(const DocumentKeySet &)keys {
89-
MaybeDocumentMap results;
90-
91-
LevelDbRemoteDocumentKey currentKey;
92-
auto it = _db.currentTransaction->NewIterator();
93-
94-
for (const DocumentKey &key : keys) {
95-
it->Seek([self remoteDocumentKey:key]);
96-
if (!it->Valid() || !currentKey.Decode(it->key()) || currentKey.document_key() != key) {
97-
results = results.insert(key, nil);
98-
} else {
99-
results = results.insert(key, [self decodeMaybeDocument:it->value() withKey:key]);
100-
}
101-
}
102-
103-
return results;
73+
return _cache->GetAll(keys);
10474
}
10575

10676
- (DocumentMap)documentsMatchingQuery:(FSTQuery *)query {
107-
DocumentMap results;
108-
109-
// Documents are ordered by key, so we can use a prefix scan to narrow down
110-
// the documents we need to match the query against.
111-
std::string startKey = LevelDbRemoteDocumentKey::KeyPrefix(query.path);
112-
auto it = _db.currentTransaction->NewIterator();
113-
it->Seek(startKey);
114-
115-
LevelDbRemoteDocumentKey currentKey;
116-
for (; it->Valid() && currentKey.Decode(it->key()); it->Next()) {
117-
FSTMaybeDocument *maybeDoc =
118-
[self decodeMaybeDocument:it->value() withKey:currentKey.document_key()];
119-
if (!query.path.IsPrefixOf(maybeDoc.key.path())) {
120-
break;
121-
} else if ([maybeDoc isKindOfClass:[FSTDocument class]]) {
122-
results = results.insert(maybeDoc.key, static_cast<FSTDocument *>(maybeDoc));
123-
}
124-
}
125-
126-
return results;
127-
}
128-
129-
- (std::string)remoteDocumentKey:(const DocumentKey &)key {
130-
return LevelDbRemoteDocumentKey::Key(key);
131-
}
132-
133-
- (FSTMaybeDocument *)decodeMaybeDocument:(absl::string_view)encoded
134-
withKey:(const DocumentKey &)documentKey {
135-
NSData *data = [[NSData alloc] initWithBytesNoCopy:(void *)encoded.data()
136-
length:encoded.size()
137-
freeWhenDone:NO];
138-
139-
NSError *error;
140-
FSTPBMaybeDocument *proto = [FSTPBMaybeDocument parseFromData:data error:&error];
141-
if (!proto) {
142-
HARD_FAIL("FSTPBMaybeDocument failed to parse: %s", error);
143-
}
144-
145-
FSTMaybeDocument *maybeDocument = [self.serializer decodedMaybeDocument:proto];
146-
HARD_ASSERT(maybeDocument.key == documentKey,
147-
"Read document has key (%s) instead of expected key (%s).",
148-
maybeDocument.key.ToString(), documentKey.ToString());
149-
return maybeDocument;
77+
return _cache->GetMatchingDocuments(query);
15078
}
15179

15280
@end
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2018 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LEVELDB_REMOTE_DOCUMENT_CACHE_H_
18+
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LEVELDB_REMOTE_DOCUMENT_CACHE_H_
19+
20+
#if !defined(__OBJC__)
21+
#error "For now, this file must only be included by ObjC source files."
22+
#endif // !defined(__OBJC__)
23+
24+
#include <vector>
25+
26+
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
27+
#include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
28+
#include "Firestore/core/src/firebase/firestore/model/document_map.h"
29+
#include "Firestore/core/src/firebase/firestore/model/types.h"
30+
#include "absl/strings/string_view.h"
31+
32+
@class FSTLevelDB;
33+
@class FSTLocalSerializer;
34+
@class FSTMaybeDocument;
35+
@class FSTQuery;
36+
37+
NS_ASSUME_NONNULL_BEGIN
38+
39+
namespace firebase {
40+
namespace firestore {
41+
namespace local {
42+
43+
/** Cached Remote Documents backed by leveldb. */
44+
class LevelDbRemoteDocumentCache {
45+
public:
46+
LevelDbRemoteDocumentCache(FSTLevelDB* db, FSTLocalSerializer* serializer);
47+
48+
void AddEntry(FSTMaybeDocument* document);
49+
void RemoveEntry(const model::DocumentKey& key);
50+
51+
FSTMaybeDocument* _Nullable Get(const model::DocumentKey& key);
52+
model::MaybeDocumentMap GetAll(const model::DocumentKeySet& keys);
53+
model::DocumentMap GetMatchingDocuments(FSTQuery* query);
54+
55+
private:
56+
FSTMaybeDocument* DecodeMaybeDocument(absl::string_view encoded,
57+
const model::DocumentKey& key);
58+
59+
FSTLevelDB* db_;
60+
FSTLocalSerializer* serializer_;
61+
};
62+
63+
} // namespace local
64+
} // namespace firestore
65+
} // namespace firebase
66+
67+
NS_ASSUME_NONNULL_END
68+
69+
#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LEVELDB_REMOTE_DOCUMENT_CACHE_H_
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2018 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.h"
18+
19+
#import <Foundation/Foundation.h>
20+
21+
#include <string>
22+
23+
#import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h"
24+
#import "Firestore/Source/Core/FSTQuery.h"
25+
#import "Firestore/Source/Local/FSTLevelDB.h"
26+
#import "Firestore/Source/Local/FSTLocalSerializer.h"
27+
#include "Firestore/core/src/firebase/firestore/local/leveldb_key.h"
28+
#include "Firestore/core/src/firebase/firestore/util/status.h"
29+
#include "leveldb/db.h"
30+
31+
using firebase::firestore::model::DocumentKey;
32+
using firebase::firestore::model::DocumentKeySet;
33+
using firebase::firestore::model::DocumentMap;
34+
using firebase::firestore::model::MaybeDocumentMap;
35+
using leveldb::Status;
36+
37+
namespace firebase {
38+
namespace firestore {
39+
namespace local {
40+
41+
LevelDbRemoteDocumentCache::LevelDbRemoteDocumentCache(
42+
FSTLevelDB* db, FSTLocalSerializer* serializer)
43+
: db_(db), serializer_(serializer) {
44+
}
45+
46+
void LevelDbRemoteDocumentCache::AddEntry(FSTMaybeDocument* document) {
47+
std::string ldb_key = LevelDbRemoteDocumentKey::Key(document.key);
48+
db_.currentTransaction->Put(ldb_key,
49+
[serializer_ encodedMaybeDocument:document]);
50+
}
51+
52+
void LevelDbRemoteDocumentCache::RemoveEntry(const DocumentKey& key) {
53+
std::string ldb_key = LevelDbRemoteDocumentKey::Key(key);
54+
db_.currentTransaction->Delete(ldb_key);
55+
}
56+
57+
FSTMaybeDocument* _Nullable LevelDbRemoteDocumentCache::Get(
58+
const DocumentKey& key) {
59+
std::string ldb_key = LevelDbRemoteDocumentKey::Key(key);
60+
std::string value;
61+
Status status = db_.currentTransaction->Get(ldb_key, &value);
62+
if (status.IsNotFound()) {
63+
return nil;
64+
} else if (status.ok()) {
65+
return DecodeMaybeDocument(value, key);
66+
} else {
67+
HARD_FAIL("Fetch document for key (%s) failed with status: %s",
68+
key.ToString(), status.ToString());
69+
}
70+
}
71+
72+
MaybeDocumentMap LevelDbRemoteDocumentCache::GetAll(
73+
const DocumentKeySet& keys) {
74+
MaybeDocumentMap results;
75+
76+
LevelDbRemoteDocumentKey currentKey;
77+
auto it = db_.currentTransaction->NewIterator();
78+
79+
for (const DocumentKey& key : keys) {
80+
it->Seek(LevelDbRemoteDocumentKey::Key(key));
81+
if (!it->Valid() || !currentKey.Decode(it->key()) ||
82+
currentKey.document_key() != key) {
83+
results = results.insert(key, nil);
84+
} else {
85+
results = results.insert(key, DecodeMaybeDocument(it->value(), key));
86+
}
87+
}
88+
89+
return results;
90+
}
91+
92+
DocumentMap LevelDbRemoteDocumentCache::GetMatchingDocuments(FSTQuery* query) {
93+
DocumentMap results;
94+
95+
// Documents are ordered by key, so we can use a prefix scan to narrow down
96+
// the documents we need to match the query against.
97+
std::string startKey = LevelDbRemoteDocumentKey::KeyPrefix(query.path);
98+
auto it = db_.currentTransaction->NewIterator();
99+
it->Seek(startKey);
100+
101+
LevelDbRemoteDocumentKey currentKey;
102+
for (; it->Valid() && currentKey.Decode(it->key()); it->Next()) {
103+
FSTMaybeDocument* maybeDoc =
104+
DecodeMaybeDocument(it->value(), currentKey.document_key());
105+
if (!query.path.IsPrefixOf(maybeDoc.key.path())) {
106+
break;
107+
} else if ([maybeDoc isKindOfClass:[FSTDocument class]]) {
108+
results =
109+
results.insert(maybeDoc.key, static_cast<FSTDocument*>(maybeDoc));
110+
}
111+
}
112+
113+
return results;
114+
}
115+
116+
FSTMaybeDocument* LevelDbRemoteDocumentCache::DecodeMaybeDocument(
117+
absl::string_view encoded, const DocumentKey& key) {
118+
NSData* data = [[NSData alloc] initWithBytesNoCopy:(void*)encoded.data()
119+
length:encoded.size()
120+
freeWhenDone:NO];
121+
122+
NSError* error;
123+
FSTPBMaybeDocument* proto =
124+
[FSTPBMaybeDocument parseFromData:data error:&error];
125+
if (!proto) {
126+
HARD_FAIL("FSTPBMaybeDocument failed to parse: %s", error);
127+
}
128+
129+
FSTMaybeDocument* maybeDocument = [serializer_ decodedMaybeDocument:proto];
130+
HARD_ASSERT(maybeDocument.key == key,
131+
"Read document has key (%s) instead of expected key (%s).",
132+
maybeDocument.key.ToString(), key.ToString());
133+
return maybeDocument;
134+
}
135+
136+
} // namespace local
137+
} // namespace firestore
138+
} // namespace firebase

Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
#error "For now, this file must only be included by ObjC source files."
2222
#endif // !defined(__OBJC__)
2323

24-
#import <Foundation/Foundation.h>
25-
2624
#include <vector>
2725

2826
#include "Firestore/core/src/firebase/firestore/model/document_key.h"

0 commit comments

Comments
 (0)