Skip to content

Commit 578dc58

Browse files
maneeshtjmwski
andauthored
Fix for repoGet request (#6273)
Fixed issue where `get()` saved results incorrectly for non-default queries. Co-authored-by: Jan Wyszynski <[email protected]>
1 parent 8e952e2 commit 578dc58

File tree

11 files changed

+274
-41
lines changed

11 files changed

+274
-41
lines changed

.changeset/rotten-tables-brush.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@firebase/database": patch
3+
---
4+
5+
Fixed issue where `get()` saved results incorrectly for non-default queries.

.vscode/launch.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
"type": "node",
99
"request": "launch",
1010
"name": "RTDB Unit Tests (Node)",
11-
"program": "${workspaceRoot}/packages/firebase/node_modules/.bin/_mocha",
11+
"program": "${workspaceRoot}/node_modules/.bin/_mocha",
1212
"cwd": "${workspaceRoot}/packages/database",
1313
"args": [
1414
"test/{,!(browser)/**/}*.test.ts",
15-
"--file", "index.node.ts",
16-
"--config", "../../config/mocharc.node.js"
15+
"--file", "src/index.node.ts",
16+
"--config", "../../config/mocharc.node.js",
1717
],
1818
"env": {
19+
"TS_NODE_FILES":true,
1920
"TS_NODE_CACHE": "NO",
2021
"TS_NODE_COMPILER_OPTIONS" : "{\"module\":\"commonjs\"}"
2122
},
@@ -30,7 +31,7 @@
3031
"cwd": "${workspaceRoot}/packages/firestore",
3132
"args": [
3233
"--require", "babel-register.js",
33-
"--require", "index.node.ts",
34+
"--require", "src/index.node.ts",
3435
"--timeout", "5000",
3536
"test/{,!(browser|integration)/**/}*.test.ts",
3637
"--exit"

packages/database/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@
5656
"@firebase/app": "0.7.26",
5757
"rollup": "2.72.1",
5858
"rollup-plugin-typescript2": "0.31.2",
59-
"typescript": "4.2.2"
59+
"typescript": "4.2.2",
60+
"uuid": "^8.3.2"
6061
},
6162
"repository": {
6263
"directory": "packages/database",

packages/database/src/core/PersistentConnection.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,6 @@ export class PersistentConnection extends ServerActions {
206206
onComplete: (message: { [k: string]: unknown }) => {
207207
const payload = message['d'] as string;
208208
if (message['s'] === 'ok') {
209-
this.onDataUpdate_(
210-
request['p'],
211-
payload,
212-
/*isMerge*/ false,
213-
/*tag*/ null
214-
);
215209
deferred.resolve(payload);
216210
} else {
217211
deferred.reject(payload);
@@ -265,7 +259,7 @@ export class PersistentConnection extends ServerActions {
265259
);
266260
assert(
267261
!this.listens.get(pathString)!.has(queryId),
268-
'listen() called twice for same path/queryId.'
262+
`listen() called twice for same path/queryId.`
269263
);
270264
const listenSpec: ListenSpec = {
271265
onComplete,

packages/database/src/core/Repo.ts

+28-7
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ import {
6060
syncTreeApplyUserOverwrite,
6161
syncTreeCalcCompleteEventCache,
6262
syncTreeGetServerValue,
63-
syncTreeRemoveEventRegistration
63+
syncTreeRemoveEventRegistration,
64+
syncTreeRegisterQuery
6465
} from './SyncTree';
6566
import { Indexable } from './util/misc';
6667
import {
@@ -466,16 +467,36 @@ export function repoGetValue(repo: Repo, query: QueryContext): Promise<Node> {
466467
}
467468
return repo.server_.get(query).then(
468469
payload => {
469-
const node = nodeFromJSON(payload as string).withIndex(
470+
const node = nodeFromJSON(payload).withIndex(
470471
query._queryParams.getIndex()
471472
);
472-
const events = syncTreeApplyServerOverwrite(
473+
// if this is a filtered query, then overwrite at path
474+
if (query._queryParams.loadsAllData()) {
475+
syncTreeApplyServerOverwrite(repo.serverSyncTree_, query._path, node);
476+
} else {
477+
// Simulate `syncTreeAddEventRegistration` without events/listener setup.
478+
// We do this (along with the syncTreeRemoveEventRegistration` below) so that
479+
// `repoGetValue` results have the same cache effects as initial listener(s)
480+
// updates.
481+
const tag = syncTreeRegisterQuery(repo.serverSyncTree_, query);
482+
syncTreeApplyTaggedQueryOverwrite(
483+
repo.serverSyncTree_,
484+
query._path,
485+
node,
486+
tag
487+
);
488+
// Call `syncTreeRemoveEventRegistration` with a null event registration, since there is none.
489+
// Note: The below code essentially unregisters the query and cleans up any views/syncpoints temporarily created above.
490+
}
491+
const cancels = syncTreeRemoveEventRegistration(
473492
repo.serverSyncTree_,
474-
query._path,
475-
node
493+
query,
494+
null
476495
);
477-
eventQueueRaiseEventsAtPath(repo.eventQueue_, query._path, events);
478-
return Promise.resolve(node);
496+
if (cancels.length > 0) {
497+
repoLog(repo, 'unexpected cancel events in repoGetValue');
498+
}
499+
return node;
479500
},
480501
err => {
481502
repoLog(repo, 'get for query ' + stringify(query) + ' failed: ' + err);

packages/database/src/core/SyncTree.ts

+67-8
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,38 @@ export function syncTreeRemoveEventRegistration(
423423
return cancelEvents;
424424
}
425425

426+
/**
427+
* This function was added to support non-listener queries,
428+
* specifically for use in repoGetValue. It sets up all the same
429+
* local cache data-structures (SyncPoint + View) that are
430+
* needed for listeners without installing an event registration.
431+
* If `query` is not `loadsAllData`, it will also provision a tag for
432+
* the query so that query results can be merged into the sync
433+
* tree using existing logic for tagged listener queries.
434+
*
435+
* @param syncTree - Synctree to add the query to.
436+
* @param query - Query to register
437+
* @returns tag as a string if query is not a default query, null if query is not.
438+
*/
439+
export function syncTreeRegisterQuery(syncTree: SyncTree, query: QueryContext) {
440+
const { syncPoint, serverCache, writesCache, serverCacheComplete } =
441+
syncTreeRegisterSyncPoint(query, syncTree);
442+
const view = syncPointGetView(
443+
syncPoint,
444+
query,
445+
writesCache,
446+
serverCache,
447+
serverCacheComplete
448+
);
449+
if (!syncPoint.views.has(query._queryIdentifier)) {
450+
syncPoint.views.set(query._queryIdentifier, view);
451+
}
452+
if (!query._queryParams.loadsAllData()) {
453+
return syncTreeTagForQuery_(syncTree, query);
454+
}
455+
return null;
456+
}
457+
426458
/**
427459
* Apply new server data for the specified tagged query.
428460
*
@@ -483,15 +515,14 @@ export function syncTreeApplyTaggedQueryMerge(
483515
}
484516

485517
/**
486-
* Add an event callback for the specified query.
487-
*
488-
* @returns Events to raise.
518+
* Creates a new syncpoint for a query and creates a tag if the view doesn't exist.
519+
* Extracted from addEventRegistration to allow `repoGetValue` to properly set up the SyncTree
520+
* without actually listening on a query.
489521
*/
490-
export function syncTreeAddEventRegistration(
491-
syncTree: SyncTree,
522+
export function syncTreeRegisterSyncPoint(
492523
query: QueryContext,
493-
eventRegistration: EventRegistration
494-
): Event[] {
524+
syncTree: SyncTree
525+
) {
495526
const path = query._path;
496527

497528
let serverCache: Node | null = null;
@@ -550,6 +581,35 @@ export function syncTreeAddEventRegistration(
550581
syncTree.tagToQueryMap.set(tag, queryKey);
551582
}
552583
const writesCache = writeTreeChildWrites(syncTree.pendingWriteTree_, path);
584+
return {
585+
syncPoint,
586+
writesCache,
587+
serverCache,
588+
serverCacheComplete,
589+
foundAncestorDefaultView,
590+
viewAlreadyExists
591+
};
592+
}
593+
594+
/**
595+
* Add an event callback for the specified query.
596+
*
597+
* @returns Events to raise.
598+
*/
599+
export function syncTreeAddEventRegistration(
600+
syncTree: SyncTree,
601+
query: QueryContext,
602+
eventRegistration: EventRegistration
603+
): Event[] {
604+
const {
605+
syncPoint,
606+
serverCache,
607+
writesCache,
608+
serverCacheComplete,
609+
viewAlreadyExists,
610+
foundAncestorDefaultView
611+
} = syncTreeRegisterSyncPoint(query, syncTree);
612+
553613
let events = syncPointAddEventRegistration(
554614
syncPoint,
555615
query,
@@ -937,7 +997,6 @@ function syncTreeSetupListener_(
937997
const path = query._path;
938998
const tag = syncTreeTagForQuery_(syncTree, query);
939999
const listener = syncTreeCreateListenerForView_(syncTree, view);
940-
9411000
const events = syncTree.listenProvider_.startListening(
9421001
syncTreeQueryForListening_(query),
9431002
tag,

packages/database/src/core/util/Path.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ export function pathEquals(path: Path, other: Path): boolean {
230230
}
231231

232232
/**
233-
* @returns True if this path is a parent (or the same as) other
233+
* @returns True if this path is a parent of (or the same as) other
234234
*/
235235
export function pathContains(path: Path, other: Path): boolean {
236236
let i = path.pieceNum_;

0 commit comments

Comments
 (0)