FWriteTree.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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 "FWriteTree.h"
  17. #import "FCacheNode.h"
  18. #import "FChildrenNode.h"
  19. #import "FCompoundWrite.h"
  20. #import "FEmptyNode.h"
  21. #import "FImmutableTree.h"
  22. #import "FIndex.h"
  23. #import "FNamedNode.h"
  24. #import "FNode.h"
  25. #import "FPath.h"
  26. #import "FWriteRecord.h"
  27. #import "FWriteTreeRef.h"
  28. @interface FWriteTree ()
  29. /**
  30. * A tree tracking the results of applying all visible writes. This does not
  31. * include transactions with applyLocally=false or writes that are completely
  32. * shadowed by other writes. Contains id<FNode> as values.
  33. */
  34. @property(nonatomic, strong) FCompoundWrite *visibleWrites;
  35. /**
  36. * A list of pending writes, regardless of visibility and shadowed-ness. Used to
  37. * calcuate arbitrary sets of the changed data, such as hidden writes (from
  38. * transactions) or changes with certain writes excluded (also used by
  39. * transactions). Contains FWriteRecords.
  40. */
  41. @property(nonatomic, strong) NSMutableArray *allWrites;
  42. @property(nonatomic) NSInteger lastWriteId;
  43. @end
  44. /**
  45. * FWriteTree tracks all pending user-initiated writes and has methods to
  46. * calcuate the result of merging them with underlying server data (to create
  47. * "event cache" data). Pending writes are added with addOverwriteAtPath: and
  48. * addMergeAtPath: and removed with removeWriteId:.
  49. */
  50. @implementation FWriteTree
  51. @synthesize allWrites;
  52. @synthesize lastWriteId;
  53. - (id)init {
  54. self = [super init];
  55. if (self) {
  56. self.visibleWrites = [FCompoundWrite emptyWrite];
  57. self.allWrites = [[NSMutableArray alloc] init];
  58. self.lastWriteId = -1;
  59. }
  60. return self;
  61. }
  62. /**
  63. * Create a new WriteTreeRef for the given path. For use with a new sync point
  64. * at the given path.
  65. */
  66. - (FWriteTreeRef *)childWritesForPath:(FPath *)path {
  67. return [[FWriteTreeRef alloc] initWithPath:path writeTree:self];
  68. }
  69. /**
  70. * Record a new overwrite from user code.
  71. * @param visible Is set to false by some transactions. It should be excluded
  72. * from event caches.
  73. */
  74. - (void)addOverwriteAtPath:(FPath *)path
  75. newData:(id<FNode>)newData
  76. writeId:(NSInteger)writeId
  77. isVisible:(BOOL)visible {
  78. NSAssert(writeId > self.lastWriteId,
  79. @"Stacking an older write on top of a newer one");
  80. FWriteRecord *record = [[FWriteRecord alloc] initWithPath:path
  81. overwrite:newData
  82. writeId:writeId
  83. visible:visible];
  84. [self.allWrites addObject:record];
  85. if (visible) {
  86. self.visibleWrites = [self.visibleWrites addWrite:newData atPath:path];
  87. }
  88. self.lastWriteId = writeId;
  89. }
  90. /**
  91. * Record a new merge from user code.
  92. * @param changedChildren maps NSString -> id<FNode>
  93. */
  94. - (void)addMergeAtPath:(FPath *)path
  95. changedChildren:(FCompoundWrite *)changedChildren
  96. writeId:(NSInteger)writeId {
  97. NSAssert(writeId > self.lastWriteId,
  98. @"Stacking an older merge on top of newer one");
  99. FWriteRecord *record = [[FWriteRecord alloc] initWithPath:path
  100. merge:changedChildren
  101. writeId:writeId];
  102. [self.allWrites addObject:record];
  103. self.visibleWrites = [self.visibleWrites addCompoundWrite:changedChildren
  104. atPath:path];
  105. self.lastWriteId = writeId;
  106. }
  107. - (FWriteRecord *)writeForId:(NSInteger)writeId {
  108. NSUInteger index = [self.allWrites
  109. indexOfObjectPassingTest:^BOOL(FWriteRecord *write, NSUInteger idx,
  110. BOOL *stop) {
  111. return write.writeId == writeId;
  112. }];
  113. return (index == NSNotFound) ? nil : self.allWrites[index];
  114. }
  115. /**
  116. * Remove a write (either an overwrite or merge) that has been successfully
  117. * acknowledged by the server. Recalculates the tree if necessary. We return the
  118. * path of the write and whether it may have been visible, meaning views need to
  119. * reevaluate.
  120. *
  121. * @return YES if the write may have been visible (meaning we'll need to
  122. * reevaluate / raise events as a result).
  123. */
  124. - (BOOL)removeWriteId:(NSInteger)writeId {
  125. NSUInteger index = [self.allWrites
  126. indexOfObjectPassingTest:^BOOL(FWriteRecord *record, NSUInteger idx,
  127. BOOL *stop) {
  128. if (record.writeId == writeId) {
  129. return YES;
  130. } else {
  131. return NO;
  132. }
  133. }];
  134. NSAssert(index != NSNotFound,
  135. @"[FWriteTree removeWriteId:] called with nonexistent writeId.");
  136. FWriteRecord *writeToRemove = self.allWrites[index];
  137. [self.allWrites removeObjectAtIndex:index];
  138. BOOL removedWriteWasVisible = writeToRemove.visible;
  139. BOOL removedWriteOverlapsWithOtherWrites = NO;
  140. NSInteger i = [self.allWrites count] - 1;
  141. while (removedWriteWasVisible && i >= 0) {
  142. FWriteRecord *currentWrite = [self.allWrites objectAtIndex:i];
  143. if (currentWrite.visible) {
  144. if (i >= index && [self record:currentWrite
  145. containsPath:writeToRemove.path]) {
  146. // The removed write was completely shadowed by a subsequent
  147. // write.
  148. removedWriteWasVisible = NO;
  149. } else if ([writeToRemove.path contains:currentWrite.path]) {
  150. // Either we're covering some writes or they're covering part of
  151. // us (depending on which came first).
  152. removedWriteOverlapsWithOtherWrites = YES;
  153. }
  154. }
  155. i--;
  156. }
  157. if (!removedWriteWasVisible) {
  158. return NO;
  159. } else if (removedWriteOverlapsWithOtherWrites) {
  160. // There's some shadowing going on. Just rebuild the visible writes from
  161. // scratch.
  162. [self resetTree];
  163. return YES;
  164. } else {
  165. // There's no shadowing. We can safely just remove the write(s) from
  166. // visibleWrites.
  167. if ([writeToRemove isOverwrite]) {
  168. self.visibleWrites =
  169. [self.visibleWrites removeWriteAtPath:writeToRemove.path];
  170. } else {
  171. FCompoundWrite *merge = writeToRemove.merge;
  172. [merge enumerateWrites:^(FPath *path, id<FNode> node, BOOL *stop) {
  173. self.visibleWrites = [self.visibleWrites
  174. removeWriteAtPath:[writeToRemove.path child:path]];
  175. }];
  176. }
  177. return YES;
  178. }
  179. }
  180. - (NSArray *)removeAllWrites {
  181. NSArray *writes = self.allWrites;
  182. self.visibleWrites = [FCompoundWrite emptyWrite];
  183. self.allWrites = [NSMutableArray array];
  184. return writes;
  185. }
  186. /**
  187. * @return A complete snapshot for the given path if there's visible write data
  188. * at that path, else nil. No server data is considered.
  189. */
  190. - (id<FNode>)completeWriteDataAtPath:(FPath *)path {
  191. return [self.visibleWrites completeNodeAtPath:path];
  192. }
  193. /**
  194. * Given optional, underlying server data, and an optional set of constraints
  195. * (exclude some sets, include hidden writes), attempt to calculate a complete
  196. * snapshot for the given path
  197. * @param includeHiddenWrites Defaults to false, whether or not to layer on
  198. * writes with visible set to false
  199. */
  200. - (id<FNode>)calculateCompleteEventCacheAtPath:(FPath *)treePath
  201. completeServerCache:(id<FNode>)completeServerCache
  202. excludeWriteIds:(NSArray *)writeIdsToExclude
  203. includeHiddenWrites:(BOOL)includeHiddenWrites {
  204. if (writeIdsToExclude == nil && !includeHiddenWrites) {
  205. id<FNode> shadowingNode =
  206. [self.visibleWrites completeNodeAtPath:treePath];
  207. if (shadowingNode != nil) {
  208. return shadowingNode;
  209. } else {
  210. // No cache here. Can't claim complete knowledge.
  211. FCompoundWrite *subMerge =
  212. [self.visibleWrites childCompoundWriteAtPath:treePath];
  213. if (subMerge.isEmpty) {
  214. return completeServerCache;
  215. } else if (completeServerCache == nil &&
  216. ![subMerge hasCompleteWriteAtPath:[FPath empty]]) {
  217. // We wouldn't have a complete snapshot since there's no
  218. // underlying data and no complete shadow
  219. return nil;
  220. } else {
  221. id<FNode> layeredCache = completeServerCache != nil
  222. ? completeServerCache
  223. : [FEmptyNode emptyNode];
  224. return [subMerge applyToNode:layeredCache];
  225. }
  226. }
  227. } else {
  228. FCompoundWrite *merge =
  229. [self.visibleWrites childCompoundWriteAtPath:treePath];
  230. if (!includeHiddenWrites && merge.isEmpty) {
  231. return completeServerCache;
  232. } else {
  233. // If the server cache is null and we don't have a complete cache,
  234. // we need to return nil
  235. if (!includeHiddenWrites && completeServerCache == nil &&
  236. ![merge hasCompleteWriteAtPath:[FPath empty]]) {
  237. return nil;
  238. } else {
  239. BOOL (^filter)(FWriteRecord *) = ^(FWriteRecord *record) {
  240. return (BOOL)(
  241. (record.visible || includeHiddenWrites) &&
  242. (writeIdsToExclude == nil ||
  243. ![writeIdsToExclude
  244. containsObject:
  245. [NSNumber numberWithInteger:record.writeId]]) &&
  246. ([record.path contains:treePath] ||
  247. [treePath contains:record.path]));
  248. };
  249. FCompoundWrite *mergeAtPath =
  250. [FWriteTree layerTreeFromWrites:self.allWrites
  251. filter:filter
  252. treeRoot:treePath];
  253. id<FNode> layeredCache = completeServerCache
  254. ? completeServerCache
  255. : [FEmptyNode emptyNode];
  256. return [mergeAtPath applyToNode:layeredCache];
  257. }
  258. }
  259. }
  260. }
  261. /**
  262. * With optional, underlying server data, attempt to return a children node of
  263. * children that we have complete data for. Used when creating new views, to
  264. * pre-fill their complete event children snapshot.
  265. */
  266. - (FChildrenNode *)calculateCompleteEventChildrenAtPath:(FPath *)treePath
  267. completeServerChildren:
  268. (id<FNode>)completeServerChildren {
  269. __block id<FNode> completeChildren = [FEmptyNode emptyNode];
  270. id<FNode> topLevelSet = [self.visibleWrites completeNodeAtPath:treePath];
  271. if (topLevelSet != nil) {
  272. if (![topLevelSet isLeafNode]) {
  273. // We're shadowing everything. Return the children.
  274. FChildrenNode *topChildrenNode = topLevelSet;
  275. [topChildrenNode enumerateChildrenUsingBlock:^(
  276. NSString *key, id<FNode> node, BOOL *stop) {
  277. completeChildren = [completeChildren updateImmediateChild:key
  278. withNewChild:node];
  279. }];
  280. }
  281. return completeChildren;
  282. } else {
  283. // Layer any children we have on top of this
  284. // We know we don't have a top-level set, so just enumerate existing
  285. // children, and apply any updates
  286. FCompoundWrite *merge =
  287. [self.visibleWrites childCompoundWriteAtPath:treePath];
  288. [completeServerChildren enumerateChildrenUsingBlock:^(
  289. NSString *key, id<FNode> node, BOOL *stop) {
  290. FCompoundWrite *childMerge =
  291. [merge childCompoundWriteAtPath:[[FPath alloc] initWith:key]];
  292. id<FNode> newChildNode = [childMerge applyToNode:node];
  293. completeChildren =
  294. [completeChildren updateImmediateChild:key
  295. withNewChild:newChildNode];
  296. }];
  297. // Add any complete children we have from the set.
  298. for (FNamedNode *node in merge.completeChildren) {
  299. completeChildren =
  300. [completeChildren updateImmediateChild:node.name
  301. withNewChild:node.node];
  302. }
  303. return completeChildren;
  304. }
  305. }
  306. /**
  307. * Given that the underlying server data has updated, determine what, if
  308. * anything, needs to be applied to the event cache.
  309. *
  310. * Possibilities
  311. *
  312. * 1. No write are shadowing. Events should be raised, the snap to be applied
  313. * comes from the server data.
  314. *
  315. * 2. Some write is completely shadowing. No events to be raised.
  316. *
  317. * 3. Is partially shadowed. Events ..
  318. *
  319. * Either existingEventSnap or existingServerSnap must exist.
  320. */
  321. - (id<FNode>)calculateEventCacheAfterServerOverwriteAtPath:(FPath *)treePath
  322. childPath:(FPath *)childPath
  323. existingEventSnap:
  324. (id<FNode>)existingEventSnap
  325. existingServerSnap:
  326. (id<FNode>)existingServerSnap {
  327. NSAssert(existingEventSnap != nil || existingServerSnap != nil,
  328. @"Either existingEventSnap or existingServerSanp must exist.");
  329. FPath *path = [treePath child:childPath];
  330. if ([self.visibleWrites hasCompleteWriteAtPath:path]) {
  331. // At this point we can probably guarantee that we're in case 2, meaning
  332. // no events May need to check visibility while doing the
  333. // findRootMostValueAndPath call
  334. return nil;
  335. } else {
  336. // This could be more efficient if the serverNode + updates doesn't
  337. // change the eventSnap However this is tricky to find out, since user
  338. // updates don't necessary change the server snap, e.g. priority updates
  339. // on empty nodes, or deep deletes. Another special case is if the
  340. // server adds nodes, but doesn't change any existing writes. It is
  341. // therefore not enough to only check if the updates change the
  342. // serverNode. Maybe check if the merge tree contains these special
  343. // cases and only do a full overwrite in that case?
  344. FCompoundWrite *childMerge =
  345. [self.visibleWrites childCompoundWriteAtPath:path];
  346. if (childMerge.isEmpty) {
  347. // We're not shadowing at all. Case 1
  348. return [existingServerSnap getChild:childPath];
  349. } else {
  350. return [childMerge
  351. applyToNode:[existingServerSnap getChild:childPath]];
  352. }
  353. }
  354. }
  355. /**
  356. * Returns a complete child for a given server snap after applying all user
  357. * writes or nil if there is no complete child for this child key.
  358. */
  359. - (id<FNode>)calculateCompleteChildAtPath:(FPath *)treePath
  360. childKey:(NSString *)childKey
  361. cache:(FCacheNode *)existingServerCache {
  362. FPath *path = [treePath childFromString:childKey];
  363. id<FNode> shadowingNode = [self.visibleWrites completeNodeAtPath:path];
  364. if (shadowingNode != nil) {
  365. return shadowingNode;
  366. } else {
  367. if ([existingServerCache isCompleteForChild:childKey]) {
  368. FCompoundWrite *childMerge =
  369. [self.visibleWrites childCompoundWriteAtPath:path];
  370. return [childMerge applyToNode:[existingServerCache.node
  371. getImmediateChild:childKey]];
  372. } else {
  373. return nil;
  374. }
  375. }
  376. }
  377. /**
  378. * Returns a node if there is a complete overwrite for this path. More
  379. * specifically, if there is a write at a higher path, this will return the
  380. * child of that write relative to the write and this path. Returns null if
  381. * there is no write at this path.
  382. */
  383. - (id<FNode>)shadowingWriteAtPath:(FPath *)path {
  384. return [self.visibleWrites completeNodeAtPath:path];
  385. }
  386. /**
  387. * This method is used when processing child remove events on a query. If we
  388. * can, we pull in children that were outside the window, but may now be in the
  389. * window.
  390. */
  391. - (FNamedNode *)calculateNextNodeAfterPost:(FNamedNode *)post
  392. atPath:(FPath *)treePath
  393. completeServerData:(id<FNode>)completeServerData
  394. reverse:(BOOL)reverse
  395. index:(id<FIndex>)index {
  396. __block id<FNode> toIterate;
  397. FCompoundWrite *merge =
  398. [self.visibleWrites childCompoundWriteAtPath:treePath];
  399. id<FNode> shadowingNode = [merge completeNodeAtPath:[FPath empty]];
  400. if (shadowingNode != nil) {
  401. toIterate = shadowingNode;
  402. } else if (completeServerData != nil) {
  403. toIterate = [merge applyToNode:completeServerData];
  404. } else {
  405. return nil;
  406. }
  407. __block NSString *currentNextKey = nil;
  408. __block id<FNode> currentNextNode = nil;
  409. [toIterate enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
  410. BOOL *stop) {
  411. if ([index compareKey:key
  412. andNode:node
  413. toOtherKey:post.name
  414. andNode:post.node
  415. reverse:reverse] > NSOrderedSame &&
  416. (!currentNextKey || [index compareKey:key
  417. andNode:node
  418. toOtherKey:currentNextKey
  419. andNode:currentNextNode
  420. reverse:reverse] < NSOrderedSame)) {
  421. currentNextKey = key;
  422. currentNextNode = node;
  423. }
  424. }];
  425. if (currentNextKey != nil) {
  426. return [FNamedNode nodeWithName:currentNextKey node:currentNextNode];
  427. } else {
  428. return nil;
  429. }
  430. }
  431. #pragma mark -
  432. #pragma mark Private Methods
  433. - (BOOL)record:(FWriteRecord *)record containsPath:(FPath *)path {
  434. if ([record isOverwrite]) {
  435. return [record.path contains:path];
  436. } else {
  437. __block BOOL contains = NO;
  438. [record.merge
  439. enumerateWrites:^(FPath *childPath, id<FNode> node, BOOL *stop) {
  440. contains = [[record.path child:childPath] contains:path];
  441. *stop = contains;
  442. }];
  443. return contains;
  444. }
  445. }
  446. /**
  447. * Re-layer the writes and merges into a tree so we can efficiently calculate
  448. * event snapshots
  449. */
  450. - (void)resetTree {
  451. self.visibleWrites =
  452. [FWriteTree layerTreeFromWrites:self.allWrites
  453. filter:[FWriteTree defaultFilter]
  454. treeRoot:[FPath empty]];
  455. if ([self.allWrites count] > 0) {
  456. FWriteRecord *lastRecord = self.allWrites[[self.allWrites count] - 1];
  457. self.lastWriteId = lastRecord.writeId;
  458. } else {
  459. self.lastWriteId = -1;
  460. }
  461. }
  462. /**
  463. * The default filter used when constructing the tree. Keep everything that's
  464. * visible.
  465. */
  466. + (BOOL (^)(FWriteRecord *record))defaultFilter {
  467. static BOOL (^filter)(FWriteRecord *);
  468. static dispatch_once_t filterToken;
  469. dispatch_once(&filterToken, ^{
  470. filter = ^(FWriteRecord *record) {
  471. return YES;
  472. };
  473. });
  474. return filter;
  475. }
  476. /**
  477. * Static method. Given an array of WriteRecords, a filter for which ones to
  478. * include, and a path, construct a merge at that path
  479. * @return An FImmutableTree of id<FNode>s.
  480. */
  481. + (FCompoundWrite *)layerTreeFromWrites:(NSArray *)writes
  482. filter:(BOOL (^)(FWriteRecord *record))filter
  483. treeRoot:(FPath *)treeRoot {
  484. __block FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  485. [writes enumerateObjectsUsingBlock:^(FWriteRecord *record, NSUInteger idx,
  486. BOOL *stop) {
  487. // Theory, a later set will either:
  488. // a) abort a relevant transaction, so no need to worry about excluding it
  489. // from calculating that transaction b) not be relevant to a transaction
  490. // (separate branch), so again will not affect the data for that
  491. // transaction
  492. if (filter(record)) {
  493. FPath *writePath = record.path;
  494. if ([record isOverwrite]) {
  495. if ([treeRoot contains:writePath]) {
  496. FPath *relativePath = [FPath relativePathFrom:treeRoot
  497. to:writePath];
  498. compoundWrite = [compoundWrite addWrite:record.overwrite
  499. atPath:relativePath];
  500. } else if ([writePath contains:treeRoot]) {
  501. id<FNode> child = [record.overwrite
  502. getChild:[FPath relativePathFrom:writePath to:treeRoot]];
  503. compoundWrite = [compoundWrite addWrite:child
  504. atPath:[FPath empty]];
  505. } else {
  506. // There is no overlap between root path and write path,
  507. // ignore write
  508. }
  509. } else {
  510. if ([treeRoot contains:writePath]) {
  511. FPath *relativePath = [FPath relativePathFrom:treeRoot
  512. to:writePath];
  513. compoundWrite = [compoundWrite addCompoundWrite:record.merge
  514. atPath:relativePath];
  515. } else if ([writePath contains:treeRoot]) {
  516. FPath *relativePath = [FPath relativePathFrom:writePath
  517. to:treeRoot];
  518. if (relativePath.isEmpty) {
  519. compoundWrite =
  520. [compoundWrite addCompoundWrite:record.merge
  521. atPath:[FPath empty]];
  522. } else {
  523. id<FNode> child =
  524. [record.merge completeNodeAtPath:relativePath];
  525. if (child != nil) {
  526. // There exists a child in this node that matches the
  527. // root path
  528. id<FNode> deepNode =
  529. [child getChild:[relativePath popFront]];
  530. compoundWrite =
  531. [compoundWrite addWrite:deepNode
  532. atPath:[FPath empty]];
  533. }
  534. }
  535. } else {
  536. // There is no overlap between root path and write path,
  537. // ignore write
  538. }
  539. }
  540. }
  541. }];
  542. return compoundWrite;
  543. }
  544. @end