Skip to content

Change FServerValues to just-in-time read from SyncTrees. #5183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions Firebase/Database/Core/FRepo.m
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,7 @@ - (void)restoreWrites {
}
lastWriteId = writeId;
self.writeIdCounter = writeId + 1;
id<FNode> existing =
[self.serverSyncTree calcCompleteEventCacheAtPath:write.path
excludeWriteIds:@[]];

if ([write isOverwrite]) {
FFLog(@"I-RDB038001", @"Restoring overwrite with id %ld",
(long)write.writeId);
Expand All @@ -250,7 +248,8 @@ - (void)restoreWrites {
withCallback:callback];
id<FNode> resolved =
[FServerValues resolveDeferredValueSnapshot:write.overwrite
withExisting:existing
withExisting:self.serverSyncTree
atPath:write.path
serverValues:serverValues];
[self.serverSyncTree applyUserOverwriteAtPath:write.path
newData:resolved
Expand All @@ -262,10 +261,11 @@ - (void)restoreWrites {
[self.connection mergeData:[write.merge valForExport:YES]
forPath:[write.path toString]
withCallback:callback];
FCompoundWrite *resolved =
[FServerValues resolveDeferredValueCompoundWrite:write.merge
withExisting:existing
serverValues:serverValues];
FCompoundWrite *resolved = [FServerValues
resolveDeferredValueCompoundWrite:write.merge
withSyncTree:self.serverSyncTree
atPath:write.path
serverValues:serverValues];
[self.serverSyncTree applyUserMergeAtPath:write.path
changedChildren:resolved
writeId:writeId];
Expand Down Expand Up @@ -366,11 +366,10 @@ - (void)update:(FPath *)path
[values description]);
NSDictionary *serverValues =
[FServerValues generateServerValues:self.serverClock];
id<FNode> existing = [self.serverSyncTree calcCompleteEventCacheAtPath:path
excludeWriteIds:@[]];
FCompoundWrite *resolved =
[FServerValues resolveDeferredValueCompoundWrite:nodes
withExisting:existing
withSyncTree:self.serverSyncTree
atPath:path
serverValues:serverValues];

if (!resolved.isEmpty) {
Expand Down
18 changes: 11 additions & 7 deletions Firebase/Database/Core/FServerValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@
#import "FCompoundWrite.h"
#import "FNode.h"
#import "FSparseSnapshotTree.h"
#import "FSyncTree.h"
#import <Foundation/Foundation.h>

@interface FServerValues : NSObject

+ (NSDictionary *)generateServerValues:(id<FClock>)clock;
+ (id)resolveDeferredValueCompoundWrite:(FCompoundWrite *)write
withExisting:(id<FNode>)existing
serverValues:(NSDictionary *)serverValues;

+ (FCompoundWrite *)resolveDeferredValueCompoundWrite:(FCompoundWrite *)write
withSyncTree:(FSyncTree *)tree
atPath:(FPath *)path
serverValues:
(NSDictionary *)serverValues;
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
withExisting:(id<FNode>)existing
atPath:(FPath *)path
serverValues:(NSDictionary *)serverValues;
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
withExisting:(id<FNode>)existing
serverValues:(NSDictionary *)serverValues;
+ (id)resolveDeferredValueTree:(FSparseSnapshotTree *)tree
withExisting:(id<FNode>)node
serverValues:(NSDictionary *)serverValues;

@end
123 changes: 97 additions & 26 deletions Firebase/Database/Core/FServerValues.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,77 @@ BOOL canBeRepresentedAsLong(NSNumber *num) {
return YES;
}

// Running through CompoundWrites for all update paths has been shown to
// be a 20% pessimization in microbenchmarks. This is because it slows
// down by O(N) of the write queue length. To eliminate the performance
// hit, we wrap around existing data of either snapshot or CompoundWrite
// (allowing us to share code) and read from the CompoundWrite only when/where
// we need to calculate an incremented value's prior state.
@protocol ValueProvider <NSObject>
- (id<ValueProvider>)getChild:(NSString *)pathSegment;
- (id<FNode>)value;
@end

@interface DeferredValueProvider : NSObject <ValueProvider>
- (instancetype)initWithSyncTree:(FSyncTree *)tree atPath:(FPath *)path;
- (id<ValueProvider>)getChild:(NSString *)pathSegment;
- (id<FNode>)value;
@property FPath *path;
@property FSyncTree *tree;
@end

@interface ExistingValueProvider : NSObject <ValueProvider>
- (instancetype)initWithSnapshot:(id<FNode>)snapshot;
- (id<ValueProvider>)getChild:(NSString *)pathSegment;
- (id<FNode>)value;
@property id<FNode> snapshot;
@end

@implementation DeferredValueProvider
- (instancetype)initWithSyncTree:(FSyncTree *)tree atPath:(FPath *)path {
self.tree = tree;
self.path = path;
return self;
}

- (id<ValueProvider>)getChild:(NSString *)pathSegment {
FPath *child = [self.path childFromString:pathSegment];
return [[DeferredValueProvider alloc] initWithSyncTree:self.tree
atPath:child];
}

- (id<FNode>)value {
return [self.tree calcCompleteEventCacheAtPath:self.path
excludeWriteIds:@[]];
}
@end

@implementation ExistingValueProvider
- (instancetype)initWithSnapshot:(id<FNode>)snapshot {
self.snapshot = snapshot;
return self;
}

- (id<ValueProvider>)getChild:(NSString *)pathSegment {
return [[ExistingValueProvider alloc]
initWithSnapshot:[self.snapshot getImmediateChild:pathSegment]];
}

- (id<FNode>)value {
return self.snapshot.val;
}
@end

@interface FServerValues ()
+ (id)resolveScalarServerOp:(NSString *)op
withServerValues:(NSDictionary *)serverValues;
+ (id)resolveComplexServerOp:(NSDictionary *)op
withExisting:(id<FNode>)existing
withExisting:(id<ValueProvider>)existing
serverValues:(NSDictionary *)serverValues;
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
withJITExisting:(id<ValueProvider>)existing
serverValues:(NSDictionary *)serverValues;

@end

@implementation FServerValues
Expand All @@ -54,7 +119,7 @@ + (NSDictionary *)generateServerValues:(id<FClock>)clock {
}

+ (id)resolveDeferredValue:(id)val
withExisting:(id<FNode>)existing
withExisting:(id<ValueProvider>)existing
serverValues:(NSDictionary *)serverValues {
if (![val isKindOfClass:[NSDictionary class]]) {
return val;
Expand All @@ -81,7 +146,7 @@ + (id)resolveScalarServerOp:(NSString *)op
}

+ (id)resolveComplexServerOp:(NSDictionary *)op
withExisting:(id<FNode>)existing
withExisting:(id<ValueProvider>)jitExisting
serverValues:(NSDictionary *)serverValues {
// Only increment is supported as of now
if (op[kIncrement] == nil) {
Expand All @@ -90,6 +155,7 @@ + (id)resolveComplexServerOp:(NSDictionary *)op

// Incrementing a non-number sets the value to the incremented amount
NSNumber *delta = op[kIncrement];
id<FNode> existing = jitExisting.value;
if (![existing isLeafNode]) {
return delta;
}
Expand Down Expand Up @@ -117,14 +183,18 @@ + (id)resolveComplexServerOp:(NSDictionary *)op
}

+ (FCompoundWrite *)resolveDeferredValueCompoundWrite:(FCompoundWrite *)write
withExisting:(id<FNode>)existing
withSyncTree:(FSyncTree *)tree
atPath:(FPath *)path
serverValues:
(NSDictionary *)serverValues {
__block FCompoundWrite *resolved = write;
[write enumerateWrites:^(FPath *path, id<FNode> node, BOOL *stop) {
[write enumerateWrites:^(FPath *subPath, id<FNode> node, BOOL *stop) {
id<ValueProvider> existing =
[[DeferredValueProvider alloc] initWithSyncTree:tree
atPath:[path child:subPath]];
id<FNode> resolvedNode =
[FServerValues resolveDeferredValueSnapshot:node
withExisting:existing
withJITExisting:existing
serverValues:serverValues];
// Node actually changed, use pointer inequality here
if (resolvedNode != node) {
Expand All @@ -134,32 +204,33 @@ + (FCompoundWrite *)resolveDeferredValueCompoundWrite:(FCompoundWrite *)write
return resolved;
}

+ (id)resolveDeferredValueTree:(FSparseSnapshotTree *)tree
withExisting:(id<FNode>)existing
serverValues:(NSDictionary *)serverValues {
FSparseSnapshotTree *resolvedTree = [[FSparseSnapshotTree alloc] init];
[tree
forEachTreeAtPath:[FPath empty]
do:^(FPath *path, id<FNode> node) {
[resolvedTree
rememberData:
[FServerValues
resolveDeferredValueSnapshot:node
withExisting:
[existing
getChild:path]
serverValues:serverValues]
onPath:path];
}];
return resolvedTree;
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
withExisting:(FSyncTree *)tree
atPath:(FPath *)path
serverValues:(NSDictionary *)serverValues {
id<ValueProvider> jitExisting =
[[DeferredValueProvider alloc] initWithSyncTree:tree atPath:path];
return [FServerValues resolveDeferredValueSnapshot:node
withJITExisting:jitExisting
serverValues:serverValues];
}

+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
withExisting:(id<FNode>)existing
serverValues:(NSDictionary *)serverValues {
id<ValueProvider> jitExisting =
[[ExistingValueProvider alloc] initWithSnapshot:existing];
return [FServerValues resolveDeferredValueSnapshot:node
withJITExisting:jitExisting
serverValues:serverValues];
}

+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
withJITExisting:(id<ValueProvider>)existing
serverValues:(NSDictionary *)serverValues {
id priorityVal =
[FServerValues resolveDeferredValue:[[node getPriority] val]
withExisting:existing.getPriority
withExisting:[existing getChild:@".priority"]
serverValues:serverValues];
id<FNode> priority = [FSnapshotUtilities nodeFrom:priorityVal];

Expand All @@ -184,7 +255,7 @@ + (id)resolveDeferredValueTree:(FSparseSnapshotTree *)tree
id<FNode> childNode, BOOL *stop) {
id newChildNode = [FServerValues
resolveDeferredValueSnapshot:childNode
withExisting:[existing getImmediateChild:childKey]
withJITExisting:[existing getChild:childKey]
serverValues:serverValues];
if (![newChildNode isEqual:childNode]) {
newNode = [newNode updateImmediateChild:childKey
Expand Down
8 changes: 4 additions & 4 deletions Firebase/Database/Core/FSyncTree.m
Original file line number Diff line number Diff line change
Expand Up @@ -231,19 +231,19 @@ - (NSArray *)ackUserWriteWithWriteId:(NSInteger)writeId
if (!revert) {
NSDictionary *serverValues =
[FServerValues generateServerValues:clock];
id<FNode> existing = [self calcCompleteEventCacheAtPath:write.path
excludeWriteIds:@[]];
if ([write isOverwrite]) {
id<FNode> resolvedNode =
[FServerValues resolveDeferredValueSnapshot:write.overwrite
withExisting:existing
withExisting:self
atPath:write.path
serverValues:serverValues];
[self.persistenceManager applyUserWrite:resolvedNode
toServerCacheAtPath:write.path];
} else {
FCompoundWrite *resolvedMerge = [FServerValues
resolveDeferredValueCompoundWrite:write.merge
withExisting:existing
withSyncTree:self
atPath:write.path
serverValues:serverValues];
[self.persistenceManager applyUserMerge:resolvedMerge
toServerCacheAtPath:write.path];
Expand Down