Skip to content

Commit bd03a98

Browse files
committed
WIP: fix transaction bug
1 parent 6935085 commit bd03a98

File tree

4 files changed

+61
-50
lines changed

4 files changed

+61
-50
lines changed

src/database.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Reference } from "./database/api/Reference";
2222
import { enableLogging } from "./database/core/util/util";
2323
import { RepoManager } from "./database/core/RepoManager";
2424
import * as INTERNAL from './database/api/internal';
25+
import * as TEST_ACCESS from './database/api/test_access';
2526

2627
export function registerDatabase(instance) {
2728
// Register the Database Service with the 'firebase' namespace.
@@ -36,6 +37,7 @@ export function registerDatabase(instance) {
3637
enableLogging,
3738
INTERNAL,
3839
ServerValue: Database.ServerValue,
40+
TEST_ACCESS
3941
}
4042
);
4143
}

src/database/core/CompoundWrite.ts

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ImmutableTree } from "./util/ImmutableTree";
22
import { Path } from "./util/Path";
33
import { forEach } from "../../utils/obj";
4-
import { NamedNode } from "./snap/Node";
4+
import { Node, NamedNode } from "./snap/Node";
55
import { PRIORITY_INDEX } from "./snap/indexes/PriorityIndex";
66
import { assert } from "../../utils/assert";
77

