FSTLevelDBQueryCache.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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/FSTLevelDBQueryCache.h"
  17. #include <leveldb/db.h>
  18. #include <leveldb/write_batch.h>
  19. #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
  20. #import "Firestore/Source/Core/FSTQuery.h"
  21. #import "Firestore/Source/Local/FSTLevelDB.h"
  22. #import "Firestore/Source/Local/FSTLevelDBKey.h"
  23. #import "Firestore/Source/Local/FSTLocalSerializer.h"
  24. #import "Firestore/Source/Local/FSTQueryData.h"
  25. #import "Firestore/Source/Local/FSTWriteGroup.h"
  26. #import "Firestore/Source/Model/FSTDocumentKey.h"
  27. #import "Firestore/Source/Util/FSTAssert.h"
  28. NS_ASSUME_NONNULL_BEGIN
  29. using Firestore::StringView;
  30. using leveldb::DB;
  31. using leveldb::Iterator;
  32. using leveldb::ReadOptions;
  33. using leveldb::Slice;
  34. using leveldb::Status;
  35. using leveldb::WriteOptions;
  36. @interface FSTLevelDBQueryCache ()
  37. /** A write-through cached copy of the metadata for the query cache. */
  38. @property(nonatomic, strong, nullable) FSTPBTargetGlobal *metadata;
  39. @property(nonatomic, strong, readonly) FSTLocalSerializer *serializer;
  40. @end
  41. @implementation FSTLevelDBQueryCache {
  42. // The DB pointer is shared with all cooperating LevelDB-related objects.
  43. std::shared_ptr<DB> _db;
  44. /**
  45. * The last received snapshot version. This is part of `metadata` but we store it separately to
  46. * avoid extra conversion to/from GPBTimestamp.
  47. */
  48. FSTSnapshotVersion *_lastRemoteSnapshotVersion;
  49. }
  50. + (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(std::shared_ptr<DB>)db {
  51. std::string key = [FSTLevelDBTargetGlobalKey key];
  52. std::string value;
  53. Status status = db->Get([FSTLevelDB standardReadOptions], key, &value);
  54. if (status.IsNotFound()) {
  55. return nil;
  56. } else if (!status.ok()) {
  57. FSTFail(@"metadataForKey: failed loading key %s with status: %s", key.c_str(),
  58. status.ToString().c_str());
  59. }
  60. NSData *data =
  61. [[NSData alloc] initWithBytesNoCopy:(void *)value.data() length:value.size() freeWhenDone:NO];
  62. NSError *error;
  63. FSTPBTargetGlobal *proto = [FSTPBTargetGlobal parseFromData:data error:&error];
  64. if (!proto) {
  65. FSTFail(@"FSTPBTargetGlobal failed to parse: %@", error);
  66. }
  67. return proto;
  68. }
  69. - (instancetype)initWithDB:(std::shared_ptr<DB>)db serializer:(FSTLocalSerializer *)serializer {
  70. if (self = [super init]) {
  71. FSTAssert(db, @"db must not be NULL");
  72. _db = db;
  73. _serializer = serializer;
  74. }
  75. return self;
  76. }
  77. - (void)start {
  78. FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db];
  79. FSTAssert(
  80. metadata != nil,
  81. @"Found nil metadata, expected schema to be at version 0 which ensures metadata existence");
  82. _lastRemoteSnapshotVersion = [self.serializer decodedVersion:metadata.lastRemoteSnapshotVersion];
  83. self.metadata = metadata;
  84. }
  85. #pragma mark - FSTQueryCache implementation
  86. - (FSTTargetID)highestTargetID {
  87. return self.metadata.highestTargetId;
  88. }
  89. - (FSTListenSequenceNumber)highestListenSequenceNumber {
  90. return self.metadata.highestListenSequenceNumber;
  91. }
  92. - (FSTSnapshotVersion *)lastRemoteSnapshotVersion {
  93. return _lastRemoteSnapshotVersion;
  94. }
  95. - (void)setLastRemoteSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
  96. group:(FSTWriteGroup *)group {
  97. _lastRemoteSnapshotVersion = snapshotVersion;
  98. self.metadata.lastRemoteSnapshotVersion = [self.serializer encodedVersion:snapshotVersion];
  99. [group setMessage:self.metadata forKey:[FSTLevelDBTargetGlobalKey key]];
  100. }
  101. - (void)shutdown {
  102. _db.reset();
  103. }
  104. - (void)saveQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
  105. FSTTargetID targetID = queryData.targetID;
  106. std::string key = [FSTLevelDBTargetKey keyWithTargetID:targetID];
  107. [group setMessage:[self.serializer encodedQueryData:queryData] forKey:key];
  108. }
  109. - (void)saveMetadataInGroup:(FSTWriteGroup *)group {
  110. [group setMessage:self.metadata forKey:[FSTLevelDBTargetGlobalKey key]];
  111. }
  112. - (BOOL)updateMetadataForQueryData:(FSTQueryData *)queryData {
  113. BOOL updatedMetadata = NO;
  114. if (queryData.targetID > self.metadata.highestTargetId) {
  115. self.metadata.highestTargetId = queryData.targetID;
  116. updatedMetadata = YES;
  117. }
  118. if (queryData.sequenceNumber > self.metadata.highestListenSequenceNumber) {
  119. self.metadata.highestListenSequenceNumber = queryData.sequenceNumber;
  120. updatedMetadata = YES;
  121. }
  122. return updatedMetadata;
  123. }
  124. - (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
  125. [self saveQueryData:queryData group:group];
  126. NSString *canonicalID = queryData.query.canonicalID;
  127. std::string indexKey =
  128. [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:queryData.targetID];
  129. std::string emptyBuffer;
  130. [group setData:emptyBuffer forKey:indexKey];
  131. self.metadata.targetCount += 1;
  132. [self updateMetadataForQueryData:queryData];
  133. [self saveMetadataInGroup:group];
  134. }
  135. - (void)updateQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
  136. [self saveQueryData:queryData group:group];
  137. if ([self updateMetadataForQueryData:queryData]) {
  138. [self saveMetadataInGroup:group];
  139. }
  140. }
  141. - (void)removeQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
  142. FSTTargetID targetID = queryData.targetID;
  143. [self removeMatchingKeysForTargetID:targetID group:group];
  144. std::string key = [FSTLevelDBTargetKey keyWithTargetID:targetID];
  145. [group removeMessageForKey:key];
  146. std::string indexKey =
  147. [FSTLevelDBQueryTargetKey keyWithCanonicalID:queryData.query.canonicalID targetID:targetID];
  148. [group removeMessageForKey:indexKey];
  149. self.metadata.targetCount -= 1;
  150. [self saveMetadataInGroup:group];
  151. }
  152. - (int32_t)count {
  153. return self.metadata.targetCount;
  154. }
  155. /**
  156. * Parses the given bytes as an FSTPBTarget protocol buffer and then converts to the equivalent
  157. * query data.
  158. */
  159. - (FSTQueryData *)decodedTargetWithSlice:(Slice)slice {
  160. NSData *data =
  161. [[NSData alloc] initWithBytesNoCopy:(void *)slice.data() length:slice.size() freeWhenDone:NO];
  162. NSError *error;
  163. FSTPBTarget *proto = [FSTPBTarget parseFromData:data error:&error];
  164. if (!proto) {
  165. FSTFail(@"FSTPBTarget failed to parse: %@", error);
  166. }
  167. return [self.serializer decodedQueryData:proto];
  168. }
  169. - (nullable FSTQueryData *)queryDataForQuery:(FSTQuery *)query {
  170. // Scan the query-target index starting with a prefix starting with the given query's canonicalID.
  171. // Note that this is a scan rather than a get because canonicalIDs are not required to be unique
  172. // per target.
  173. Slice canonicalID = StringView(query.canonicalID);
  174. std::unique_ptr<Iterator> indexItererator(_db->NewIterator([FSTLevelDB standardReadOptions]));
  175. std::string indexPrefix = [FSTLevelDBQueryTargetKey keyPrefixWithCanonicalID:canonicalID];
  176. indexItererator->Seek(indexPrefix);
  177. // Simultaneously scan the targets table. This works because each (canonicalID, targetID) pair is
  178. // unique and ordered, so when scanning a table prefixed by exactly one canonicalID, all the
  179. // targetIDs will be unique and in order.
  180. std::string targetPrefix = [FSTLevelDBTargetKey keyPrefix];
  181. std::unique_ptr<Iterator> targetIterator(_db->NewIterator([FSTLevelDB standardReadOptions]));
  182. FSTLevelDBQueryTargetKey *rowKey = [[FSTLevelDBQueryTargetKey alloc] init];
  183. for (; indexItererator->Valid(); indexItererator->Next()) {
  184. Slice indexKey = indexItererator->key();
  185. // Only consider rows matching exactly the specific canonicalID of interest.
  186. if (!indexKey.starts_with(indexPrefix) || ![rowKey decodeKey:indexKey] ||
  187. canonicalID != rowKey.canonicalID) {
  188. // End of this canonicalID's possible targets.
  189. break;
  190. }
  191. // Each row is a unique combination of canonicalID and targetID, so this foreign key reference
  192. // can only occur once.
  193. std::string targetKey = [FSTLevelDBTargetKey keyWithTargetID:rowKey.targetID];
  194. targetIterator->Seek(targetKey);
  195. if (!targetIterator->Valid() || targetIterator->key() != targetKey) {
  196. NSString *foundKeyDescription = @"the end of the table";
  197. if (targetIterator->Valid()) {
  198. foundKeyDescription = [FSTLevelDBKey descriptionForKey:targetIterator->key()];
  199. }
  200. FSTFail(
  201. @"Dangling query-target reference found: "
  202. @"%@ points to %@; seeking there found %@",
  203. [FSTLevelDBKey descriptionForKey:indexKey], [FSTLevelDBKey descriptionForKey:targetKey],
  204. foundKeyDescription);
  205. }
  206. // Finally after finding a potential match, check that the query is actually equal to the
  207. // requested query.
  208. FSTQueryData *target = [self decodedTargetWithSlice:targetIterator->value()];
  209. if ([target.query isEqual:query]) {
  210. return target;
  211. }
  212. }
  213. return nil;
  214. }
  215. #pragma mark Matching Key tracking
  216. - (void)addMatchingKeys:(FSTDocumentKeySet *)keys
  217. forTargetID:(FSTTargetID)targetID
  218. group:(FSTWriteGroup *)group {
  219. // Store an empty value in the index which is equivalent to serializing a GPBEmpty message. In the
  220. // future if we wanted to store some other kind of value here, we can parse these empty values as
  221. // with some other protocol buffer (and the parser will see all default values).
  222. std::string emptyBuffer;
  223. [keys enumerateObjectsUsingBlock:^(FSTDocumentKey *documentKey, BOOL *stop) {
  224. [group setData:emptyBuffer
  225. forKey:[FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:documentKey]];
  226. [group setData:emptyBuffer
  227. forKey:[FSTLevelDBDocumentTargetKey keyWithDocumentKey:documentKey targetID:targetID]];
  228. }];
  229. }
  230. - (void)removeMatchingKeys:(FSTDocumentKeySet *)keys
  231. forTargetID:(FSTTargetID)targetID
  232. group:(FSTWriteGroup *)group {
  233. [keys enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
  234. [group
  235. removeMessageForKey:[FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:key]];
  236. [group
  237. removeMessageForKey:[FSTLevelDBDocumentTargetKey keyWithDocumentKey:key targetID:targetID]];
  238. [self.garbageCollector addPotentialGarbageKey:key];
  239. }];
  240. }
  241. - (void)removeMatchingKeysForTargetID:(FSTTargetID)targetID group:(FSTWriteGroup *)group {
  242. std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID];
  243. std::unique_ptr<Iterator> indexIterator(_db->NewIterator([FSTLevelDB standardReadOptions]));
  244. indexIterator->Seek(indexPrefix);
  245. FSTLevelDBTargetDocumentKey *rowKey = [[FSTLevelDBTargetDocumentKey alloc] init];
  246. for (; indexIterator->Valid(); indexIterator->Next()) {
  247. Slice indexKey = indexIterator->key();
  248. // Only consider rows matching this specific targetID.
  249. if (![rowKey decodeKey:indexKey] || rowKey.targetID != targetID) {
  250. break;
  251. }
  252. FSTDocumentKey *documentKey = rowKey.documentKey;
  253. // Delete both index rows
  254. [group removeMessageForKey:indexKey];
  255. [group removeMessageForKey:[FSTLevelDBDocumentTargetKey keyWithDocumentKey:documentKey
  256. targetID:targetID]];
  257. [self.garbageCollector addPotentialGarbageKey:documentKey];
  258. }
  259. }
  260. - (FSTDocumentKeySet *)matchingKeysForTargetID:(FSTTargetID)targetID {
  261. std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID];
  262. std::unique_ptr<Iterator> indexIterator(_db->NewIterator([FSTLevelDB standardReadOptions]));
  263. indexIterator->Seek(indexPrefix);
  264. FSTDocumentKeySet *result = [FSTDocumentKeySet keySet];
  265. FSTLevelDBTargetDocumentKey *rowKey = [[FSTLevelDBTargetDocumentKey alloc] init];
  266. for (; indexIterator->Valid(); indexIterator->Next()) {
  267. Slice indexKey = indexIterator->key();
  268. // Only consider rows matching this specific targetID.
  269. if (![rowKey decodeKey:indexKey] || rowKey.targetID != targetID) {
  270. break;
  271. }
  272. result = [result setByAddingObject:rowKey.documentKey];
  273. }
  274. return result;
  275. }
  276. #pragma mark - FSTGarbageSource implementation
  277. - (BOOL)containsKey:(FSTDocumentKey *)key {
  278. std::string indexPrefix = [FSTLevelDBDocumentTargetKey keyPrefixWithResourcePath:key.path];
  279. std::unique_ptr<Iterator> indexIterator(_db->NewIterator([FSTLevelDB standardReadOptions]));
  280. indexIterator->Seek(indexPrefix);
  281. if (indexIterator->Valid()) {
  282. FSTLevelDBDocumentTargetKey *rowKey = [[FSTLevelDBDocumentTargetKey alloc] init];
  283. if ([rowKey decodeKey:indexIterator->key()] && [rowKey.documentKey isEqualToKey:key]) {
  284. return YES;
  285. }
  286. }
  287. return NO;
  288. }
  289. @end
  290. NS_ASSUME_NONNULL_END