FSyncPoint.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 "FirebaseDatabase/Sources/Core/FSyncPoint.h"
  17. #import "FirebaseDatabase/Sources/Core/FQueryParams.h"
  18. #import "FirebaseDatabase/Sources/Core/FQuerySpec.h"
  19. #import "FirebaseDatabase/Sources/Core/FWriteTreeRef.h"
  20. #import "FirebaseDatabase/Sources/Core/Operation/FOperation.h"
  21. #import "FirebaseDatabase/Sources/Core/Operation/FOperationSource.h"
  22. #import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
  23. #import "FirebaseDatabase/Sources/Core/View/FCacheNode.h"
  24. #import "FirebaseDatabase/Sources/Core/View/FDataEvent.h"
  25. #import "FirebaseDatabase/Sources/Core/View/FEventRegistration.h"
  26. #import "FirebaseDatabase/Sources/Core/View/FView.h"
  27. #import "FirebaseDatabase/Sources/Core/View/FViewCache.h"
  28. #import "FirebaseDatabase/Sources/Persistence/FPersistenceManager.h"
  29. #import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseQuery.h"
  30. #import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
  31. #import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
  32. #import "FirebaseDatabase/Sources/Snapshot/FNode.h"
  33. #import "FirebaseDatabase/Sources/Utilities/Tuples/FTupleRemovedQueriesEvents.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. - (FView *)getView:(FQuerySpec *)query
  123. writesCache:(FWriteTreeRef *)writesCache
  124. serverCache:(FCacheNode *)serverCache {
  125. FView *view = self.views[query.params];
  126. if (view != nil) {
  127. return view;
  128. }
  129. id<FNode> eventCache = [writesCache
  130. calculateCompleteEventCacheWithCompleteServerCache:
  131. serverCache.isFullyInitialized ? serverCache.node : nil];
  132. BOOL eventCacheComplete;
  133. if (eventCache != nil) {
  134. eventCacheComplete = YES;
  135. } else {
  136. eventCache = [writesCache
  137. calculateCompleteEventChildrenWithCompleteServerChildren:
  138. serverCache.node != nil ? serverCache.node
  139. : [FEmptyNode emptyNode]];
  140. eventCacheComplete = NO;
  141. }
  142. FIndexedNode *indexed = [FIndexedNode indexedNodeWithNode:eventCache
  143. index:query.index];
  144. FCacheNode *eventCacheNode =
  145. [[FCacheNode alloc] initWithIndexedNode:indexed
  146. isFullyInitialized:eventCacheComplete
  147. isFiltered:NO];
  148. FViewCache *viewCache =
  149. [[FViewCache alloc] initWithEventCache:eventCacheNode
  150. serverCache:serverCache];
  151. return [[FView alloc] initWithQuery:query initialViewCache:viewCache];
  152. }
  153. /**
  154. * Add an event callback for the specified query
  155. * Returns an array of events to raise.
  156. */
  157. - (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
  158. forNonExistingViewForQuery:(FQuerySpec *)query
  159. writesCache:(FWriteTreeRef *)writesCache
  160. serverCache:(FCacheNode *)serverCache {
  161. NSAssert(self.views[query.params] == nil, @"Found view for query: %@",
  162. query.params);
  163. // TODO: make writesCache take flag for complete server node
  164. FView *view = [self getView:query
  165. writesCache:writesCache
  166. serverCache:serverCache];
  167. // If this is a non-default query we need to tell persistence our current
  168. // view of the data
  169. if (!query.loadsAllData) {
  170. NSMutableSet *allKeys = [NSMutableSet set];
  171. [view.eventCache enumerateChildrenUsingBlock:^(
  172. NSString *key, id<FNode> node, BOOL *stop) {
  173. [allKeys addObject:key];
  174. }];
  175. [self.persistenceManager setTrackedQueryKeys:allKeys forQuery:query];
  176. }
  177. self.views[query.params] = view;
  178. return [self addEventRegistration:eventRegistration
  179. forExistingViewForQuery:query];
  180. }
  181. - (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
  182. forExistingViewForQuery:(FQuerySpec *)query {
  183. FView *view = self.views[query.params];
  184. NSAssert(view != nil, @"No view for query: %@", query);
  185. [view addEventRegistration:eventRegistration];
  186. return [view initialEvents:eventRegistration];
  187. }
  188. /**
  189. * Remove event callback(s). Return cancelEvents if a cancelError is specified.
  190. *
  191. * If query is the default query, we'll check all views for the specified
  192. * eventRegistration. If eventRegistration is nil, we'll remove all callbacks
  193. * for the specified view(s).
  194. *
  195. * @return FTupleRemovedQueriesEvents removed queries and any cancel events
  196. */
  197. - (FTupleRemovedQueriesEvents *)removeEventRegistration:
  198. (id<FEventRegistration>)eventRegistration
  199. forQuery:(FQuerySpec *)query
  200. cancelError:(NSError *)cancelError {
  201. NSMutableArray *removedQueries = [[NSMutableArray alloc] init];
  202. __block NSMutableArray *cancelEvents = [[NSMutableArray alloc] init];
  203. BOOL hadCompleteView = [self hasCompleteView];
  204. if ([query isDefault]) {
  205. // When you do [ref removeObserverWithHandle:], we search all views for
  206. // the registration to remove.
  207. [self.views enumerateKeysAndObjectsUsingBlock:^(
  208. FQueryParams *viewQueryParams, FView *view,
  209. BOOL *stop) {
  210. [cancelEvents
  211. addObjectsFromArray:[view
  212. removeEventRegistration:eventRegistration
  213. cancelError:cancelError]];
  214. if ([view isEmpty]) {
  215. [self.views removeObjectForKey:viewQueryParams];
  216. // We'll deal with complete views later
  217. if (![view.query loadsAllData]) {
  218. [removedQueries addObject:view.query];
  219. }
  220. }
  221. }];
  222. } else {
  223. // remove the callback from the specific view
  224. FView *view = [self.views objectForKey:query.params];
  225. if (view != nil) {
  226. [cancelEvents addObjectsFromArray:
  227. [view removeEventRegistration:eventRegistration
  228. cancelError:cancelError]];
  229. if ([view isEmpty]) {
  230. [self.views removeObjectForKey:query.params];
  231. // We'll deal with complete views later
  232. if (![view.query loadsAllData]) {
  233. [removedQueries addObject:view.query];
  234. }
  235. }
  236. }
  237. }
  238. if (hadCompleteView && ![self hasCompleteView]) {
  239. // We removed our last complete view
  240. [removedQueries addObject:[FQuerySpec defaultQueryAtPath:query.path]];
  241. }
  242. return [[FTupleRemovedQueriesEvents alloc]
  243. initWithRemovedQueries:removedQueries
  244. cancelEvents:cancelEvents];
  245. }
  246. - (NSArray *)queryViews {
  247. __block NSMutableArray *filteredViews = [[NSMutableArray alloc] init];
  248. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key,
  249. FView *view, BOOL *stop) {
  250. if (![view.query loadsAllData]) {
  251. [filteredViews addObject:view];
  252. }
  253. }];
  254. return filteredViews;
  255. }
  256. - (id<FNode>)completeServerCacheAtPath:(FPath *)path {
  257. __block id<FNode> serverCache = nil;
  258. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key,
  259. FView *view, BOOL *stop) {
  260. serverCache = [view completeServerCacheFor:path];
  261. *stop = (serverCache != nil);
  262. }];
  263. return serverCache;
  264. }
  265. - (id<FNode>)completeEventCacheAtPath:(FPath *)path {
  266. __block id<FNode> eventCache = nil;
  267. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key,
  268. FView *view, BOOL *stop) {
  269. eventCache = [view completeEventCacheFor:path];
  270. *stop = (eventCache != nil);
  271. }];
  272. return eventCache;
  273. }
  274. - (FView *)viewForQuery:(FQuerySpec *)query {
  275. return [self.views objectForKey:query.params];
  276. }
  277. - (BOOL)viewExistsForQuery:(FQuerySpec *)query {
  278. return [self viewForQuery:query] != nil;
  279. }
  280. - (BOOL)hasCompleteView {
  281. return [self completeView] != nil;
  282. }
  283. - (FView *)completeView {
  284. __block FView *completeView = nil;
  285. [self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key,
  286. FView *view, BOOL *stop) {
  287. if ([view.query loadsAllData]) {
  288. completeView = view;
  289. *stop = YES;
  290. }
  291. }];
  292. return completeView;
  293. }
  294. @end