FSTLevelDBQueryCache.mm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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 <memory>
  18. #include <string>
  19. #include <utility>
  20. #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
  21. #import "Firestore/Source/Core/FSTQuery.h"
  22. #import "Firestore/Source/Local/FSTLevelDB.h"
  23. #import "Firestore/Source/Local/FSTLevelDBKey.h"
  24. #import "Firestore/Source/Local/FSTLocalSerializer.h"
  25. #import "Firestore/Source/Local/FSTQueryData.h"
  26. #include "Firestore/core/src/firebase/firestore/local/leveldb_key.h"
  27. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  28. #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
  29. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  30. #include "Firestore/core/src/firebase/firestore/util/ordered_code.h"
  31. #include "absl/strings/match.h"
  32. NS_ASSUME_NONNULL_BEGIN
  33. using Firestore::StringView;
  34. using firebase::firestore::local::DescribeKey;
  35. using firebase::firestore::local::LevelDbTransaction;
  36. using firebase::firestore::model::DocumentKey;
  37. using firebase::firestore::model::DocumentKeySet;
  38. using firebase::firestore::model::SnapshotVersion;
  39. using firebase::firestore::util::OrderedCode;
  40. using leveldb::DB;
  41. using leveldb::Slice;
  42. using leveldb::Status;
  43. namespace {
  44. FSTListenSequenceNumber ReadSequenceNumber(const absl::string_view &slice) {
  45. FSTListenSequenceNumber decoded;
  46. absl::string_view tmp(slice.data(), slice.size());
  47. if (!OrderedCode::ReadSignedNumIncreasing(&tmp, &decoded)) {
  48. HARD_FAIL("Failed to read sequence number from a sentinel row");
  49. }
  50. return decoded;
  51. }
  52. } // namespace
  53. @interface FSTLevelDBQueryCache ()
  54. /** A write-through cached copy of the metadata for the query cache. */
  55. @property(nonatomic, strong, nullable) FSTPBTargetGlobal *metadata;
  56. @property(nonatomic, strong, readonly) FSTLocalSerializer *serializer;
  57. @end
  58. @implementation FSTLevelDBQueryCache {
  59. FSTLevelDB *_db;
  60. /**
  61. * The last received snapshot version. This is part of `metadata` but we store it separately to
  62. * avoid extra conversion to/from GPBTimestamp.
  63. */
  64. SnapshotVersion _lastRemoteSnapshotVersion;
  65. }
  66. + (nullable FSTPBTargetGlobal *)readTargetMetadataWithTransaction:
  67. (firebase::firestore::local::LevelDbTransaction *)transaction {
  68. std::string key = [FSTLevelDBTargetGlobalKey key];
  69. std::string value;
  70. Status status = transaction->Get(key, &value);
  71. if (status.IsNotFound()) {
  72. return nil;
  73. } else if (!status.ok()) {
  74. HARD_FAIL("metadataForKey: failed loading key %s with status: %s", key, status.ToString());
  75. }
  76. NSData *data =
  77. [[NSData alloc] initWithBytesNoCopy:(void *)value.data() length:value.size() freeWhenDone:NO];
  78. NSError *error;
  79. FSTPBTargetGlobal *proto = [FSTPBTargetGlobal parseFromData:data error:&error];
  80. if (!proto) {
  81. HARD_FAIL("FSTPBTargetGlobal failed to parse: %s", error);
  82. }
  83. return proto;
  84. }
  85. + (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(DB *)db {
  86. std::string key = [FSTLevelDBTargetGlobalKey key];
  87. std::string value;
  88. Status status = db->Get([FSTLevelDB standardReadOptions], key, &value);
  89. if (status.IsNotFound()) {
  90. return nil;
  91. } else if (!status.ok()) {
  92. HARD_FAIL("metadataForKey: failed loading key %s with status: %s", key, status.ToString());
  93. }
  94. NSData *data =
  95. [[NSData alloc] initWithBytesNoCopy:(void *)value.data() length:value.size() freeWhenDone:NO];
  96. NSError *error;
  97. FSTPBTargetGlobal *proto = [FSTPBTargetGlobal parseFromData:data error:&error];
  98. if (!proto) {
  99. HARD_FAIL("FSTPBTargetGlobal failed to parse: %s", error);
  100. }
  101. return proto;
  102. }
  103. - (instancetype)initWithDB:(FSTLevelDB *)db serializer:(FSTLocalSerializer *)serializer {
  104. if (self = [super init]) {
  105. HARD_ASSERT(db, "db must not be NULL");
  106. _db = db;
  107. _serializer = serializer;
  108. }
  109. return self;
  110. }
  111. - (void)start {
  112. // TODO(gsoltis): switch this usage of ptr to currentTransaction
  113. FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db.ptr];
  114. HARD_ASSERT(
  115. metadata != nil,
  116. "Found nil metadata, expected schema to be at version 0 which ensures metadata existence");
  117. _lastRemoteSnapshotVersion = [self.serializer decodedVersion:metadata.lastRemoteSnapshotVersion];
  118. self.metadata = metadata;
  119. }
  120. #pragma mark - FSTQueryCache implementation
  121. - (FSTTargetID)highestTargetID {
  122. return self.metadata.highestTargetId;
  123. }
  124. - (FSTListenSequenceNumber)highestListenSequenceNumber {
  125. return self.metadata.highestListenSequenceNumber;
  126. }
  127. - (const SnapshotVersion &)lastRemoteSnapshotVersion {
  128. return _lastRemoteSnapshotVersion;
  129. }
  130. - (void)setLastRemoteSnapshotVersion:(SnapshotVersion)snapshotVersion {
  131. _lastRemoteSnapshotVersion = std::move(snapshotVersion);
  132. self.metadata.lastRemoteSnapshotVersion =
  133. [self.serializer encodedVersion:_lastRemoteSnapshotVersion];
  134. _db.currentTransaction->Put([FSTLevelDBTargetGlobalKey key], self.metadata);
  135. }
  136. - (void)enumerateTargetsUsingBlock:(void (^)(FSTQueryData *queryData, BOOL *stop))block {
  137. // Enumerate all targets, give their sequence numbers.
  138. std::string targetPrefix = [FSTLevelDBTargetKey keyPrefix];
  139. auto it = _db.currentTransaction->NewIterator();
  140. it->Seek(targetPrefix);
  141. BOOL stop = NO;
  142. for (; !stop && it->Valid() && absl::StartsWith(it->key(), targetPrefix); it->Next()) {
  143. FSTQueryData *target = [self decodedTarget:it->value()];
  144. block(target, &stop);
  145. }
  146. }
  147. - (void)enumerateOrphanedDocumentsUsingBlock:
  148. (void (^)(const DocumentKey &docKey, FSTListenSequenceNumber sequenceNumber, BOOL *stop))block {
  149. std::string documentTargetPrefix = [FSTLevelDBDocumentTargetKey keyPrefix];
  150. auto it = _db.currentTransaction->NewIterator();
  151. it->Seek(documentTargetPrefix);
  152. FSTListenSequenceNumber nextToReport = 0;
  153. DocumentKey keyToReport;
  154. FSTLevelDBDocumentTargetKey *key = [[FSTLevelDBDocumentTargetKey alloc] init];
  155. BOOL stop = NO;
  156. for (; !stop && it->Valid() && absl::StartsWith(it->key(), documentTargetPrefix); it->Next()) {
  157. [key decodeKey:it->key()];
  158. if (FSTTargetIDIsSentinel(key.targetID)) {
  159. // if nextToReport is non-zero, report it, this is a new key so the last one
  160. // must be not be a member of any targets.
  161. if (nextToReport != 0) {
  162. block(keyToReport, nextToReport, &stop);
  163. }
  164. // set nextToReport to be this sequence number. It's the next one we might
  165. // report, if we don't find any targets for this document.
  166. nextToReport = ReadSequenceNumber(it->value());
  167. keyToReport = key.documentKey;
  168. } else {
  169. // set nextToReport to be 0, we know we don't need to report this one since
  170. // we found a target for it.
  171. nextToReport = 0;
  172. }
  173. }
  174. // if not stop and nextToReport is non-zero, report it. We didn't find any targets for
  175. // that document, and we weren't asked to stop.
  176. if (!stop && nextToReport != 0) {
  177. block(keyToReport, nextToReport, &stop);
  178. }
  179. }
  180. - (void)saveQueryData:(FSTQueryData *)queryData {
  181. FSTTargetID targetID = queryData.targetID;
  182. std::string key = [FSTLevelDBTargetKey keyWithTargetID:targetID];
  183. _db.currentTransaction->Put(key, [self.serializer encodedQueryData:queryData]);
  184. }
  185. - (BOOL)updateMetadataForQueryData:(FSTQueryData *)queryData {
  186. BOOL updatedMetadata = NO;
  187. if (queryData.targetID > self.metadata.highestTargetId) {
  188. self.metadata.highestTargetId = queryData.targetID;
  189. updatedMetadata = YES;
  190. }
  191. if (queryData.sequenceNumber > self.metadata.highestListenSequenceNumber) {
  192. self.metadata.highestListenSequenceNumber = queryData.sequenceNumber;
  193. updatedMetadata = YES;
  194. }
  195. return updatedMetadata;
  196. }
  197. - (void)addQueryData:(FSTQueryData *)queryData {
  198. [self saveQueryData:queryData];
  199. NSString *canonicalID = queryData.query.canonicalID;
  200. std::string indexKey =
  201. [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:queryData.targetID];
  202. std::string emptyBuffer;
  203. _db.currentTransaction->Put(indexKey, emptyBuffer);
  204. self.metadata.targetCount += 1;
  205. [self updateMetadataForQueryData:queryData];
  206. _db.currentTransaction->Put([FSTLevelDBTargetGlobalKey key], self.metadata);
  207. }
  208. - (void)updateQueryData:(FSTQueryData *)queryData {
  209. [self saveQueryData:queryData];
  210. if ([self updateMetadataForQueryData:queryData]) {
  211. _db.currentTransaction->Put([FSTLevelDBTargetGlobalKey key], self.metadata);
  212. }
  213. }
  214. - (void)removeQueryData:(FSTQueryData *)queryData {
  215. FSTTargetID targetID = queryData.targetID;
  216. [self removeMatchingKeysForTargetID:targetID];
  217. std::string key = [FSTLevelDBTargetKey keyWithTargetID:targetID];
  218. _db.currentTransaction->Delete(key);
  219. std::string indexKey =
  220. [FSTLevelDBQueryTargetKey keyWithCanonicalID:queryData.query.canonicalID targetID:targetID];
  221. _db.currentTransaction->Delete(indexKey);
  222. self.metadata.targetCount -= 1;
  223. _db.currentTransaction->Put([FSTLevelDBTargetGlobalKey key], self.metadata);
  224. }
  225. - (int)removeQueriesThroughSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
  226. liveQueries:(NSDictionary<NSNumber *, FSTQueryData *> *)liveQueries {
  227. int count = 0;
  228. std::string targetPrefix = [FSTLevelDBTargetKey keyPrefix];
  229. auto it = _db.currentTransaction->NewIterator();
  230. it->Seek(targetPrefix);
  231. for (; it->Valid() && absl::StartsWith(it->key(), targetPrefix); it->Next()) {
  232. FSTQueryData *queryData = [self decodedTarget:it->value()];
  233. if (queryData.sequenceNumber <= sequenceNumber && !liveQueries[@(queryData.targetID)]) {
  234. [self removeQueryData:queryData];
  235. count++;
  236. }
  237. }
  238. return count;
  239. }
  240. - (int32_t)count {
  241. return self.metadata.targetCount;
  242. }
  243. /**
  244. * Parses the given bytes as an FSTPBTarget protocol buffer and then converts to the equivalent
  245. * query data.
  246. */
  247. - (FSTQueryData *)decodedTarget:(absl::string_view)encoded {
  248. NSData *data = [[NSData alloc] initWithBytesNoCopy:(void *)encoded.data()
  249. length:encoded.size()
  250. freeWhenDone:NO];
  251. NSError *error;
  252. FSTPBTarget *proto = [FSTPBTarget parseFromData:data error:&error];
  253. if (!proto) {
  254. HARD_FAIL("FSTPBTarget failed to parse: %s", error);
  255. }
  256. return [self.serializer decodedQueryData:proto];
  257. }
  258. - (nullable FSTQueryData *)queryDataForQuery:(FSTQuery *)query {
  259. // Scan the query-target index starting with a prefix starting with the given query's canonicalID.
  260. // Note that this is a scan rather than a get because canonicalIDs are not required to be unique
  261. // per target.
  262. Slice canonicalID = StringView(query.canonicalID);
  263. auto indexItererator = _db.currentTransaction->NewIterator();
  264. std::string indexPrefix = [FSTLevelDBQueryTargetKey keyPrefixWithCanonicalID:canonicalID];
  265. indexItererator->Seek(indexPrefix);
  266. // Simultaneously scan the targets table. This works because each (canonicalID, targetID) pair is
  267. // unique and ordered, so when scanning a table prefixed by exactly one canonicalID, all the
  268. // targetIDs will be unique and in order.
  269. std::string targetPrefix = [FSTLevelDBTargetKey keyPrefix];
  270. auto targetIterator = _db.currentTransaction->NewIterator();
  271. FSTLevelDBQueryTargetKey *rowKey = [[FSTLevelDBQueryTargetKey alloc] init];
  272. for (; indexItererator->Valid(); indexItererator->Next()) {
  273. // Only consider rows matching exactly the specific canonicalID of interest.
  274. if (!absl::StartsWith(indexItererator->key(), indexPrefix) ||
  275. ![rowKey decodeKey:indexItererator->key()] || canonicalID != rowKey.canonicalID) {
  276. // End of this canonicalID's possible targets.
  277. break;
  278. }
  279. // Each row is a unique combination of canonicalID and targetID, so this foreign key reference
  280. // can only occur once.
  281. std::string targetKey = [FSTLevelDBTargetKey keyWithTargetID:rowKey.targetID];
  282. targetIterator->Seek(targetKey);
  283. if (!targetIterator->Valid() || targetIterator->key() != targetKey) {
  284. HARD_FAIL(
  285. "Dangling query-target reference found: "
  286. "%s points to %s; seeking there found %s",
  287. DescribeKey(indexItererator), DescribeKey(targetKey), DescribeKey(targetIterator));
  288. }
  289. // Finally after finding a potential match, check that the query is actually equal to the
  290. // requested query.
  291. FSTQueryData *target = [self decodedTarget:targetIterator->value()];
  292. if ([target.query isEqual:query]) {
  293. return target;
  294. }
  295. }
  296. return nil;
  297. }
  298. #pragma mark Matching Key tracking
  299. - (void)addMatchingKeys:(const DocumentKeySet &)keys forTargetID:(FSTTargetID)targetID {
  300. // Store an empty value in the index which is equivalent to serializing a GPBEmpty message. In the
  301. // future if we wanted to store some other kind of value here, we can parse these empty values as
  302. // with some other protocol buffer (and the parser will see all default values).
  303. std::string emptyBuffer;
  304. for (const DocumentKey &key : keys) {
  305. self->_db.currentTransaction->Put(
  306. [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:key], emptyBuffer);
  307. self->_db.currentTransaction->Put(
  308. [FSTLevelDBDocumentTargetKey keyWithDocumentKey:key targetID:targetID], emptyBuffer);
  309. [self->_db.referenceDelegate addReference:key];
  310. };
  311. }
  312. - (void)removeMatchingKeys:(const DocumentKeySet &)keys forTargetID:(FSTTargetID)targetID {
  313. for (const DocumentKey &key : keys) {
  314. self->_db.currentTransaction->Delete(
  315. [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:key]);
  316. self->_db.currentTransaction->Delete(
  317. [FSTLevelDBDocumentTargetKey keyWithDocumentKey:key targetID:targetID]);
  318. [self->_db.referenceDelegate removeReference:key];
  319. }
  320. }
  321. - (void)removeMatchingKeysForTargetID:(FSTTargetID)targetID {
  322. std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID];
  323. auto indexIterator = _db.currentTransaction->NewIterator();
  324. indexIterator->Seek(indexPrefix);
  325. FSTLevelDBTargetDocumentKey *rowKey = [[FSTLevelDBTargetDocumentKey alloc] init];
  326. for (; indexIterator->Valid(); indexIterator->Next()) {
  327. absl::string_view indexKey = indexIterator->key();
  328. // Only consider rows matching this specific targetID.
  329. if (![rowKey decodeKey:indexKey] || rowKey.targetID != targetID) {
  330. break;
  331. }
  332. const DocumentKey &documentKey = rowKey.documentKey;
  333. // Delete both index rows
  334. _db.currentTransaction->Delete(indexKey);
  335. _db.currentTransaction->Delete(
  336. [FSTLevelDBDocumentTargetKey keyWithDocumentKey:documentKey targetID:targetID]);
  337. }
  338. }
  339. - (DocumentKeySet)matchingKeysForTargetID:(FSTTargetID)targetID {
  340. std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID];
  341. auto indexIterator = _db.currentTransaction->NewIterator();
  342. indexIterator->Seek(indexPrefix);
  343. DocumentKeySet result;
  344. FSTLevelDBTargetDocumentKey *rowKey = [[FSTLevelDBTargetDocumentKey alloc] init];
  345. for (; indexIterator->Valid(); indexIterator->Next()) {
  346. absl::string_view indexKey = indexIterator->key();
  347. // Only consider rows matching this specific targetID.
  348. if (![rowKey decodeKey:indexKey] || rowKey.targetID != targetID) {
  349. break;
  350. }
  351. result = result.insert(rowKey.documentKey);
  352. }
  353. return result;
  354. }
  355. - (BOOL)containsKey:(const DocumentKey &)key {
  356. // ignore sentinel rows when determining if a key belongs to a target. Sentinel row just says the
  357. // document exists, not that it's a member of any particular target.
  358. std::string indexPrefix = [FSTLevelDBDocumentTargetKey keyPrefixWithResourcePath:key.path()];
  359. auto indexIterator = _db.currentTransaction->NewIterator();
  360. indexIterator->Seek(indexPrefix);
  361. for (; indexIterator->Valid() && absl::StartsWith(indexIterator->key(), indexPrefix);
  362. indexIterator->Next()) {
  363. FSTLevelDBDocumentTargetKey *rowKey = [[FSTLevelDBDocumentTargetKey alloc] init];
  364. if ([rowKey decodeKey:indexIterator->key()] && !FSTTargetIDIsSentinel(rowKey.targetID) &&
  365. DocumentKey{rowKey.documentKey} == key) {
  366. return YES;
  367. }
  368. }
  369. return NO;
  370. }
  371. @end
  372. NS_ASSUME_NONNULL_END