FViewProcessor.m 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  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 "FViewProcessor.h"
  17. #import "FCompleteChildSource.h"
  18. #import "FWriteTreeRef.h"
  19. #import "FViewCache.h"
  20. #import "FCacheNode.h"
  21. #import "FNode.h"
  22. #import "FOperation.h"
  23. #import "FOperationSource.h"
  24. #import "FChildChangeAccumulator.h"
  25. #import "FNodeFilter.h"
  26. #import "FOverwrite.h"
  27. #import "FMerge.h"
  28. #import "FAckUserWrite.h"
  29. #import "FViewProcessorResult.h"
  30. #import "FIRDataEventType.h"
  31. #import "FChange.h"
  32. #import "FEmptyNode.h"
  33. #import "FChildrenNode.h"
  34. #import "FPath.h"
  35. #import "FKeyIndex.h"
  36. #import "FCompoundWrite.h"
  37. #import "FImmutableTree.h"
  38. /**
  39. * An implementation of FCompleteChildSource that never returns any additional children
  40. */
  41. @interface FNoCompleteChildSource: NSObject<FCompleteChildSource>
  42. @end
  43. @implementation FNoCompleteChildSource
  44. + (FNoCompleteChildSource *) instance {
  45. static FNoCompleteChildSource *source = nil;
  46. static dispatch_once_t once;
  47. dispatch_once(&once, ^{
  48. source = [[FNoCompleteChildSource alloc] init];
  49. });
  50. return source;
  51. }
  52. - (id<FNode>) completeChild:(NSString *)childKey {
  53. return nil;
  54. }
  55. - (FNamedNode *) childByIndex:(id<FIndex>)index afterChild:(FNamedNode *)child isReverse:(BOOL)reverse {
  56. return nil;
  57. }
  58. @end
  59. /**
  60. * An implementation of FCompleteChildSource that uses a FWriteTree in addition to any other server data or
  61. * old event caches available to calculate complete children.
  62. */
  63. @interface FWriteTreeCompleteChildSource: NSObject<FCompleteChildSource>
  64. @property (nonatomic, strong) FWriteTreeRef *writes;
  65. @property (nonatomic, strong) FViewCache *viewCache;
  66. @property (nonatomic, strong) id<FNode> optCompleteServerCache;
  67. @end
  68. @implementation FWriteTreeCompleteChildSource
  69. - (id) initWithWrites:(FWriteTreeRef *)writes viewCache:(FViewCache *)viewCache serverCache:(id<FNode>)optCompleteServerCache {
  70. self = [super init];
  71. if (self) {
  72. self.writes = writes;
  73. self.viewCache = viewCache;
  74. self.optCompleteServerCache = optCompleteServerCache;
  75. }
  76. return self;
  77. }
  78. - (id<FNode>) completeChild:(NSString *)childKey {
  79. FCacheNode *node = self.viewCache.cachedEventSnap;
  80. if ([node isCompleteForChild:childKey]) {
  81. return [node.node getImmediateChild:childKey];
  82. } else {
  83. FCacheNode *serverNode;
  84. if (self.optCompleteServerCache) {
  85. // Since we're only ever getting child nodes, we can use the key index here
  86. FIndexedNode *indexed = [FIndexedNode indexedNodeWithNode:self.optCompleteServerCache index:[FKeyIndex keyIndex]];
  87. serverNode = [[FCacheNode alloc] initWithIndexedNode:indexed isFullyInitialized:YES isFiltered:NO];
  88. } else {
  89. serverNode = self.viewCache.cachedServerSnap;
  90. }
  91. return [self.writes calculateCompleteChild:childKey cache:serverNode];
  92. }
  93. }
  94. - (FNamedNode *) childByIndex:(id<FIndex>)index afterChild:(FNamedNode *)child isReverse:(BOOL)reverse {
  95. id<FNode> completeServerData = self.optCompleteServerCache != nil
  96. ? self.optCompleteServerCache
  97. : self.viewCache.completeServerSnap;
  98. return [self.writes calculateNextNodeAfterPost:child
  99. completeServerData:completeServerData
  100. reverse:reverse
  101. index:index];
  102. }
  103. @end
  104. @interface FViewProcessor ()
  105. @property (nonatomic, strong) id<FNodeFilter> filter;
  106. @end
  107. @implementation FViewProcessor
  108. - (id)initWithFilter:(id<FNodeFilter>)nodeFilter {
  109. self = [super init];
  110. if (self) {
  111. self.filter = nodeFilter;
  112. }
  113. return self;
  114. }
  115. - (FViewProcessorResult *)applyOperationOn:(FViewCache *)oldViewCache operation:(id<FOperation>)operation writesCache:(FWriteTreeRef *)writesCache completeCache:(id <FNode>)optCompleteCache {
  116. FChildChangeAccumulator *accumulator = [[FChildChangeAccumulator alloc] init];
  117. FViewCache *newViewCache;
  118. if (operation.type == FOperationTypeOverwrite) {
  119. FOverwrite *overwrite = (FOverwrite *) operation;
  120. if (operation.source.fromUser) {
  121. newViewCache = [self applyUserOverwriteTo:oldViewCache
  122. changePath:overwrite.path
  123. changedSnap:overwrite.snap
  124. writesCache:writesCache
  125. completeCache:optCompleteCache
  126. accumulator:accumulator];
  127. } else {
  128. NSAssert(operation.source.fromServer, @"Unknown source for overwrite.");
  129. // We filter the node if it's a tagged update or the node has been previously filtered and the update is
  130. // not at the root in which case it is ok (and necessary) to mark the node unfiltered again
  131. BOOL filterServerNode = overwrite.source.isTagged || (oldViewCache.cachedServerSnap.isFiltered &&
  132. !overwrite.path.isEmpty);
  133. newViewCache = [self applyServerOverwriteTo:oldViewCache
  134. changePath:overwrite.path
  135. snap:overwrite.snap
  136. writesCache:writesCache
  137. completeCache:optCompleteCache
  138. filterServerNode:filterServerNode
  139. accumulator:accumulator];
  140. }
  141. } else if (operation.type == FOperationTypeMerge) {
  142. FMerge *merge = (FMerge*)operation;
  143. if (operation.source.fromUser) {
  144. newViewCache = [self applyUserMergeTo:oldViewCache
  145. path:merge.path
  146. changedChildren:merge.children
  147. writesCache:writesCache
  148. completeCache:optCompleteCache
  149. accumulator:accumulator];
  150. } else {
  151. NSAssert(operation.source.fromServer, @"Unknown source for merge.");
  152. // We filter the node if it's a tagged update or the node has been previously filtered
  153. BOOL filterServerNode = merge.source.isTagged || oldViewCache.cachedServerSnap.isFiltered;
  154. newViewCache = [self applyServerMergeTo:oldViewCache
  155. path:merge.path
  156. changedChildren:merge.children
  157. writesCache:writesCache
  158. completeCache:optCompleteCache
  159. filterServerNode:filterServerNode
  160. accumulator:accumulator];
  161. }
  162. } else if (operation.type == FOperationTypeAckUserWrite) {
  163. FAckUserWrite *ackWrite = (FAckUserWrite *) operation;
  164. if (!ackWrite.revert) {
  165. newViewCache = [self ackUserWriteOn:oldViewCache
  166. ackPath:ackWrite.path
  167. affectedTree:ackWrite.affectedTree
  168. writesCache:writesCache
  169. completeCache:optCompleteCache
  170. accumulator:accumulator];
  171. } else {
  172. newViewCache = [self revertUserWriteOn:oldViewCache
  173. path:ackWrite.path
  174. writesCache:writesCache
  175. completeCache:optCompleteCache
  176. accumulator:accumulator];
  177. }
  178. } else if (operation.type == FOperationTypeListenComplete) {
  179. newViewCache = [self listenCompleteOldCache:oldViewCache
  180. path:operation.path
  181. writesCache:writesCache
  182. serverCache:optCompleteCache
  183. accumulator:accumulator];
  184. } else {
  185. [NSException raise:NSInternalInconsistencyException format:@"Unknown operation encountered %zd.", operation.type];
  186. return nil;
  187. }
  188. NSArray *changes = [self maybeAddValueFromOldViewCache:oldViewCache newViewCache:newViewCache changes:accumulator.changes];
  189. FViewProcessorResult *results = [[FViewProcessorResult alloc] initWithViewCache:newViewCache changes:changes];
  190. return results;
  191. }
  192. - (NSArray *) maybeAddValueFromOldViewCache:(FViewCache *)oldViewCache newViewCache:(FViewCache *)newViewCache changes:(NSArray *)changes {
  193. NSArray *newChanges = changes;
  194. FCacheNode *eventSnap = newViewCache.cachedEventSnap;
  195. if (eventSnap.isFullyInitialized) {
  196. BOOL isLeafOrEmpty = eventSnap.node.isLeafNode || eventSnap.node.isEmpty;
  197. if ([changes count] > 0 ||
  198. !oldViewCache.cachedEventSnap.isFullyInitialized ||
  199. (isLeafOrEmpty && ![eventSnap.node isEqual:oldViewCache.completeEventSnap]) ||
  200. ![eventSnap.node.getPriority isEqual:oldViewCache.completeEventSnap.getPriority]) {
  201. FChange *valueChange = [[FChange alloc] initWithType:FIRDataEventTypeValue indexedNode:eventSnap.indexedNode];
  202. NSMutableArray *mutableChanges = [changes mutableCopy];
  203. [mutableChanges addObject:valueChange];
  204. newChanges = mutableChanges;
  205. }
  206. }
  207. return newChanges;
  208. }
  209. - (FViewCache *) generateEventCacheAfterServerEvent:(FViewCache *)viewCache
  210. path:(FPath *)changePath
  211. writesCache:(FWriteTreeRef *)writesCache
  212. source:(id<FCompleteChildSource>)source
  213. accumulator:(FChildChangeAccumulator *)accumulator {
  214. FCacheNode *oldEventSnap = viewCache.cachedEventSnap;
  215. if ([writesCache shadowingWriteAtPath:changePath] != nil) {
  216. // we have a shadowing write, ignore changes.
  217. return viewCache;
  218. } else {
  219. FIndexedNode *newEventCache;
  220. if (changePath.isEmpty) {
  221. // TODO: figure out how this plays with "sliding ack windows"
  222. NSAssert(viewCache.cachedServerSnap.isFullyInitialized, @"If change path is empty, we must have complete server data");
  223. id<FNode> nodeWithLocalWrites;
  224. if (viewCache.cachedServerSnap.isFiltered) {
  225. // We need to special case this, because we need to only apply writes to complete children, or
  226. // we might end up raising events for incomplete children. If the server data is filtered deep
  227. // writes cannot be guaranteed to be complete
  228. id<FNode> serverCache = viewCache.completeServerSnap;
  229. FChildrenNode *completeChildren = ([serverCache isKindOfClass:[FChildrenNode class]]) ? serverCache : [FEmptyNode emptyNode];
  230. nodeWithLocalWrites = [writesCache calculateCompleteEventChildrenWithCompleteServerChildren:completeChildren];
  231. } else {
  232. nodeWithLocalWrites = [writesCache calculateCompleteEventCacheWithCompleteServerCache:viewCache.completeServerSnap];
  233. }
  234. FIndexedNode *indexedNode = [FIndexedNode indexedNodeWithNode:nodeWithLocalWrites index:self.filter.index];
  235. newEventCache = [self.filter updateFullNode:viewCache.cachedEventSnap.indexedNode
  236. withNewNode:indexedNode
  237. accumulator:accumulator];
  238. } else {
  239. NSString *childKey = [changePath getFront];
  240. if ([childKey isEqualToString:@".priority"]) {
  241. NSAssert(changePath.length == 1, @"Can't have a priority with additional path components");
  242. id<FNode> oldEventNode = oldEventSnap.node;
  243. id<FNode> serverNode = viewCache.cachedServerSnap.node;
  244. // we might have overwrites for this priority
  245. id<FNode> updatedPriority = [writesCache calculateEventCacheAfterServerOverwriteWithChildPath:changePath
  246. existingEventSnap:oldEventNode
  247. existingServerSnap:serverNode];
  248. if (updatedPriority != nil) {
  249. newEventCache = [self.filter updatePriority:updatedPriority forNode:oldEventSnap.indexedNode];
  250. } else {
  251. // priority didn't change, keep old node
  252. newEventCache = oldEventSnap.indexedNode;
  253. }
  254. } else {
  255. FPath *childChangePath = [changePath popFront];
  256. id<FNode> newEventChild;
  257. if ([oldEventSnap isCompleteForChild:childKey]) {
  258. id<FNode> serverNode = viewCache.cachedServerSnap.node;
  259. id<FNode> eventChildUpdate = [writesCache calculateEventCacheAfterServerOverwriteWithChildPath:changePath existingEventSnap:oldEventSnap.node existingServerSnap:serverNode];
  260. if (eventChildUpdate != nil) {
  261. newEventChild = [[oldEventSnap.node getImmediateChild:childKey] updateChild:childChangePath withNewChild:eventChildUpdate];
  262. } else {
  263. // Nothing changed, just keep the old child
  264. newEventChild = [oldEventSnap.node getImmediateChild:childKey];
  265. }
  266. } else {
  267. newEventChild = [writesCache calculateCompleteChild:childKey cache:viewCache.cachedServerSnap];
  268. }
  269. if (newEventChild != nil) {
  270. newEventCache = [self.filter updateChildIn:oldEventSnap.indexedNode
  271. forChildKey:childKey
  272. newChild:newEventChild
  273. affectedPath:childChangePath
  274. fromSource:source
  275. accumulator:accumulator];
  276. } else {
  277. // No complete children available or no change
  278. newEventCache = oldEventSnap.indexedNode;
  279. }
  280. }
  281. }
  282. return [viewCache updateEventSnap:newEventCache
  283. isComplete:(oldEventSnap.isFullyInitialized || changePath.isEmpty)
  284. isFiltered:self.filter.filtersNodes];
  285. }
  286. }
  287. - (FViewCache *) applyServerOverwriteTo:(FViewCache *)oldViewCache changePath:(FPath *)changePath snap:(id<FNode>)changedSnap
  288. writesCache:(FWriteTreeRef *)writesCache completeCache:(id<FNode>)optCompleteCache
  289. filterServerNode:(BOOL)filterServerNode accumulator:(FChildChangeAccumulator *)accumulator {
  290. FCacheNode *oldServerSnap = oldViewCache.cachedServerSnap;
  291. FIndexedNode *newServerCache;
  292. id<FNodeFilter> serverFilter = filterServerNode ? self.filter : self.filter.indexedFilter;
  293. if (changePath.isEmpty) {
  294. FIndexedNode *indexed = [FIndexedNode indexedNodeWithNode:changedSnap index:serverFilter.index];
  295. newServerCache = [serverFilter updateFullNode:oldServerSnap.indexedNode withNewNode:indexed accumulator:nil];
  296. } else if (serverFilter.filtersNodes && !oldServerSnap.isFiltered) {
  297. // We want to filter the server node, but we didn't filter the server node yet, so simulate a full update
  298. NSAssert(![changePath isEmpty], @"An empty path should been caught in the other branch");
  299. NSString *childKey = [changePath getFront];
  300. FPath *updatePath = [changePath popFront];
  301. id<FNode> newChild = [[oldServerSnap.node getImmediateChild:childKey] updateChild:updatePath
  302. withNewChild:changedSnap];
  303. FIndexedNode *indexed = [oldServerSnap.indexedNode updateChild:childKey withNewChild:newChild];
  304. newServerCache = [serverFilter updateFullNode:oldServerSnap.indexedNode withNewNode:indexed accumulator:nil];
  305. } else {
  306. NSString *childKey = [changePath getFront];
  307. if (![oldServerSnap isCompleteForPath:changePath] && changePath.length > 1) {
  308. // We don't update incomplete nodes with updates intended for other listeners.
  309. return oldViewCache;
  310. }
  311. FPath *childChangePath = [changePath popFront];
  312. id<FNode> childNode = [oldServerSnap.node getImmediateChild:childKey];
  313. id<FNode> newChildNode = [childNode updateChild:childChangePath withNewChild:changedSnap];
  314. if ([childKey isEqualToString:@".priority"]) {
  315. newServerCache = [serverFilter updatePriority:newChildNode forNode:oldServerSnap.indexedNode];
  316. } else {
  317. newServerCache = [serverFilter updateChildIn:oldServerSnap.indexedNode
  318. forChildKey:childKey
  319. newChild:newChildNode
  320. affectedPath:childChangePath
  321. fromSource:[FNoCompleteChildSource instance]
  322. accumulator:nil];
  323. }
  324. }
  325. FViewCache *newViewCache = [oldViewCache updateServerSnap:newServerCache
  326. isComplete:(oldServerSnap.isFullyInitialized || changePath.isEmpty)
  327. isFiltered:serverFilter.filtersNodes];
  328. id<FCompleteChildSource> source = [[FWriteTreeCompleteChildSource alloc] initWithWrites:writesCache
  329. viewCache:newViewCache
  330. serverCache:optCompleteCache];
  331. return [self generateEventCacheAfterServerEvent:newViewCache
  332. path:changePath
  333. writesCache:writesCache
  334. source:source
  335. accumulator:accumulator];
  336. }
  337. - (FViewCache *) applyUserOverwriteTo:(FViewCache *)oldViewCache
  338. changePath:(FPath *)changePath
  339. changedSnap:(id<FNode>)changedSnap
  340. writesCache:(FWriteTreeRef *)writesCache
  341. completeCache:(id<FNode>)optCompleteCache
  342. accumulator:(FChildChangeAccumulator *)accumulator {
  343. FCacheNode *oldEventSnap = oldViewCache.cachedEventSnap;
  344. FViewCache *newViewCache;
  345. id<FCompleteChildSource> source = [[FWriteTreeCompleteChildSource alloc] initWithWrites:writesCache
  346. viewCache:oldViewCache
  347. serverCache:optCompleteCache];
  348. if (changePath.isEmpty) {
  349. FIndexedNode *newIndexed = [FIndexedNode indexedNodeWithNode:changedSnap index:self.filter.index];
  350. FIndexedNode *newEventCache = [self.filter updateFullNode:oldEventSnap.indexedNode
  351. withNewNode:newIndexed
  352. accumulator:accumulator];
  353. newViewCache = [oldViewCache updateEventSnap:newEventCache isComplete:YES isFiltered:self.filter.filtersNodes];
  354. } else {
  355. NSString *childKey = [changePath getFront];
  356. if ([childKey isEqualToString:@".priority"]) {
  357. FIndexedNode *newEventCache = [self.filter updatePriority:changedSnap
  358. forNode:oldViewCache.cachedEventSnap.indexedNode];
  359. newViewCache = [oldViewCache updateEventSnap:newEventCache
  360. isComplete:oldEventSnap.isFullyInitialized
  361. isFiltered:oldEventSnap.isFiltered];
  362. } else {
  363. FPath *childChangePath = [changePath popFront];
  364. id<FNode> oldChild = [oldEventSnap.node getImmediateChild:childKey];
  365. id<FNode> newChild;
  366. if (childChangePath.isEmpty) {
  367. // Child overwrite, we can replace the child
  368. newChild = changedSnap;
  369. } else {
  370. id<FNode> childNode = [source completeChild:childKey];
  371. if (childNode != nil) {
  372. if ([[childChangePath getBack] isEqualToString:@".priority"] && [childNode getChild:[childChangePath parent]].isEmpty) {
  373. // This is a priority update on an empty node. If this node exists on the server, the server
  374. // will send down the priority in the update, so ignore for now
  375. newChild = childNode;
  376. } else {
  377. newChild = [childNode updateChild:childChangePath withNewChild:changedSnap];
  378. }
  379. } else {
  380. newChild = [FEmptyNode emptyNode];
  381. }
  382. }
  383. if (![oldChild isEqual:newChild]) {
  384. FIndexedNode *newEventSnap = [self.filter updateChildIn:oldEventSnap.indexedNode
  385. forChildKey:childKey
  386. newChild:newChild
  387. affectedPath:childChangePath
  388. fromSource:source
  389. accumulator:accumulator];
  390. newViewCache = [oldViewCache updateEventSnap:newEventSnap isComplete:oldEventSnap.isFullyInitialized isFiltered:self.filter.filtersNodes];
  391. } else {
  392. newViewCache = oldViewCache;
  393. }
  394. }
  395. }
  396. return newViewCache;
  397. }
  398. + (BOOL) cache:(FViewCache *)viewCache hasChild:(NSString *)childKey {
  399. return [viewCache.cachedEventSnap isCompleteForChild:childKey];
  400. }
  401. /**
  402. * @param changedChildren NSDictionary of child name (NSString*) to child value (id<FNode>)
  403. */
  404. - (FViewCache *) applyUserMergeTo:(FViewCache *)viewCache
  405. path:(FPath *)path
  406. changedChildren:(FCompoundWrite *)changedChildren
  407. writesCache:(FWriteTreeRef *)writesCache
  408. completeCache:(id<FNode>)serverCache
  409. accumulator:(FChildChangeAccumulator *)accumulator {
  410. // HACK: In the case of a limit query, there may be some changes that bump things out of the
  411. // window leaving room for new items. It's important we process these changes first, so we
  412. // iterate the changes twice, first processing any that affect items currently in view.
  413. // TODO: I consider an item "in view" if cacheHasChild is true, which checks both the server
  414. // and event snap. I'm not sure if this will result in edge cases when a child is in one but
  415. // not the other.
  416. __block FViewCache *curViewCache = viewCache;
  417. [changedChildren enumerateWrites:^(FPath *relativePath, id<FNode> childNode, BOOL *stop) {
  418. FPath *writePath = [path child:relativePath];
  419. if ([FViewProcessor cache:viewCache hasChild:[writePath getFront]]) {
  420. curViewCache = [self applyUserOverwriteTo:curViewCache
  421. changePath:writePath
  422. changedSnap:childNode
  423. writesCache:writesCache
  424. completeCache:serverCache
  425. accumulator:accumulator];
  426. }
  427. }];
  428. [changedChildren enumerateWrites:^(FPath *relativePath, id<FNode> childNode, BOOL *stop) {
  429. FPath *writePath = [path child:relativePath];
  430. if (![FViewProcessor cache:viewCache hasChild:[writePath getFront]]) {
  431. curViewCache = [self applyUserOverwriteTo:curViewCache
  432. changePath:writePath
  433. changedSnap:childNode
  434. writesCache:writesCache
  435. completeCache:serverCache
  436. accumulator:accumulator];
  437. }
  438. }];
  439. return curViewCache;
  440. }
  441. - (FViewCache *) applyServerMergeTo:(FViewCache *)viewCache
  442. path:(FPath *)path
  443. changedChildren:(FCompoundWrite *)changedChildren
  444. writesCache:(FWriteTreeRef *)writesCache
  445. completeCache:(id<FNode>)serverCache
  446. filterServerNode:(BOOL)filterServerNode
  447. accumulator:(FChildChangeAccumulator *)accumulator {
  448. // If we don't have a cache yet, this merge was intended for a previously listen in the same location. Ignore it and
  449. // wait for the complete data update coming soon.
  450. if (viewCache.cachedServerSnap.node.isEmpty && !viewCache.cachedServerSnap.isFullyInitialized) {
  451. return viewCache;
  452. }
  453. // HACK: In the case of a limit query, there may be some changes that bump things out of the
  454. // window leaving room for new items. It's important we process these changes first, so we
  455. // iterate the changes twice, first processing any that affect items currently in view.
  456. // TODO: I consider an item "in view" if cacheHasChild is true, which checks both the server
  457. // and event snap. I'm not sure if this will result in edge cases when a child is in one but
  458. // not the other.
  459. __block FViewCache *curViewCache = viewCache;
  460. FCompoundWrite *actualMerge;
  461. if (path.isEmpty) {
  462. actualMerge = changedChildren;
  463. } else {
  464. actualMerge = [[FCompoundWrite emptyWrite] addCompoundWrite:changedChildren atPath:path];
  465. }
  466. id<FNode> serverNode = viewCache.cachedServerSnap.node;
  467. NSDictionary *childCompoundWrites = actualMerge.childCompoundWrites;
  468. [childCompoundWrites enumerateKeysAndObjectsUsingBlock:^(NSString *childKey, FCompoundWrite *childMerge, BOOL *stop) {
  469. if ([serverNode hasChild:childKey]) {
  470. id<FNode> serverChild = [viewCache.cachedServerSnap.node getImmediateChild:childKey];
  471. id<FNode> newChild = [childMerge applyToNode:serverChild];
  472. curViewCache = [self applyServerOverwriteTo:curViewCache
  473. changePath:[[FPath alloc] initWith:childKey]
  474. snap:newChild
  475. writesCache:writesCache
  476. completeCache:serverCache
  477. filterServerNode:filterServerNode
  478. accumulator:accumulator];
  479. }
  480. }];
  481. [childCompoundWrites enumerateKeysAndObjectsUsingBlock:^(NSString *childKey, FCompoundWrite *childMerge, BOOL *stop) {
  482. bool isUnknownDeepMerge = ![viewCache.cachedServerSnap isCompleteForChild:childKey] && childMerge.rootWrite == nil;
  483. if (![serverNode hasChild:childKey] && !isUnknownDeepMerge) {
  484. id<FNode> serverChild = [viewCache.cachedServerSnap.node getImmediateChild:childKey];
  485. id<FNode> newChild = [childMerge applyToNode:serverChild];
  486. curViewCache = [self applyServerOverwriteTo:curViewCache
  487. changePath:[[FPath alloc] initWith:childKey]
  488. snap:newChild
  489. writesCache:writesCache
  490. completeCache:serverCache
  491. filterServerNode:filterServerNode
  492. accumulator:accumulator];
  493. }
  494. }];
  495. return curViewCache;
  496. }
  497. - (FViewCache *) ackUserWriteOn:(FViewCache *)viewCache
  498. ackPath:(FPath *)ackPath
  499. affectedTree:(FImmutableTree *)affectedTree
  500. writesCache:(FWriteTreeRef *)writesCache
  501. completeCache:(id <FNode>)optCompleteCache
  502. accumulator:(FChildChangeAccumulator *)accumulator {
  503. if ([writesCache shadowingWriteAtPath:ackPath] != nil) {
  504. return viewCache;
  505. }
  506. // Only filter server node if it is currently filtered
  507. BOOL filterServerNode = viewCache.cachedServerSnap.isFiltered;
  508. // Essentially we'll just get our existing server cache for the affected paths and re-apply it as a server update
  509. // now that it won't be shadowed.
  510. FCacheNode *serverCache = viewCache.cachedServerSnap;
  511. if (affectedTree.value != nil) {
  512. // This is an overwrite.
  513. if ((ackPath.isEmpty && serverCache.isFullyInitialized) || [serverCache isCompleteForPath:ackPath]) {
  514. return [self applyServerOverwriteTo:viewCache changePath:ackPath snap:[serverCache.node getChild:ackPath]
  515. writesCache:writesCache completeCache:optCompleteCache
  516. filterServerNode:filterServerNode accumulator:accumulator];
  517. } else if (ackPath.isEmpty) {
  518. // This is a goofy edge case where we are acking data at this location but don't have full data. We
  519. // should just re-apply whatever we have in our cache as a merge.
  520. FCompoundWrite *changedChildren = [FCompoundWrite emptyWrite];
  521. for(FNamedNode *child in serverCache.node.childEnumerator) {
  522. changedChildren = [changedChildren addWrite:child.node atKey:child.name];
  523. }
  524. return [self applyServerMergeTo:viewCache path:ackPath changedChildren:changedChildren
  525. writesCache:writesCache completeCache:optCompleteCache
  526. filterServerNode:filterServerNode accumulator:accumulator];
  527. } else {
  528. return viewCache;
  529. }
  530. } else {
  531. // This is a merge.
  532. __block FCompoundWrite *changedChildren = [FCompoundWrite emptyWrite];
  533. [affectedTree forEach:^(FPath *mergePath, id value) {
  534. FPath *serverCachePath = [ackPath child:mergePath];
  535. if ([serverCache isCompleteForPath:serverCachePath]) {
  536. changedChildren = [changedChildren addWrite:[serverCache.node getChild:serverCachePath] atPath:mergePath];
  537. }
  538. }];
  539. return [self applyServerMergeTo:viewCache path:ackPath changedChildren:changedChildren
  540. writesCache:writesCache completeCache:optCompleteCache
  541. filterServerNode:filterServerNode accumulator:accumulator];
  542. }
  543. }
  544. - (FViewCache *) revertUserWriteOn:(FViewCache *)viewCache
  545. path:(FPath *)path
  546. writesCache:(FWriteTreeRef *)writesCache
  547. completeCache:(id<FNode>)optCompleteCache
  548. accumulator:(FChildChangeAccumulator *)accumulator {
  549. if ([writesCache shadowingWriteAtPath:path] != nil) {
  550. return viewCache;
  551. } else {
  552. id<FCompleteChildSource> source = [[FWriteTreeCompleteChildSource alloc] initWithWrites:writesCache
  553. viewCache:viewCache
  554. serverCache:optCompleteCache];
  555. FIndexedNode *oldEventCache = viewCache.cachedEventSnap.indexedNode;
  556. FIndexedNode *newEventCache;
  557. if (path.isEmpty || [[path getFront] isEqualToString:@".priority"]) {
  558. id<FNode> newNode;
  559. if (viewCache.cachedServerSnap.isFullyInitialized) {
  560. newNode = [writesCache calculateCompleteEventCacheWithCompleteServerCache:viewCache.completeServerSnap];
  561. } else {
  562. newNode = [writesCache calculateCompleteEventChildrenWithCompleteServerChildren:viewCache.cachedServerSnap.node];
  563. }
  564. FIndexedNode *indexedNode = [FIndexedNode indexedNodeWithNode:newNode index:self.filter.index];
  565. newEventCache = [self.filter updateFullNode:oldEventCache withNewNode:indexedNode accumulator:accumulator];
  566. } else {
  567. NSString *childKey = [path getFront];
  568. id<FNode> newChild = [writesCache calculateCompleteChild:childKey cache:viewCache.cachedServerSnap];
  569. if (newChild == nil && [viewCache.cachedServerSnap isCompleteForChild:childKey]) {
  570. newChild = [oldEventCache.node getImmediateChild:childKey];
  571. }
  572. if (newChild != nil) {
  573. newEventCache = [self.filter updateChildIn:oldEventCache
  574. forChildKey:childKey
  575. newChild:newChild
  576. affectedPath:[path popFront]
  577. fromSource:source
  578. accumulator:accumulator];
  579. } else if (newChild == nil && [viewCache.cachedEventSnap.node hasChild:childKey]) {
  580. // No complete child available, delete the existing one, if any
  581. newEventCache = [self.filter updateChildIn:oldEventCache
  582. forChildKey:childKey
  583. newChild:[FEmptyNode emptyNode]
  584. affectedPath:[path popFront]
  585. fromSource:source
  586. accumulator:accumulator];
  587. } else {
  588. newEventCache = oldEventCache;
  589. }
  590. if (newEventCache.node.isEmpty && viewCache.cachedServerSnap.isFullyInitialized) {
  591. // We might have reverted all child writes. Maybe the old event was a leaf node.
  592. id<FNode> complete = [writesCache calculateCompleteEventCacheWithCompleteServerCache:viewCache.completeServerSnap];
  593. if (complete.isLeafNode) {
  594. FIndexedNode *indexed = [FIndexedNode indexedNodeWithNode:complete];
  595. newEventCache = [self.filter updateFullNode:newEventCache
  596. withNewNode:indexed
  597. accumulator:accumulator];
  598. }
  599. }
  600. }
  601. BOOL complete = viewCache.cachedServerSnap.isFullyInitialized || [writesCache shadowingWriteAtPath:[FPath empty]] != nil;
  602. return [viewCache updateEventSnap:newEventCache isComplete:complete isFiltered:self.filter.filtersNodes];
  603. }
  604. }
  605. - (FViewCache *) listenCompleteOldCache:(FViewCache *)viewCache
  606. path:(FPath *)path
  607. writesCache:(FWriteTreeRef *)writesCache
  608. serverCache:(id<FNode>)servercache
  609. accumulator:(FChildChangeAccumulator *)accumulator {
  610. FCacheNode *oldServerNode = viewCache.cachedServerSnap;
  611. FViewCache *newViewCache = [viewCache updateServerSnap:oldServerNode.indexedNode
  612. isComplete:(oldServerNode.isFullyInitialized || path.isEmpty)
  613. isFiltered:oldServerNode.isFiltered];
  614. return [self generateEventCacheAfterServerEvent:newViewCache path:path writesCache:writesCache source:[FNoCompleteChildSource instance] accumulator:accumulator];
  615. }
  616. @end