FSTRemoteEvent.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  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/Remote/FSTRemoteEvent.h"
  17. #import "Firestore/Source/Core/FSTSnapshotVersion.h"
  18. #import "Firestore/Source/Model/FSTDocument.h"
  19. #import "Firestore/Source/Model/FSTDocumentKey.h"
  20. #import "Firestore/Source/Remote/FSTWatchChange.h"
  21. #import "Firestore/Source/Util/FSTAssert.h"
  22. #import "Firestore/Source/Util/FSTClasses.h"
  23. #import "Firestore/Source/Util/FSTLogger.h"
  24. NS_ASSUME_NONNULL_BEGIN
  25. #pragma mark - FSTTargetMapping
  26. @interface FSTTargetMapping ()
  27. /** Private mutator method to add a document key to the mapping */
  28. - (void)addDocumentKey:(FSTDocumentKey *)documentKey;
  29. /** Private mutator method to remove a document key from the mapping */
  30. - (void)removeDocumentKey:(FSTDocumentKey *)documentKey;
  31. @end
  32. @implementation FSTTargetMapping
  33. - (void)addDocumentKey:(FSTDocumentKey *)documentKey {
  34. @throw FSTAbstractMethodException(); // NOLINT
  35. }
  36. - (void)removeDocumentKey:(FSTDocumentKey *)documentKey {
  37. @throw FSTAbstractMethodException(); // NOLINT
  38. }
  39. @end
  40. #pragma mark - FSTResetMapping
  41. @interface FSTResetMapping ()
  42. @property(nonatomic, strong) FSTDocumentKeySet *documents;
  43. @end
  44. @implementation FSTResetMapping
  45. + (instancetype)mappingWithDocuments:(NSArray<FSTDocument *> *)documents {
  46. FSTResetMapping *mapping = [[FSTResetMapping alloc] init];
  47. for (FSTDocument *doc in documents) {
  48. mapping.documents = [mapping.documents setByAddingObject:doc.key];
  49. }
  50. return mapping;
  51. }
  52. - (instancetype)init {
  53. self = [super init];
  54. if (self) {
  55. _documents = [FSTDocumentKeySet keySet];
  56. }
  57. return self;
  58. }
  59. - (BOOL)isEqual:(id)other {
  60. if (other == self) {
  61. return YES;
  62. }
  63. if (![other isMemberOfClass:[FSTResetMapping class]]) {
  64. return NO;
  65. }
  66. FSTResetMapping *otherMapping = (FSTResetMapping *)other;
  67. return [self.documents isEqual:otherMapping.documents];
  68. }
  69. - (NSUInteger)hash {
  70. return self.documents.hash;
  71. }
  72. - (void)addDocumentKey:(FSTDocumentKey *)documentKey {
  73. self.documents = [self.documents setByAddingObject:documentKey];
  74. }
  75. - (void)removeDocumentKey:(FSTDocumentKey *)documentKey {
  76. self.documents = [self.documents setByRemovingObject:documentKey];
  77. }
  78. @end
  79. #pragma mark - FSTUpdateMapping
  80. @interface FSTUpdateMapping ()
  81. @property(nonatomic, strong) FSTDocumentKeySet *addedDocuments;
  82. @property(nonatomic, strong) FSTDocumentKeySet *removedDocuments;
  83. @end
  84. @implementation FSTUpdateMapping
  85. + (FSTUpdateMapping *)mappingWithAddedDocuments:(NSArray<FSTDocument *> *)added
  86. removedDocuments:(NSArray<FSTDocument *> *)removed {
  87. FSTUpdateMapping *mapping = [[FSTUpdateMapping alloc] init];
  88. for (FSTDocument *doc in added) {
  89. mapping.addedDocuments = [mapping.addedDocuments setByAddingObject:doc.key];
  90. }
  91. for (FSTDocument *doc in removed) {
  92. mapping.removedDocuments = [mapping.removedDocuments setByAddingObject:doc.key];
  93. }
  94. return mapping;
  95. }
  96. - (instancetype)init {
  97. self = [super init];
  98. if (self) {
  99. _addedDocuments = [FSTDocumentKeySet keySet];
  100. _removedDocuments = [FSTDocumentKeySet keySet];
  101. }
  102. return self;
  103. }
  104. - (BOOL)isEqual:(id)other {
  105. if (other == self) {
  106. return YES;
  107. }
  108. if (![other isMemberOfClass:[FSTUpdateMapping class]]) {
  109. return NO;
  110. }
  111. FSTUpdateMapping *otherMapping = (FSTUpdateMapping *)other;
  112. return [self.addedDocuments isEqual:otherMapping.addedDocuments] &&
  113. [self.removedDocuments isEqual:otherMapping.removedDocuments];
  114. }
  115. - (NSUInteger)hash {
  116. return self.addedDocuments.hash * 31 + self.removedDocuments.hash;
  117. }
  118. - (FSTDocumentKeySet *)applyTo:(FSTDocumentKeySet *)keys {
  119. __block FSTDocumentKeySet *result = keys;
  120. [self.addedDocuments enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
  121. result = [result setByAddingObject:key];
  122. }];
  123. [self.removedDocuments enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) {
  124. result = [result setByRemovingObject:key];
  125. }];
  126. return result;
  127. }
  128. - (void)addDocumentKey:(FSTDocumentKey *)documentKey {
  129. self.addedDocuments = [self.addedDocuments setByAddingObject:documentKey];
  130. self.removedDocuments = [self.removedDocuments setByRemovingObject:documentKey];
  131. }
  132. - (void)removeDocumentKey:(FSTDocumentKey *)documentKey {
  133. self.addedDocuments = [self.addedDocuments setByRemovingObject:documentKey];
  134. self.removedDocuments = [self.removedDocuments setByAddingObject:documentKey];
  135. }
  136. @end
  137. #pragma mark - FSTTargetChange
  138. @interface FSTTargetChange ()
  139. @property(nonatomic, assign) FSTCurrentStatusUpdate currentStatusUpdate;
  140. @property(nonatomic, strong, nullable) FSTTargetMapping *mapping;
  141. @property(nonatomic, strong) FSTSnapshotVersion *snapshotVersion;
  142. @property(nonatomic, strong) NSData *resumeToken;
  143. @end
  144. @implementation FSTTargetChange
  145. - (instancetype)init {
  146. if (self = [super init]) {
  147. _currentStatusUpdate = FSTCurrentStatusUpdateNone;
  148. _resumeToken = [NSData data];
  149. }
  150. return self;
  151. }
  152. + (instancetype)changeWithDocuments:(NSArray<FSTMaybeDocument *> *)docs
  153. currentStatusUpdate:(FSTCurrentStatusUpdate)currentStatusUpdate {
  154. FSTUpdateMapping *mapping = [[FSTUpdateMapping alloc] init];
  155. for (FSTMaybeDocument *doc in docs) {
  156. if ([doc isKindOfClass:[FSTDeletedDocument class]]) {
  157. mapping.removedDocuments = [mapping.removedDocuments setByAddingObject:doc.key];
  158. } else {
  159. mapping.addedDocuments = [mapping.addedDocuments setByAddingObject:doc.key];
  160. }
  161. }
  162. FSTTargetChange *change = [[FSTTargetChange alloc] init];
  163. change.mapping = mapping;
  164. change.currentStatusUpdate = currentStatusUpdate;
  165. return change;
  166. }
  167. + (instancetype)changeWithMapping:(FSTTargetMapping *)mapping
  168. snapshotVersion:(FSTSnapshotVersion *)snapshotVersion
  169. currentStatusUpdate:(FSTCurrentStatusUpdate)currentStatusUpdate {
  170. FSTTargetChange *change = [[FSTTargetChange alloc] init];
  171. change.mapping = mapping;
  172. change.snapshotVersion = snapshotVersion;
  173. change.currentStatusUpdate = currentStatusUpdate;
  174. return change;
  175. }
  176. - (FSTTargetMapping *)mapping {
  177. if (!_mapping) {
  178. // Create an FSTUpdateMapping by default, since resets are always explicit
  179. _mapping = [[FSTUpdateMapping alloc] init];
  180. }
  181. return _mapping;
  182. }
  183. /**
  184. * Sets the resume token but only when it has a new value. Empty resumeTokens are
  185. * discarded.
  186. */
  187. - (void)setResumeToken:(NSData *)resumeToken {
  188. if (resumeToken.length > 0) {
  189. _resumeToken = resumeToken;
  190. }
  191. }
  192. @end
  193. #pragma mark - FSTRemoteEvent
  194. @interface FSTRemoteEvent () {
  195. NSMutableDictionary<FSTDocumentKey *, FSTMaybeDocument *> *_documentUpdates;
  196. NSMutableDictionary<FSTBoxedTargetID *, FSTTargetChange *> *_targetChanges;
  197. }
  198. - (instancetype)
  199. initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
  200. targetChanges:(NSMutableDictionary<FSTBoxedTargetID *, FSTTargetChange *> *)targetChanges
  201. documentUpdates:
  202. (NSMutableDictionary<FSTDocumentKey *, FSTMaybeDocument *> *)documentUpdates;
  203. @property(nonatomic, strong) FSTSnapshotVersion *snapshotVersion;
  204. @end
  205. @implementation FSTRemoteEvent
  206. + (instancetype)
  207. eventWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
  208. targetChanges:(NSMutableDictionary<NSNumber *, FSTTargetChange *> *)targetChanges
  209. documentUpdates:
  210. (NSMutableDictionary<FSTDocumentKey *, FSTMaybeDocument *> *)documentUpdates {
  211. return [[FSTRemoteEvent alloc] initWithSnapshotVersion:snapshotVersion
  212. targetChanges:targetChanges
  213. documentUpdates:documentUpdates];
  214. }
  215. - (instancetype)
  216. initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
  217. targetChanges:(NSMutableDictionary<NSNumber *, FSTTargetChange *> *)targetChanges
  218. documentUpdates:
  219. (NSMutableDictionary<FSTDocumentKey *, FSTMaybeDocument *> *)documentUpdates {
  220. self = [super init];
  221. if (self) {
  222. _snapshotVersion = snapshotVersion;
  223. _targetChanges = targetChanges;
  224. _documentUpdates = documentUpdates;
  225. }
  226. return self;
  227. }
  228. /** Adds a document update to this remote event */
  229. - (void)addDocumentUpdate:(FSTMaybeDocument *)document {
  230. _documentUpdates[document.key] = document;
  231. }
  232. /** Handles an existence filter mismatch */
  233. - (void)handleExistenceFilterMismatchForTargetID:(FSTBoxedTargetID *)targetID {
  234. // An existence filter mismatch will reset the query and we need to reset the mapping to contain
  235. // no documents and an empty resume token.
  236. //
  237. // Note:
  238. // * The reset mapping is empty, specifically forcing the consumer of the change to
  239. // forget all keys for this targetID;
  240. // * The resume snapshot for this target must be reset
  241. // * The target must be unacked because unwatching and rewatching introduces a race for
  242. // changes.
  243. //
  244. // TODO(dimond): keep track of reset targets not to raise.
  245. FSTTargetChange *targetChange =
  246. [FSTTargetChange changeWithMapping:[[FSTResetMapping alloc] init]
  247. snapshotVersion:[FSTSnapshotVersion noVersion]
  248. currentStatusUpdate:FSTCurrentStatusUpdateMarkNotCurrent];
  249. _targetChanges[targetID] = targetChange;
  250. }
  251. @end
  252. #pragma mark - FSTWatchChangeAggregator
  253. @interface FSTWatchChangeAggregator ()
  254. /** The snapshot version for every target change this creates. */
  255. @property(nonatomic, strong, readonly) FSTSnapshotVersion *snapshotVersion;
  256. /** Keeps track of the current target mappings */
  257. @property(nonatomic, strong, readonly)
  258. NSMutableDictionary<FSTBoxedTargetID *, FSTTargetChange *> *targetChanges;
  259. /** Keeps track of document to update */
  260. @property(nonatomic, strong, readonly)
  261. NSMutableDictionary<FSTDocumentKey *, FSTMaybeDocument *> *documentUpdates;
  262. /** The set of open listens on the client */
  263. @property(nonatomic, strong, readonly)
  264. NSDictionary<FSTBoxedTargetID *, FSTQueryData *> *listenTargets;
  265. /** Whether this aggregator was frozen and can no longer be modified */
  266. @property(nonatomic, assign) BOOL frozen;
  267. @end
  268. @implementation FSTWatchChangeAggregator {
  269. NSMutableDictionary<FSTBoxedTargetID *, FSTExistenceFilter *> *_existenceFilters;
  270. }
  271. - (instancetype)
  272. initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
  273. listenTargets:(NSDictionary<FSTBoxedTargetID *, FSTQueryData *> *)listenTargets
  274. pendingTargetResponses:(NSDictionary<FSTBoxedTargetID *, NSNumber *> *)pendingTargetResponses {
  275. self = [super init];
  276. if (self) {
  277. _snapshotVersion = snapshotVersion;
  278. _frozen = NO;
  279. _targetChanges = [NSMutableDictionary dictionary];
  280. _listenTargets = listenTargets;
  281. _pendingTargetResponses = [NSMutableDictionary dictionaryWithDictionary:pendingTargetResponses];
  282. _existenceFilters = [NSMutableDictionary dictionary];
  283. _documentUpdates = [NSMutableDictionary dictionary];
  284. }
  285. return self;
  286. }
  287. - (FSTTargetChange *)targetChangeForTargetID:(FSTBoxedTargetID *)targetID {
  288. FSTTargetChange *change = self.targetChanges[targetID];
  289. if (!change) {
  290. change = [[FSTTargetChange alloc] init];
  291. change.snapshotVersion = self.snapshotVersion;
  292. self.targetChanges[targetID] = change;
  293. }
  294. return change;
  295. }
  296. - (void)addWatchChanges:(NSArray<FSTWatchChange *> *)watchChanges {
  297. FSTAssert(!self.frozen, @"Trying to modify frozen FSTWatchChangeAggregator");
  298. for (FSTWatchChange *watchChange in watchChanges) {
  299. [self addWatchChange:watchChange];
  300. }
  301. }
  302. - (void)addWatchChange:(FSTWatchChange *)watchChange {
  303. FSTAssert(!self.frozen, @"Trying to modify frozen FSTWatchChangeAggregator");
  304. if ([watchChange isKindOfClass:[FSTDocumentWatchChange class]]) {
  305. [self addDocumentChange:(FSTDocumentWatchChange *)watchChange];
  306. } else if ([watchChange isKindOfClass:[FSTWatchTargetChange class]]) {
  307. [self addTargetChange:(FSTWatchTargetChange *)watchChange];
  308. } else if ([watchChange isKindOfClass:[FSTExistenceFilterWatchChange class]]) {
  309. [self addExistenceFilterChange:(FSTExistenceFilterWatchChange *)watchChange];
  310. } else {
  311. FSTFail(@"Unknown watch change: %@", watchChange);
  312. }
  313. }
  314. - (void)addDocumentChange:(FSTDocumentWatchChange *)docChange {
  315. BOOL relevant = NO;
  316. for (FSTBoxedTargetID *targetID in docChange.updatedTargetIDs) {
  317. if ([self isActiveTarget:targetID]) {
  318. FSTTargetChange *change = [self targetChangeForTargetID:targetID];
  319. [change.mapping addDocumentKey:docChange.documentKey];
  320. relevant = YES;
  321. }
  322. }
  323. for (FSTBoxedTargetID *targetID in docChange.removedTargetIDs) {
  324. if ([self isActiveTarget:targetID]) {
  325. FSTTargetChange *change = [self targetChangeForTargetID:targetID];
  326. [change.mapping removeDocumentKey:docChange.documentKey];
  327. relevant = YES;
  328. }
  329. }
  330. // Only update the document if there is a new document to replace, this might be just a target
  331. // update instead.
  332. if (docChange.document && relevant) {
  333. self.documentUpdates[docChange.documentKey] = docChange.document;
  334. }
  335. }
  336. - (void)addTargetChange:(FSTWatchTargetChange *)targetChange {
  337. for (FSTBoxedTargetID *targetID in targetChange.targetIDs) {
  338. FSTTargetChange *change = [self targetChangeForTargetID:targetID];
  339. switch (targetChange.state) {
  340. case FSTWatchTargetChangeStateNoChange:
  341. if ([self isActiveTarget:targetID]) {
  342. // Creating the change above satisfies the semantics of no-change.
  343. change.resumeToken = targetChange.resumeToken;
  344. }
  345. break;
  346. case FSTWatchTargetChangeStateAdded:
  347. [self recordResponseForTargetID:targetID];
  348. if (![self.pendingTargetResponses objectForKey:targetID]) {
  349. // We have a freshly added target, so we need to reset any state that we had previously
  350. // This can happen e.g. when remove and add back a target for existence filter
  351. // mismatches.
  352. change.mapping = nil;
  353. change.currentStatusUpdate = FSTCurrentStatusUpdateNone;
  354. [_existenceFilters removeObjectForKey:targetID];
  355. }
  356. change.resumeToken = targetChange.resumeToken;
  357. break;
  358. case FSTWatchTargetChangeStateRemoved:
  359. // We need to keep track of removed targets to we can post-filter and remove any target
  360. // changes.
  361. [self recordResponseForTargetID:targetID];
  362. FSTAssert(!targetChange.cause, @"WatchChangeAggregator does not handle errored targets.");
  363. break;
  364. case FSTWatchTargetChangeStateCurrent:
  365. if ([self isActiveTarget:targetID]) {
  366. change.currentStatusUpdate = FSTCurrentStatusUpdateMarkCurrent;
  367. change.resumeToken = targetChange.resumeToken;
  368. }
  369. break;
  370. case FSTWatchTargetChangeStateReset:
  371. if ([self isActiveTarget:targetID]) {
  372. // Overwrite any existing target mapping with a reset mapping. Every subsequent update
  373. // will modify the reset mapping, not an update mapping.
  374. change.mapping = [[FSTResetMapping alloc] init];
  375. change.resumeToken = targetChange.resumeToken;
  376. }
  377. break;
  378. default:
  379. FSTWarn(@"Unknown target watch change type: %ld", (long)targetChange.state);
  380. }
  381. }
  382. }
  383. /**
  384. * Records that we got a watch target add/remove by decrementing the number of pending target
  385. * responses that we have.
  386. */
  387. - (void)recordResponseForTargetID:(FSTBoxedTargetID *)targetID {
  388. NSNumber *count = [self.pendingTargetResponses objectForKey:targetID];
  389. int newCount = count ? [count intValue] - 1 : -1;
  390. if (newCount == 0) {
  391. [self.pendingTargetResponses removeObjectForKey:targetID];
  392. } else {
  393. [self.pendingTargetResponses setObject:[NSNumber numberWithInt:newCount] forKey:targetID];
  394. }
  395. }
  396. /**
  397. * Returns true if the given targetId is active. Active targets are those for which there are no
  398. * pending requests to add a listen and are in the current list of targets the client cares about.
  399. *
  400. * Clients can repeatedly listen and stop listening to targets, so this check is useful in
  401. * preventing in preventing race conditions for a target where events arrive but the server hasn't
  402. * yet acknowledged the intended change in state.
  403. */
  404. - (BOOL)isActiveTarget:(FSTBoxedTargetID *)targetID {
  405. return [self.listenTargets objectForKey:targetID] &&
  406. ![self.pendingTargetResponses objectForKey:targetID];
  407. }
  408. - (void)addExistenceFilterChange:(FSTExistenceFilterWatchChange *)existenceFilterChange {
  409. FSTBoxedTargetID *targetID = @(existenceFilterChange.targetID);
  410. if ([self isActiveTarget:targetID]) {
  411. _existenceFilters[targetID] = existenceFilterChange.filter;
  412. }
  413. }
  414. - (FSTRemoteEvent *)remoteEvent {
  415. NSMutableDictionary<FSTBoxedTargetID *, FSTTargetChange *> *targetChanges = self.targetChanges;
  416. NSMutableArray *targetsToRemove = [NSMutableArray array];
  417. // Apply any inactive targets.
  418. for (FSTBoxedTargetID *targetID in [targetChanges keyEnumerator]) {
  419. if (![self isActiveTarget:targetID]) {
  420. [targetsToRemove addObject:targetID];
  421. }
  422. }
  423. [targetChanges removeObjectsForKeys:targetsToRemove];
  424. // Mark this aggregator as frozen so no further modifications are made.
  425. self.frozen = YES;
  426. return [FSTRemoteEvent eventWithSnapshotVersion:self.snapshotVersion
  427. targetChanges:targetChanges
  428. documentUpdates:self.documentUpdates];
  429. }
  430. @end
  431. NS_ASSUME_NONNULL_END