FSTMemoryPersistence.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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/FSTMemoryPersistence.h"
  17. #include <memory>
  18. #include <unordered_map>
  19. #include <unordered_set>
  20. #include <vector>
  21. #import "Firestore/Source/Core/FSTListenSequence.h"
  22. #import "Firestore/Source/Local/FSTMemoryMutationQueue.h"
  23. #import "Firestore/Source/Local/FSTMemoryQueryCache.h"
  24. #import "Firestore/Source/Local/FSTMemoryRemoteDocumentCache.h"
  25. #import "Firestore/Source/Local/FSTReferenceSet.h"
  26. #include "absl/memory/memory.h"
  27. #include "Firestore/core/src/firebase/firestore/auth/user.h"
  28. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  29. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  30. using firebase::firestore::auth::HashUser;
  31. using firebase::firestore::auth::User;
  32. using firebase::firestore::local::LruParams;
  33. using firebase::firestore::model::DocumentKey;
  34. using firebase::firestore::model::DocumentKeyHash;
  35. using firebase::firestore::model::ListenSequenceNumber;
  36. using firebase::firestore::util::Status;
  37. using MutationQueues = std::unordered_map<User, FSTMemoryMutationQueue *, HashUser>;
  38. NS_ASSUME_NONNULL_BEGIN
  39. @interface FSTMemoryPersistence ()
  40. - (FSTMemoryQueryCache *)queryCache;
  41. - (FSTMemoryRemoteDocumentCache *)remoteDocumentCache;
  42. @property(nonatomic, readonly) MutationQueues &mutationQueues;
  43. @property(nonatomic, assign, getter=isStarted) BOOL started;
  44. // Make this property writable so we can wire up a delegate.
  45. @property(nonatomic, strong) id<FSTReferenceDelegate> referenceDelegate;
  46. @end
  47. @implementation FSTMemoryPersistence {
  48. /**
  49. * The FSTQueryCache representing the persisted cache of queries.
  50. *
  51. * Note that this is retained here to make it easier to write tests affecting both the in-memory
  52. * and LevelDB-backed persistence layers. Tests can create a new FSTLocalStore wrapping this
  53. * FSTPersistence instance and this will make the in-memory persistence layer behave as if it
  54. * were actually persisting values.
  55. */
  56. FSTMemoryQueryCache *_queryCache;
  57. /** The FSTRemoteDocumentCache representing the persisted cache of remote documents. */
  58. FSTMemoryRemoteDocumentCache *_remoteDocumentCache;
  59. FSTTransactionRunner _transactionRunner;
  60. id<FSTReferenceDelegate> _referenceDelegate;
  61. }
  62. + (instancetype)persistenceWithEagerGC {
  63. FSTMemoryPersistence *persistence = [[FSTMemoryPersistence alloc] init];
  64. persistence.referenceDelegate =
  65. [[FSTMemoryEagerReferenceDelegate alloc] initWithPersistence:persistence];
  66. return persistence;
  67. }
  68. + (instancetype)persistenceWithLruParams:(firebase::firestore::local::LruParams)lruParams
  69. serializer:(FSTLocalSerializer *)serializer {
  70. FSTMemoryPersistence *persistence = [[FSTMemoryPersistence alloc] init];
  71. persistence.referenceDelegate =
  72. [[FSTMemoryLRUReferenceDelegate alloc] initWithPersistence:persistence
  73. serializer:serializer
  74. lruParams:lruParams];
  75. return persistence;
  76. }
  77. - (instancetype)init {
  78. if (self = [super init]) {
  79. _queryCache = [[FSTMemoryQueryCache alloc] initWithPersistence:self];
  80. _remoteDocumentCache = [[FSTMemoryRemoteDocumentCache alloc] init];
  81. }
  82. return self;
  83. }
  84. - (void)setReferenceDelegate:(id<FSTReferenceDelegate>)referenceDelegate {
  85. _referenceDelegate = referenceDelegate;
  86. id delegate = _referenceDelegate;
  87. if ([delegate conformsToProtocol:@protocol(FSTTransactional)]) {
  88. _transactionRunner.SetBackingPersistence((id<FSTTransactional>)_referenceDelegate);
  89. }
  90. }
  91. - (Status)start {
  92. // No durable state to read on startup.
  93. HARD_ASSERT(!self.isStarted, "FSTMemoryPersistence double-started!");
  94. self.started = YES;
  95. return Status::OK();
  96. }
  97. - (void)shutdown {
  98. // No durable state to ensure is closed on shutdown.
  99. HARD_ASSERT(self.isStarted, "FSTMemoryPersistence shutdown without start!");
  100. self.started = NO;
  101. }
  102. - (id<FSTReferenceDelegate>)referenceDelegate {
  103. return _referenceDelegate;
  104. }
  105. - (ListenSequenceNumber)currentSequenceNumber {
  106. return [_referenceDelegate currentSequenceNumber];
  107. }
  108. - (const FSTTransactionRunner &)run {
  109. return _transactionRunner;
  110. }
  111. - (id<FSTMutationQueue>)mutationQueueForUser:(const User &)user {
  112. id<FSTMutationQueue> queue = _mutationQueues[user];
  113. if (!queue) {
  114. queue = [[FSTMemoryMutationQueue alloc] initWithPersistence:self];
  115. _mutationQueues[user] = queue;
  116. }
  117. return queue;
  118. }
  119. - (FSTMemoryQueryCache *)queryCache {
  120. return _queryCache;
  121. }
  122. - (id<FSTRemoteDocumentCache>)remoteDocumentCache {
  123. return _remoteDocumentCache;
  124. }
  125. @end
  126. @implementation FSTMemoryLRUReferenceDelegate {
  127. // This delegate should have the same lifetime as the persistence layer, but mark as
  128. // weak to avoid retain cycle.
  129. __weak FSTMemoryPersistence *_persistence;
  130. // Tracks sequence numbers of when documents are used. Equivalent to sentinel rows in
  131. // the leveldb implementation.
  132. std::unordered_map<DocumentKey, ListenSequenceNumber, DocumentKeyHash> _sequenceNumbers;
  133. FSTReferenceSet *_additionalReferences;
  134. FSTLRUGarbageCollector *_gc;
  135. FSTListenSequence *_listenSequence;
  136. ListenSequenceNumber _currentSequenceNumber;
  137. FSTLocalSerializer *_serializer;
  138. }
  139. - (instancetype)initWithPersistence:(FSTMemoryPersistence *)persistence
  140. serializer:(FSTLocalSerializer *)serializer
  141. lruParams:(firebase::firestore::local::LruParams)lruParams {
  142. if (self = [super init]) {
  143. _persistence = persistence;
  144. _gc = [[FSTLRUGarbageCollector alloc] initWithDelegate:self params:lruParams];
  145. _currentSequenceNumber = kFSTListenSequenceNumberInvalid;
  146. // Theoretically this is always 0, since this is all in-memory...
  147. ListenSequenceNumber highestSequenceNumber =
  148. _persistence.queryCache.highestListenSequenceNumber;
  149. _listenSequence = [[FSTListenSequence alloc] initStartingAfter:highestSequenceNumber];
  150. _serializer = serializer;
  151. }
  152. return self;
  153. }
  154. - (FSTLRUGarbageCollector *)gc {
  155. return _gc;
  156. }
  157. - (ListenSequenceNumber)currentSequenceNumber {
  158. HARD_ASSERT(_currentSequenceNumber != kFSTListenSequenceNumberInvalid,
  159. "Asking for a sequence number outside of a transaction");
  160. return _currentSequenceNumber;
  161. }
  162. - (void)addInMemoryPins:(FSTReferenceSet *)set {
  163. // Technically can't assert this, due to restartWithNoopGarbageCollector (for now...)
  164. // FSTAssert(_additionalReferences == nil, @"Overwriting additional references");
  165. _additionalReferences = set;
  166. }
  167. - (void)removeTarget:(FSTQueryData *)queryData {
  168. FSTQueryData *updated = [queryData queryDataByReplacingSnapshotVersion:queryData.snapshotVersion
  169. resumeToken:queryData.resumeToken
  170. sequenceNumber:_currentSequenceNumber];
  171. [_persistence.queryCache updateQueryData:updated];
  172. }
  173. - (void)limboDocumentUpdated:(const DocumentKey &)key {
  174. _sequenceNumbers[key] = self.currentSequenceNumber;
  175. }
  176. - (void)startTransaction:(absl::string_view)label {
  177. _currentSequenceNumber = [_listenSequence next];
  178. }
  179. - (void)commitTransaction {
  180. _currentSequenceNumber = kFSTListenSequenceNumberInvalid;
  181. }
  182. - (void)enumerateTargetsUsingBlock:(void (^)(FSTQueryData *queryData, BOOL *stop))block {
  183. return [_persistence.queryCache enumerateTargetsUsingBlock:block];
  184. }
  185. - (void)enumerateMutationsUsingBlock:
  186. (void (^)(const DocumentKey &key, ListenSequenceNumber sequenceNumber, BOOL *stop))block {
  187. BOOL stop = NO;
  188. for (const auto &entry : _sequenceNumbers) {
  189. ListenSequenceNumber sequenceNumber = entry.second;
  190. const DocumentKey &key = entry.first;
  191. // Pass in the exact sequence number as the upper bound so we know it won't be pinned by being
  192. // too recent.
  193. if (![self isPinnedAtSequenceNumber:sequenceNumber document:key]) {
  194. block(key, sequenceNumber, &stop);
  195. }
  196. }
  197. }
  198. - (int)removeTargetsThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber
  199. liveQueries:(NSDictionary<NSNumber *, FSTQueryData *> *)liveQueries {
  200. return [_persistence.queryCache removeQueriesThroughSequenceNumber:sequenceNumber
  201. liveQueries:liveQueries];
  202. }
  203. - (int32_t)sequenceNumberCount {
  204. __block int32_t totalCount = [_persistence.queryCache count];
  205. [self enumerateMutationsUsingBlock:^(const DocumentKey &key, ListenSequenceNumber sequenceNumber,
  206. BOOL *stop) {
  207. totalCount++;
  208. }];
  209. return totalCount;
  210. }
  211. - (int)removeOrphanedDocumentsThroughSequenceNumber:(ListenSequenceNumber)upperBound {
  212. std::vector<DocumentKey> removed =
  213. [(FSTMemoryRemoteDocumentCache *)_persistence.remoteDocumentCache
  214. removeOrphanedDocuments:self
  215. throughSequenceNumber:upperBound];
  216. for (const auto &key : removed) {
  217. _sequenceNumbers.erase(key);
  218. }
  219. return static_cast<int>(removed.size());
  220. }
  221. - (void)addReference:(const DocumentKey &)key {
  222. _sequenceNumbers[key] = self.currentSequenceNumber;
  223. }
  224. - (void)removeReference:(const DocumentKey &)key {
  225. _sequenceNumbers[key] = self.currentSequenceNumber;
  226. }
  227. - (BOOL)mutationQueuesContainKey:(const DocumentKey &)key {
  228. const MutationQueues &queues = [_persistence mutationQueues];
  229. for (const auto &entry : queues) {
  230. if ([entry.second containsKey:key]) {
  231. return YES;
  232. }
  233. }
  234. return NO;
  235. }
  236. - (void)removeMutationReference:(const DocumentKey &)key {
  237. _sequenceNumbers[key] = self.currentSequenceNumber;
  238. }
  239. - (BOOL)isPinnedAtSequenceNumber:(ListenSequenceNumber)upperBound
  240. document:(const DocumentKey &)key {
  241. if ([self mutationQueuesContainKey:key]) {
  242. return YES;
  243. }
  244. if ([_additionalReferences containsKey:key]) {
  245. return YES;
  246. }
  247. if ([_persistence.queryCache containsKey:key]) {
  248. return YES;
  249. }
  250. auto it = _sequenceNumbers.find(key);
  251. if (it != _sequenceNumbers.end() && it->second > upperBound) {
  252. return YES;
  253. }
  254. return NO;
  255. }
  256. - (size_t)byteSize {
  257. // Note that this method is only used for testing because this delegate is only
  258. // used for testing. The algorithm here (loop through everything, serialize it
  259. // and count bytes) is inefficient and inexact, but won't run in production.
  260. size_t count = 0;
  261. count += [_persistence.queryCache byteSizeWithSerializer:_serializer];
  262. count += [_persistence.remoteDocumentCache byteSizeWithSerializer:_serializer];
  263. const MutationQueues &queues = [_persistence mutationQueues];
  264. for (const auto &entry : queues) {
  265. count += [entry.second byteSizeWithSerializer:_serializer];
  266. }
  267. return count;
  268. }
  269. @end
  270. @implementation FSTMemoryEagerReferenceDelegate {
  271. std::unique_ptr<std::unordered_set<DocumentKey, DocumentKeyHash>> _orphaned;
  272. // This delegate should have the same lifetime as the persistence layer, but mark as
  273. // weak to avoid retain cycle.
  274. __weak FSTMemoryPersistence *_persistence;
  275. FSTReferenceSet *_additionalReferences;
  276. }
  277. - (instancetype)initWithPersistence:(FSTMemoryPersistence *)persistence {
  278. if (self = [super init]) {
  279. _persistence = persistence;
  280. }
  281. return self;
  282. }
  283. - (ListenSequenceNumber)currentSequenceNumber {
  284. return kFSTListenSequenceNumberInvalid;
  285. }
  286. - (void)addInMemoryPins:(FSTReferenceSet *)set {
  287. // We should be able to assert that _additionalReferences is nil, but due to restarts in spec
  288. // tests it would fail.
  289. _additionalReferences = set;
  290. }
  291. - (void)removeTarget:(FSTQueryData *)queryData {
  292. for (const DocumentKey &docKey :
  293. [_persistence.queryCache matchingKeysForTargetID:queryData.targetID]) {
  294. _orphaned->insert(docKey);
  295. }
  296. [_persistence.queryCache removeQueryData:queryData];
  297. }
  298. - (void)addReference:(const DocumentKey &)key {
  299. _orphaned->erase(key);
  300. }
  301. - (void)removeReference:(const DocumentKey &)key {
  302. _orphaned->insert(key);
  303. }
  304. - (void)removeMutationReference:(const DocumentKey &)key {
  305. _orphaned->insert(key);
  306. }
  307. - (BOOL)isReferenced:(const DocumentKey &)key {
  308. if ([[_persistence queryCache] containsKey:key]) {
  309. return YES;
  310. }
  311. if ([self mutationQueuesContainKey:key]) {
  312. return YES;
  313. }
  314. if ([_additionalReferences containsKey:key]) {
  315. return YES;
  316. }
  317. return NO;
  318. }
  319. - (void)limboDocumentUpdated:(const DocumentKey &)key {
  320. if ([self isReferenced:key]) {
  321. _orphaned->erase(key);
  322. } else {
  323. _orphaned->insert(key);
  324. }
  325. }
  326. - (void)startTransaction:(__unused absl::string_view)label {
  327. _orphaned = absl::make_unique<std::unordered_set<DocumentKey, DocumentKeyHash>>();
  328. }
  329. - (BOOL)mutationQueuesContainKey:(const DocumentKey &)key {
  330. const MutationQueues &queues = [_persistence mutationQueues];
  331. for (const auto &entry : queues) {
  332. if ([entry.second containsKey:key]) {
  333. return YES;
  334. }
  335. }
  336. return NO;
  337. }
  338. - (void)commitTransaction {
  339. for (const auto &key : *_orphaned) {
  340. if (![self isReferenced:key]) {
  341. [[_persistence remoteDocumentCache] removeEntryForKey:key];
  342. }
  343. }
  344. _orphaned.reset();
  345. }
  346. @end
  347. NS_ASSUME_NONNULL_END