@@ -15,32 +15,25 @@ import { assert } from "../../utils/assert";
1515
* @param {!ImmutableTree.<!Node>} writeTree
1616
*/
1717
export class CompoundWrite {
18-
writeTree_;
19-
constructor(writeTree) {
20-
/**
21-
* @type {!ImmutableTree.<!Node>}
22-
* @private
23-
*/
24-
this.writeTree_ = writeTree;
25-
};
18+
constructor(private writeTree_: ImmutableTree) {};
2619
/**
2720
* @type {!CompoundWrite}
2821
*/
29-
static Empty = new CompoundWrite(
30-
/** @type {!ImmutableTree.<!Node>} */ (new ImmutableTree(null))
31-
);
22+
static Empty = new CompoundWrite(new ImmutableTree(null));
23+
3224
/**
3325
* @param {!Path} path
3426
* @param {!Node} node
3527
* @return {!CompoundWrite}
3628
*/
37-
addWrite(path, node) {
29+
addWrite(path: Path, node: Node): CompoundWrite {
3830
if (path.isEmpty()) {
3931
return new CompoundWrite(new ImmutableTree(node));
4032
} else {
4133
var rootmost = this.writeTree_.findRootMostValueAndPath(path);
4234
if (rootmost != null) {
43-
var rootMostPath = rootmost.path, value = rootmost.value;
35+
var rootMostPath = rootmost.path
36+
var value = rootmost.value;
4437
var relativePath = Path.relativePath(rootMostPath, path);
4538
value = value.updateChild(relativePath, node);
4639
return new CompoundWrite(this.writeTree_.set(rootMostPath, value));
@@ -57,8 +50,8 @@ export class CompoundWrite {
5750
* @param {!Object.<string, !Node>} updates
5851
* @return {!CompoundWrite}
5952
*/
60-
addWrites(path, updates) {
61-
var newWrite = <any>this;
53+
addWrites(path: Path, updates: { [name: string]: Node }): CompoundWrite {
54+
var newWrite = <CompoundWrite>this;
6255
forEach(updates, function(childKey, node) {
6356
newWrite = newWrite.addWrite(path.child(childKey), node);
6457
});
@@ -72,7 +65,7 @@ export class CompoundWrite {
7265
* @param {!Path} path The path at which a write and all deeper writes should be removed
7366
* @return {!CompoundWrite} The new CompoundWrite with the removed path
7467
*/
75-
removeWrite(path) {
68+
removeWrite(path: Path): CompoundWrite {
7669
if (path.isEmpty()) {
7770
return CompoundWrite.Empty;
7871
} else {
@@ -88,7 +81,7 @@ export class CompoundWrite {
8881
* @param {!Path} path The path to check for
8982
* @return {boolean} Whether there is a complete write at that path
9083
*/
91-
hasCompleteWrite(path) {
84+
hasCompleteWrite(path: Path): boolean {
9285
return this.getCompleteNode(path) != null;
9386
};
9487

@@ -99,11 +92,10 @@ export class CompoundWrite {
9992
* @param {!Path} path The path to get a complete write
10093
* @return {?Node} The node if complete at that path, or null otherwise.
10194
*/
102-
getCompleteNode(path) {
95+
getCompleteNode(path: Path): Node {
10396
var rootmost = this.writeTree_.findRootMostValueAndPath(path);
10497
if (rootmost != null) {
105-
return this.writeTree_.get(rootmost.path).getChild(
106-
Path.relativePath(rootmost.path, path));
98+
return this.writeTree_.get(rootmost.path).getChild(Path.relativePath(rootmost.path, path));
10799
} else {
108100
return null;
109101
}
@@ -114,7 +106,7 @@ export class CompoundWrite {
114106
*
115107
* @return {!Array.<NamedNode>} A list of all complete children.
116108
*/
117-
getCompleteChildren() {
109+
getCompleteChildren(): Array<NamedNode> {
118110
var children = [];
119111
var node = this.writeTree_.value;
120112
if (node != null) {
@@ -139,7 +131,7 @@ export class CompoundWrite {
139131
* @param {!Path} path
140132
* @return {!CompoundWrite}
141133
*/
142-
childCompoundWrite(path) {
134+
childCompoundWrite(path: Path) {
143135
if (path.isEmpty()) {
144136
return this;
145137
} else {
@@ -166,7 +158,7 @@ export class CompoundWrite {
166158
* @param {!Node} node The node to apply this CompoundWrite to
167159
* @return {!Node} The node with all writes applied
168160
*/
169-
apply(node) {
161+
apply(node: Node) {
170162
return CompoundWrite.applySubtreeWrite_(Path.Empty, this.writeTree_, node);
171163
};
172164

@@ -177,7 +169,7 @@ export class CompoundWrite {
177169
* @return {!Node}
178170
* @private
179171
*/
180-
static applySubtreeWrite_ = function(relativePath, writeTree, node) {
172+
static applySubtreeWrite_ = function(relativePath: Path, writeTree: ImmutableTree, node: Node) {
181173
if (writeTree.value != null) {
182174
// Since there a write is always a leaf, we're done here
183175
return node.updateChild(relativePath, writeTree.value);
@@ -195,8 +187,7 @@ export class CompoundWrite {
195187
});
196188
// If there was a priority write, we only apply it if the node is not empty
197189
if (!node.getChild(relativePath).isEmpty() && priorityWrite !== null) {
198-
node = node.updateChild(relativePath.child('.priority'),
199-
/** @type {!Node} */ (priorityWrite));
190+
node = node.updateChild(relativePath.child('.priority'), priorityWrite);
200191
}
201192
return node;
202193
}

src/database/core/Repo_transaction.ts

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { DataSnapshot } from "../api/DataSnapshot";
44
import { Path } from "./util/Path";
55
import { Tree } from "./util/Tree";
66
import { PRIORITY_INDEX } from "./snap/indexes/PriorityIndex";
7+
import { Node } from "./snap/Node";
78
import {
89
LUIDGenerator,
910
warn,
@@ -94,6 +95,22 @@ declare module './Repo' {
9495
}
9596
}
9697

98+
type Transaction = {
99+
path: Path,
100+
update: Function,
101+
onComplete: Function,
102+
status: number,
103+
order: number,
104+
applyLocally: boolean,
105+
retryCount: number,
106+
unwatcher: Function,
107+
abortReason: any,
108+
currentWriteId: any,
109+
currentInputSnapshot: any,
110+
currentOutputSnapshotRaw: any,
111+
currentOutputSnapshotResolved: any
112+
}
113+
97114
/**
98115
* Creates a new transaction, adds it to the transactions we're tracking, and sends it to the server if possible.
99116
*
@@ -102,7 +119,10 @@ declare module './Repo' {
102119
* @param {?function(?Error, boolean, ?DataSnapshot)} onComplete Completion callback.
103120
* @param {boolean} applyLocally Whether or not to make intermediate results visible
104121
*/
105-
(Repo.prototype as any).startTransaction = function(path, transactionUpdate, onComplete, applyLocally) {
122+
(Repo.prototype as any).startTransaction = function(path: Path,
123+
transactionUpdate: () => any,
124+
onComplete: (Error, boolean, DataSnapshot) => any,
125+
applyLocally: boolean) {
106126
this.log_('transaction on ' + path);
107127

108128
// Add a watch to make sure we get server updates.
@@ -112,7 +132,7 @@ declare module './Repo' {
112132
var unwatcher = function() { watchRef.off('value', valueCallback); };
113133

114134
// Initialize transaction.
115-
var transaction = /** @type {Transaction} */ ({
135+
var transaction: Transaction = {
116136
path: path,
117137
update: transactionUpdate,
118138
onComplete: onComplete,
@@ -142,7 +162,7 @@ declare module './Repo' {
142162
currentOutputSnapshotRaw: null,
143163

144164
currentOutputSnapshotResolved: null
145-
});
165+
};
146166

147167

148168
// Run transaction initially.
@@ -156,9 +176,8 @@ declare module './Repo' {
156176
transaction.currentOutputSnapshotResolved = null;
157177
if (transaction.onComplete) {
158178
// We just set the input snapshot, so this cast should be safe
159-
var snapshot = new DataSnapshot(/** @type {!Node} */ (transaction.currentInputSnapshot),
160-
new Reference(this, transaction.path), PRIORITY_INDEX);
161-
transaction.onComplete(/*error=*/null, /*committed=*/false, snapshot);
179+
var snapshot = new DataSnapshot(transaction.currentInputSnapshot, new Reference(this, transaction.path), PRIORITY_INDEX);
180+
transaction.onComplete(null, false, snapshot);
162181
}
163182
} else {
164183
validateFirebaseData('transaction failed: Data returned ', newVal, transaction.path);
@@ -192,8 +211,7 @@ declare module './Repo' {
192211
transaction.currentOutputSnapshotResolved = newNode;
193212
transaction.currentWriteId = this.getNextWriteId_();
194213

195-
var events = this.serverSyncTree_.applyUserOverwrite(path, newNode, transaction.currentWriteId,
196-
transaction.applyLocally);
214+
var events = this.serverSyncTree_.applyUserOverwrite(path, newNode, transaction.currentWriteId, transaction.applyLocally);
197215
this.eventQueue_.raiseEventsForChangedPath(path, events);
198216

199217
this.sendReadyTransactions_();
@@ -206,7 +224,7 @@ declare module './Repo' {
206224
* @return {Node}
207225
* @private
208226
*/
209-
(Repo.prototype as any).getLatestState_ = function(path, excludeSets) {
227+
(Repo.prototype as any).getLatestState_ = function(path: Path, excludeSets: [number]): Node {
210228
return this.serverSyncTree_.calcCompleteEventCache(path, excludeSets) || ChildrenNode.EMPTY_NODE;
211229
};
212230

@@ -221,11 +239,11 @@ declare module './Repo' {
221239
* @param {Tree.<Array.<Transaction>>=} opt_node transactionQueueTree node to start at.
222240
* @private
223241
*/
224-
(Repo.prototype as any).sendReadyTransactions_ = function(opt_node) {
225-
var node = /** @type {!Tree.<Array.<!Transaction>>} */ (opt_node || this.transactionQueueTree_);
242+
(Repo.prototype as any).sendReadyTransactions_ = function(node?) {
243+
var node = /** @type {!Tree.<Array.<!Transaction>>} */ (node || this.transactionQueueTree_);
226244

227245
// Before recursing, make sure any completed transactions are removed.
228-
if (!opt_node) {
246+
if (!node) {
229247
this.pruneCompletedTransactionsBelowNode_(node);
230248
}
231249

@@ -257,7 +275,7 @@ declare module './Repo' {
257275
* @param {!Array.<Transaction>} queue Queue of transactions under the specified location.
258276
* @private
259277
*/
260-
(Repo.prototype as any).sendTransactionQueue_ = function(path, queue) {
278+
(Repo.prototype as any).sendTransactionQueue_ = function(path: Path, queue: Array<Transaction>) {
261279
// Mark transactions as sent and increment retry count!
262280
var setsToIgnore = queue.map(function(txn) { return txn.currentWriteId; });
263281
var latestState = this.getLatestState_(path, setsToIgnore);
@@ -345,7 +363,7 @@ declare module './Repo' {
345363
* @return {!Path} The rootmost path that was affected by rerunning transactions.
346364
* @private
347365
*/
348-
(Repo.prototype as any).rerunTransactions_ = function(changedPath) {
366+
(Repo.prototype as any).rerunTransactions_ = function(changedPath: Path) {
349367
var rootMostTransactionNode = this.getAncestorTransactionNode_(changedPath);
350368
var path = rootMostTransactionNode.path();
351369

@@ -363,7 +381,7 @@ declare module './Repo' {
363381
* @param {!Path} path The path the queue is for.
364382
* @private
365383
*/
366-
(Repo.prototype as any).rerunTransactionQueue_ = function(queue, path) {
384+
(Repo.prototype as any).rerunTransactionQueue_ = function(queue: Array<Transaction>, path: Path) {
367385
if (queue.length === 0) {
368386
return; // Nothing to do!
369387
}
@@ -413,7 +431,7 @@ declare module './Repo' {
413431
transaction.currentOutputSnapshotResolved = newNodeResolved;
414432
transaction.currentWriteId = this.getNextWriteId_();
415433
// Mutates setsToIgnore in place
416-
setsToIgnore.splice(setsToIgnore.indexOf(oldWriteId));
434+
setsToIgnore.splice(setsToIgnore.indexOf(oldWriteId), 1);
417435
events = events.concat(
418436
this.serverSyncTree_.applyUserOverwrite(transaction.path, newNodeResolved, transaction.currentWriteId,
419437
transaction.applyLocally)
@@ -473,7 +491,7 @@ declare module './Repo' {
473491
* @return {!Tree.<Array.<!Transaction>>} The rootmost node with a transaction.
474492
* @private
475493
*/
476-
(Repo.prototype as any).getAncestorTransactionNode_ = function(path) {
494+
(Repo.prototype as any).getAncestorTransactionNode_ = function(path: Path): Tree {
477495
var front;
478496

479497
// Start at the root and walk deeper into the tree towards path until we find a node with pending transactions.
@@ -494,7 +512,7 @@ declare module './Repo' {
494512
* @return {Array.<Transaction>} The generated queue.
495513
* @private
496514
*/
497-
(Repo.prototype as any).buildTransactionQueue_ = function(transactionNode) {
515+
(Repo.prototype as any).buildTransactionQueue_ = function(transactionNode: Tree): Array<Transaction> {
498516
// Walk any child transaction queues and aggregate them into a single queue.
499517
var transactionQueue = [];
500518
this.aggregateTransactionQueuesForNode_(transactionNode, transactionQueue);
@@ -510,7 +528,7 @@ declare module './Repo' {
510528
* @param {Array.<Transaction>} queue
511529
* @private
512530
*/
513-
(Repo.prototype as any).aggregateTransactionQueuesForNode_ = function(node, queue) {
531+
(Repo.prototype as any).aggregateTransactionQueuesForNode_ = function(node: Tree, queue: Array<Transaction>) {
514532
var nodeQueue = node.getValue();
515533
if (nodeQueue !== null) {
516534
for (var i = 0; i < nodeQueue.length; i++) {
@@ -531,7 +549,7 @@ declare module './Repo' {
531549
* @param {!Tree.<Array.<!Transaction>>} node
532550
* @private
533551
*/
534-
(Repo.prototype as any).pruneCompletedTransactionsBelowNode_ = function(node) {
552+
(Repo.prototype as any).pruneCompletedTransactionsBelowNode_ = function(node: Tree) {
535553
var queue = node.getValue();
536554
if (queue) {
537555
var to = 0;
@@ -560,7 +578,7 @@ declare module './Repo' {
560578
* @return {!Path}
561579
* @private
562580
*/
563-
(Repo.prototype as any).abortTransactions_ = function(path) {
581+
(Repo.prototype as any).abortTransactions_ = function(path: Path) {
564582
var affectedPath = this.getAncestorTransactionNode_(path).path();
565583

566584
var transactionNode = this.transactionQueueTree_.subTree(path);
@@ -586,7 +604,7 @@ declare module './Repo' {
586604
* @param {!Tree.<Array.<Transaction>>} node Node to abort transactions for.
587605
* @private
588606
*/
589-
(Repo.prototype as any).abortTransactionsOnNode_ = function(node) {
607+
(Repo.prototype as any).abortTransactionsOnNode_ = function(node: Tree) {
590608
var queue = node.getValue();
591609
if (queue !== null) {
592610

src/database/core/snap/nodeFromJSON.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function nodeFromJSON(json, priority?) {
2222
return ChildrenNode.EMPTY_NODE;
2323
}
2424

25-
priority = priority || null;
25+
priority = priority !== undefined ? priority : null;
2626
if (typeof json === 'object' && '.priority' in json) {
2727
priority = json['.priority'];
2828
}

0 commit comments

Comments
 (0)