FSyncPoint.m 11 KB

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