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