FSTLevelDBRemoteDocumentCache.mm 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. * Copyright 2017 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. #import "Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h"
  17. #include <leveldb/db.h>
  18. #include <leveldb/write_batch.h>
  19. #include <string>
  20. #import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h"
  21. #import "Firestore/Source/Core/FSTQuery.h"
  22. #import "Firestore/Source/Local/FSTLevelDBKey.h"
  23. #import "Firestore/Source/Local/FSTLocalSerializer.h"
  24. #import "Firestore/Source/Local/FSTWriteGroup.h"
  25. #import "Firestore/Source/Model/FSTDocument.h"
  26. #import "Firestore/Source/Model/FSTDocumentDictionary.h"
  27. #import "Firestore/Source/Model/FSTDocumentKey.h"
  28. #import "Firestore/Source/Model/FSTDocumentSet.h"
  29. #import "Firestore/Source/Util/FSTAssert.h"
  30. NS_ASSUME_NONNULL_BEGIN
  31. using leveldb::DB;
  32. using leveldb::Iterator;
  33. using leveldb::ReadOptions;
  34. using leveldb::Slice;
  35. using leveldb::Status;
  36. using leveldb::WriteOptions;
  37. @interface FSTLevelDBRemoteDocumentCache ()
  38. @property(nonatomic, strong, readonly) FSTLocalSerializer *serializer;
  39. @end
  40. /**
  41. * Returns a standard set of read options.
  42. *
  43. * For now this is paranoid, but perhaps disable that in production builds.
  44. */
  45. static ReadOptions StandardReadOptions() {
  46. ReadOptions options;
  47. options.verify_checksums = true;
  48. return options;
  49. }
  50. @implementation FSTLevelDBRemoteDocumentCache {
  51. // The DB pointer is shared with all cooperating LevelDB-related objects.
  52. std::shared_ptr<DB> _db;
  53. }
  54. - (instancetype)initWithDB:(std::shared_ptr<DB>)db serializer:(FSTLocalSerializer *)serializer {
  55. if (self = [super init]) {
  56. _db = db;
  57. _serializer = serializer;
  58. }
  59. return self;
  60. }
  61. - (void)shutdown {
  62. _db.reset();
  63. }
  64. - (void)addEntry:(FSTMaybeDocument *)document group:(FSTWriteGroup *)group {
  65. std::string key = [self remoteDocumentKey:document.key];
  66. [group setMessage:[self.serializer encodedMaybeDocument:document] forKey:key];
  67. }
  68. - (void)removeEntryForKey:(FSTDocumentKey *)documentKey group:(FSTWriteGroup *)group {
  69. std::string key = [self remoteDocumentKey:documentKey];
  70. [group removeMessageForKey:key];
  71. }
  72. - (nullable FSTMaybeDocument *)entryForKey:(FSTDocumentKey *)documentKey {
  73. std::string key = [FSTLevelDBRemoteDocumentKey keyWithDocumentKey:documentKey];
  74. std::string value;
  75. Status status = _db->Get(StandardReadOptions(), key, &value);
  76. if (status.IsNotFound()) {
  77. return nil;
  78. } else if (status.ok()) {
  79. return [self decodedMaybeDocument:value withKey:documentKey];
  80. } else {
  81. FSTFail(@"Fetch document for key (%@) failed with status: %s", documentKey,
  82. status.ToString().c_str());
  83. }
  84. }
  85. - (FSTDocumentDictionary *)documentsMatchingQuery:(FSTQuery *)query {
  86. FSTDocumentDictionary *results = [FSTDocumentDictionary documentDictionary];
  87. // Documents are ordered by key, so we can use a prefix scan to narrow down
  88. // the documents we need to match the query against.
  89. std::string startKey = [FSTLevelDBRemoteDocumentKey keyPrefixWithResourcePath:query.path];
  90. std::unique_ptr<Iterator> it(_db->NewIterator(StandardReadOptions()));
  91. it->Seek(startKey);
  92. FSTLevelDBRemoteDocumentKey *currentKey = [[FSTLevelDBRemoteDocumentKey alloc] init];
  93. for (; it->Valid() && [currentKey decodeKey:it->key()]; it->Next()) {
  94. FSTMaybeDocument *maybeDoc =
  95. [self decodedMaybeDocument:it->value() withKey:currentKey.documentKey];
  96. if (!query.path.IsPrefixOf(maybeDoc.key.path)) {
  97. break;
  98. } else if ([maybeDoc isKindOfClass:[FSTDocument class]]) {
  99. results = [results dictionaryBySettingObject:(FSTDocument *)maybeDoc forKey:maybeDoc.key];
  100. }
  101. }
  102. Status status = it->status();
  103. if (!status.ok()) {
  104. FSTFail(@"Find documents matching query (%@) failed with status: %s", query,
  105. status.ToString().c_str());
  106. }
  107. return results;
  108. }
  109. - (std::string)remoteDocumentKey:(FSTDocumentKey *)key {
  110. return [FSTLevelDBRemoteDocumentKey keyWithDocumentKey:key];
  111. }
  112. - (FSTMaybeDocument *)decodedMaybeDocument:(Slice)slice withKey:(FSTDocumentKey *)documentKey {
  113. NSData *data =
  114. [[NSData alloc] initWithBytesNoCopy:(void *)slice.data() length:slice.size() freeWhenDone:NO];
  115. NSError *error;
  116. FSTPBMaybeDocument *proto = [FSTPBMaybeDocument parseFromData:data error:&error];
  117. if (!proto) {
  118. FSTFail(@"FSTPBMaybeDocument failed to parse: %@", error);
  119. }
  120. FSTMaybeDocument *maybeDocument = [self.serializer decodedMaybeDocument:proto];
  121. FSTAssert([maybeDocument.key isEqualToKey:documentKey],
  122. @"Read document has key (%@) instead of expected key (%@).", maybeDocument.key,
  123. documentKey);
  124. return maybeDocument;
  125. }
  126. @end
  127. NS_ASSUME_NONNULL_END