FSTLevelDBRemoteDocumentCache.mm 5.2 KB

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