[go: nahoru, domu]

Skip to content

Commit

Permalink
Add an interface to ServerValues that lets us read synctrees just in …
Browse files Browse the repository at this point in the history
…time
  • Loading branch information
inlined committed Jan 7, 2020
1 parent 6104c68 commit 7933b20
Showing 1 changed file with 80 additions and 20 deletions.
100 changes: 80 additions & 20 deletions packages/database/src/core/util/ServerValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,53 @@ import { ChildrenNode } from '../snap/ChildrenNode';
import { SyncTree } from '../SyncTree';
import { Indexable } from './misc';

/* It's critical for performance that we not calculate actual values from a SyncTree
* unless and until the value is needed. Because we expose both a SyncTree and Node
* version of deferred value resolution, we ned a wrapper class that will let us share
* code.
*
* @see https://github.com/firebase/firebase-js-sdk/issues/2487
*/
interface DeferredExistingValue {
getImmediateChild(childName: string): DeferredExistingValue
node(): Node
}

class ExistingSnapshotValue implements DeferredExistingValue {
private node_: Node;
constructor(node: Node) {
this.node_ = node;
}

getImmediateChild(childName: string): DeferredExistingValue {
const child = this.node_.getImmediateChild(childName);
return new ExistingSnapshotValue(child);
}

node(): Node {
return this.node_;
}
}

class DeferredSyncTreeValue implements DeferredExistingValue {
private syncTree_: SyncTree;
private path_: Path;

constructor(syncTree: SyncTree, path: Path) {
this.syncTree_ = syncTree;
this.path_ = path;
}

getImmediateChild(childName: string): DeferredExistingValue {
const childPath = this.path_.child(childName);
return new DeferredSyncTreeValue(this.syncTree_, childPath);
}

node(): Node {
return this.syncTree_.calcCompleteEventCache(this.path_);
}
}

/**
* Generate placeholders for deferred values.
* @param {?Object} values
Expand All @@ -48,9 +95,9 @@ export const generateWithValues = function(
* @param {!Object} serverValues
* @return {!(string|number|boolean)}
*/
export const resolveDeferredValue = function(
export const resolveDeferredLeafValue = function(
value: { [k: string]: unknown } | string | number | boolean,
existing: Node,
existingVal: DeferredExistingValue,
serverValues: { [k: string]: unknown }
): string | number | boolean {
if (!value || typeof value !== 'object') {
Expand All @@ -59,17 +106,17 @@ export const resolveDeferredValue = function(
assert('.sv' in value, 'Unexpected leaf node or priority contents');

if (typeof value['.sv'] === 'string') {
return resolveScalarDeferredValue(value['.sv'], existing, serverValues);
return resolveScalarDeferredValue(value['.sv'], existingVal, serverValues);
} else if (typeof value['.sv'] === 'object') {
return resolveComplexDeferredValue(value['.sv'], existing, serverValues);
return resolveComplexDeferredValue(value['.sv'], existingVal, serverValues);
} else {
assert(false, 'Unexpected server value: ' + JSON.stringify(value, null, 2));
}
};

const resolveScalarDeferredValue = function(
op: string,
existing: Node,
existing: DeferredExistingValue,
serverValues: { [k: string]: unknown }
): string | number | boolean {
switch (op) {
Expand All @@ -82,7 +129,7 @@ const resolveScalarDeferredValue = function(

const resolveComplexDeferredValue = function(
op: object,
existing: Node,
existing: DeferredExistingValue,
unused: { [k: string]: unknown }
): string | number | boolean {
if (!op.hasOwnProperty('increment')) {
Expand All @@ -93,12 +140,18 @@ const resolveComplexDeferredValue = function(
assert(false, 'Unexpected increment value: ' + delta);
}

const existingNode = existing.node();
assert(
existingNode !== null && typeof existingNode !== 'undefined',
'Expected ChildrenNode.EMPTY_NODE for nulls'
);

// Incrementing a non-number sets the value to the incremented amount
if (!existing.isLeafNode()) {
if (!existingNode.isLeafNode()) {
return delta;
}

const leaf = existing as LeafNode;
const leaf = existingNode as LeafNode;
const existingVal = leaf.getValue();
if (typeof existingVal !== 'number') {
return delta;
Expand All @@ -122,14 +175,10 @@ export const resolveDeferredValueTree = function(
): SparseSnapshotTree {
const resolvedTree = new SparseSnapshotTree();
tree.forEachTree(new Path(''), (path, node) => {
const existing = syncTree.calcCompleteEventCache(path);
assert(
existing !== null && typeof existing !== 'undefined',
'Expected ChildrenNode.EMPTY_NODE for nulls'
);
const deferredExisting = new DeferredSyncTreeValue(syncTree, path);
resolvedTree.remember(
path,
resolveDeferredValueSnapshot(node, existing, serverValues)
resolveDeferredValue(node, deferredExisting, serverValues)
);
});
return resolvedTree;
Expand All @@ -148,24 +197,35 @@ export const resolveDeferredValueSnapshot = function(
existing: Node,
serverValues: Indexable
): Node {
return resolveDeferredValue(
node,
new ExistingSnapshotValue(existing),
serverValues);
};

function resolveDeferredValue(
node: Node,
existingVal: DeferredExistingValue,
serverValues: Indexable): Node
{
const rawPri = node.getPriority().val() as
| Indexable
| boolean
| null
| number
| string;
const priority = resolveDeferredValue(
const priority = resolveDeferredLeafValue(
rawPri,
existing.getPriority(),
existingVal.getImmediateChild('.priority'),
serverValues
);
let newNode: Node;

if (node.isLeafNode()) {
const leafNode = node as LeafNode;
const value = resolveDeferredValue(
const value = resolveDeferredLeafValue(
leafNode.getValue(),
existing,
existingVal,
serverValues
);
if (
Expand All @@ -183,9 +243,9 @@ export const resolveDeferredValueSnapshot = function(
newNode = newNode.updatePriority(new LeafNode(priority));
}
childrenNode.forEachChild(PRIORITY_INDEX, (childName, childNode) => {
const newChildNode = resolveDeferredValueSnapshot(
const newChildNode = resolveDeferredValue(
childNode,
existing.getImmediateChild(childName),
existingVal.getImmediateChild(childName),
serverValues
);
if (newChildNode !== childNode) {
Expand Down

0 comments on commit 7933b20

Please sign in to comment.