FSyncPoint.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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 "FSyncPoint.h"
  17. #import "FOperation.h"
  18. #import "FWriteTreeRef.h"
  19. #import "FNode.h"
  20. #import "FEventRegistration.h"
  21. #import "FIRDatabaseQuery.h"
  22. #import "FChildrenNode.h"
  23. #import "FTupleRemovedQueriesEvents.h"
  24. #import "FView.h"
  25. #import "FOperationSource.h"
  26. #import "FQuerySpec.h"
  27. #import "FQueryParams.h"
  28. #import "FPath.h"
  29. #import "FEmptyNode.h"
  30. #import "FViewCache.h"
  31. #import "FCacheNode.h"
  32. #import "FPersistenceManager.h"
  33. #import "FDataEvent.h"
  34. /**
  35. * SyncPoint represents a single location in a SyncTree with 1 or more event registrations, meaning we need to
  36. * maintain 1 or more Views at this location to cache server data and raise appropriate events for server changes
  37. * and user writes (set, transaction, update).
  38. *
  39. * It's responsible for:
  40. * - Maintaining the set of 1 or more views necessary at this location (a SyncPoint with 0 views should be removed).
  41. * - Proxying user / server operations to the views as appropriate (i.e. applyServerOverwrite,
  42. * applyUserOverwrite, etc.)
  43. */
  44. @interface FSyncPoint ()
  45. /**
  46. * The Views being tracked at this location in the tree, stored as a map where the key is a
  47. * queryParams and the value is the View for that query.
  48. *
  49. * NOTE: This list will be quite small (usually 1, but perhaps 2 or 3; any more is an odd use case).
  50. *
  51. * Maps NSString -> FView
  52. */
  53. @property (nonatomic, strong) NSMutableDictionary *views;
  54. @property (nonatomic, strong) FPersistenceManager *persistenceManager;
  55. @end
  56. @implementation FSyncPoint
  57. - (id) initWithPersistenceManager:(FPersistenceManager *)persistence {
  58. self = [super init];
  59. if (self) {
  60. self.persistenceManager = persistence;
  61. self.views = [[NSMutableDictionary alloc] init];
  62. }
  63. return self;
  64. }
  65. - (BOOL) isEmpty {
  66. return [self.views count] == 0;
  67. }
  68. - (NSArray *) applyOperation:(id<FOperation>)operation
  69. toView:(FView *)view
  70. writesCache:(FWriteTreeRef *)writesCache
  71. serverCache:(id<FNode>)optCompleteServerCache {
  72. FViewOperationResult *result = [view applyOperation:operation writesCache:writesCache serverCache:optCompleteServerCache];
  73. if (!view.query.loadsAllData) {
  74. NSMutableSet *removed = [NSMutableSet set];
  75. NSMutableSet *added = [NSMutableSet set];
  76. [result.changes enumerateObjectsUsingBlock:^(FChange *change, NSUInteger idx, BOOL *stop) {
  77. if (change.type == FIRDataEventTypeChildAdded) {
  78. [added addObject:change.childKey];
  79. } else if (change.type == FIRDataEventTypeChildRemoved) {
  80. [removed addObject:change.childKey];
  81. }
  82. }];
  83. if ([removed count] > 0 || [added count] > 0) {
  84. [self.persistenceManager updateTrackedQueryKeysWithAddedKeys:added removedKeys:removed forQuery:view.query];
  85. }
  86. }
  87. return result.events;
  88. }
  89. - (NSArray *) applyOperation:(id <FOperation>)operation writesCache:(FWriteTreeRef *)writesCache serverCache:(id <FNode>)optCompleteServerCache {
  90. FQueryParams *queryParams = operation.source.queryParams;
  91. if (queryParams != nil) {
  92. FView *view = [self.views objectForKey:queryParams];
  93. NSAssert(view != nil, @"SyncTree gave us an op for an invalid query.");
  94. return [self applyOperation:operation toView:view writesCache:writesCache serverCache:optCompleteServerCache];
  95. } else {
  96. NSMutableArray *events = [[NSMutableArray alloc] init];
  97. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key, FView *view, BOOL *stop) {
  98. NSArray *eventsForView = [self applyOperation:operation toView:view writesCache:writesCache serverCache:optCompleteServerCache];
  99. [events addObjectsFromArray:eventsForView];
  100. }];
  101. return events;
  102. }
  103. }
  104. /**
  105. * Add an event callback for the specified query
  106. * Returns Array of FEvent events to raise.
  107. */
  108. - (NSArray *) addEventRegistration:(id <FEventRegistration>)eventRegistration
  109. forNonExistingViewForQuery:(FQuerySpec *)query
  110. writesCache:(FWriteTreeRef *)writesCache
  111. serverCache:(FCacheNode *)serverCache {
  112. NSAssert(self.views[query.params] == nil, @"Found view for query: %@", query.params);
  113. // TODO: make writesCache take flag for complete server node
  114. id<FNode> eventCache = [writesCache calculateCompleteEventCacheWithCompleteServerCache:serverCache.isFullyInitialized ? serverCache.node : nil];
  115. BOOL eventCacheComplete;
  116. if (eventCache != nil) {
  117. eventCacheComplete = YES;
  118. } else {
  119. eventCache = [writesCache calculateCompleteEventChildrenWithCompleteServerChildren:serverCache.node];
  120. eventCacheComplete = NO;
  121. }
  122. FIndexedNode *indexed = [FIndexedNode indexedNodeWithNode:eventCache index:query.index];
  123. FCacheNode *eventCacheNode = [[FCacheNode alloc] initWithIndexedNode:indexed
  124. isFullyInitialized:eventCacheComplete
  125. isFiltered:NO];
  126. FViewCache *viewCache = [[FViewCache alloc] initWithEventCache:eventCacheNode serverCache:serverCache];
  127. FView *view = [[FView alloc] initWithQuery:query initialViewCache:viewCache];
  128. // If this is a non-default query we need to tell persistence our current view of the data
  129. if (!query.loadsAllData) {
  130. NSMutableSet *allKeys = [NSMutableSet set];
  131. [view.eventCache enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node, BOOL *stop) {
  132. [allKeys addObject:key];
  133. }];
  134. [self.persistenceManager setTrackedQueryKeys:allKeys forQuery:query];
  135. }
  136. self.views[query.params] = view;
  137. return [self addEventRegistration:eventRegistration forExistingViewForQuery:query];
  138. }
  139. - (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
  140. forExistingViewForQuery:(FQuerySpec *)query {
  141. FView *view = self.views[query.params];
  142. NSAssert(view != nil, @"No view for query: %@", query);
  143. [view addEventRegistration:eventRegistration];
  144. return [view initialEvents:eventRegistration];
  145. }
  146. /**
  147. * Remove event callback(s). Return cancelEvents if a cancelError is specified.
  148. *
  149. * If query is the default query, we'll check all views for the specified eventRegistration.
  150. * If eventRegistration is nil, we'll remove all callbacks for the specified view(s).
  151. *
  152. * @return FTupleRemovedQueriesEvents removed queries and any cancel events
  153. */
  154. - (FTupleRemovedQueriesEvents *) removeEventRegistration:(id <FEventRegistration>)eventRegistration
  155. forQuery:(FQuerySpec *)query
  156. cancelError:(NSError *)cancelError {
  157. NSMutableArray *removedQueries = [[NSMutableArray alloc] init];
  158. __block NSMutableArray *cancelEvents = [[NSMutableArray alloc] init];
  159. BOOL hadCompleteView = [self hasCompleteView];
  160. if ([query isDefault]) {
  161. // When you do [ref removeObserverWithHandle:], we search all views for the registration to remove.
  162. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *viewQueryParams, FView *view, BOOL *stop) {
  163. [cancelEvents addObjectsFromArray:[view removeEventRegistration:eventRegistration cancelError:cancelError]];
  164. if ([view isEmpty]) {
  165. [self.views removeObjectForKey:viewQueryParams];
  166. // We'll deal with complete views later
  167. if (![view.query loadsAllData]) {
  168. [removedQueries addObject:view.query];
  169. }
  170. }
  171. }];
  172. } else {
  173. // remove the callback from the specific view
  174. FView *view = [self.views objectForKey:query.params];
  175. if (view != nil) {
  176. [cancelEvents addObjectsFromArray:[view removeEventRegistration:eventRegistration cancelError:cancelError]];
  177. if ([view isEmpty]) {
  178. [self.views removeObjectForKey:query.params];
  179. // We'll deal with complete views later
  180. if (![view.query loadsAllData]) {
  181. [removedQueries addObject:view.query];
  182. }
  183. }
  184. }
  185. }
  186. if (hadCompleteView && ![self hasCompleteView]) {
  187. // We removed our last complete view
  188. [removedQueries addObject:[FQuerySpec defaultQueryAtPath:query.path]];
  189. }
  190. return [[FTupleRemovedQueriesEvents alloc] initWithRemovedQueries:removedQueries cancelEvents:cancelEvents];
  191. }
  192. - (NSArray *) queryViews {
  193. __block NSMutableArray *filteredViews = [[NSMutableArray alloc] init];
  194. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key, FView *view, BOOL *stop) {
  195. if (![view.query loadsAllData]) {
  196. [filteredViews addObject:view];
  197. }
  198. }];
  199. return filteredViews;
  200. }
  201. - (id <FNode>) completeServerCacheAtPath:(FPath *)path {
  202. __block id<FNode> serverCache = nil;
  203. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key, FView *view, BOOL *stop) {
  204. serverCache = [view completeServerCacheFor:path];
  205. *stop = (serverCache != nil);
  206. }];
  207. return serverCache;
  208. }
  209. - (FView *) viewForQuery:(FQuerySpec *)query {
  210. return [self.views objectForKey:query.params];
  211. }
  212. - (BOOL) viewExistsForQuery:(FQuerySpec *)query {
  213. return [self viewForQuery:query] != nil;
  214. }
  215. - (BOOL) hasCompleteView {
  216. return [self completeView] != nil;
  217. }
  218. - (FView *) completeView {
  219. __block FView *completeView = nil;
  220. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key, FView *view, BOOL *stop) {
  221. if ([view.query loadsAllData]) {
  222. completeView = view;
  223. *stop = YES;
  224. }
  225. }];
  226. return completeView;
  227. }
  228. @end