Skip to content

Add Query to Target ID Map #2179

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 4 commits into from
Sep 17, 2019
Merged
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
82 changes: 40 additions & 42 deletions packages/firestore/src/local/local_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { RemoteEvent, TargetChange } from '../remote/remote_event';
import { assert } from '../util/assert';
import * as log from '../util/log';
import * as objUtils from '../util/obj';
import { ObjectMap } from '../util/obj_map';

import { LocalDocumentsView } from './local_documents_view';
import { LocalViewChanges } from './local_view_changes';
Expand Down Expand Up @@ -162,6 +163,11 @@ export class LocalStore {
/** Maps a targetID to data about its query. */
private queryDataByTarget = {} as { [targetId: number]: QueryData };

/** Maps a query to its targetID. */
private targetIdByQuery = new ObjectMap<Query, TargetId>(q =>
q.canonicalId()
);

constructor(
/** Manages our in-memory or durable persistence. */
private persistence: Persistence,
Expand Down Expand Up @@ -774,6 +780,7 @@ export class LocalStore {
'Tried to allocate an already allocated query: ' + query
);
this.queryDataByTarget[queryData.targetId] = queryData;
this.targetIdByQuery.set(query, queryData.targetId);
return queryData;
});
}
Expand All @@ -789,22 +796,14 @@ export class LocalStore {
transaction: PersistenceTransaction,
query: Query
): PersistencePromise<QueryData | null> {
let queryData: QueryData | null = null;

// Look up the query data in our local map first, as this avoids a potential
// lookup in IndexedDb.
objUtils.forEachNumber(
this.queryDataByTarget,
(targetId, cachedQueryData) => {
if (cachedQueryData.query.isEqual(query)) {
queryData = cachedQueryData;
}
}
);

return queryData
? PersistencePromise.resolve<QueryData | null>(queryData)
: this.queryCache.getQueryData(transaction, query);
const targetId = this.targetIdByQuery.get(query);
if (targetId !== undefined) {
return PersistencePromise.resolve<QueryData | null>(
this.queryDataByTarget[targetId]
);
} else {
return this.queryCache.getQueryData(transaction, query);
}
}

/**
Expand All @@ -816,32 +815,31 @@ export class LocalStore {
releaseQuery(query: Query, keepPersistedQueryData: boolean): Promise<void> {
const mode = keepPersistedQueryData ? 'readwrite' : 'readwrite-primary';
return this.persistence.runTransaction('Release query', mode, txn => {
return this.getQueryData(txn, query).next(queryData => {
assert(
queryData != null,
'Tried to release nonexistent query: ' + query
);
const targetId = queryData!.targetId;

// References for documents sent via Watch are automatically removed
// when we delete a query's target data from the reference delegate.
// Since this does not remove references for locally mutated documents,
// we have to remove the target associations for these documents
// manually.
const removed = this.localViewReferences.removeReferencesForId(
targetId
);
delete this.queryDataByTarget[targetId];
if (!keepPersistedQueryData) {
return PersistencePromise.forEach(removed, (key: DocumentKey) =>
this.persistence.referenceDelegate.removeReference(txn, key)
).next(() =>
this.persistence.referenceDelegate.removeTarget(txn, queryData!)
);
} else {
return PersistencePromise.resolve();
}
});
const targetId = this.targetIdByQuery.get(query);
Copy link
Contributor Author

@schmidt-sebastian schmidt-sebastian Sep 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now have a map to look up targetIds for active queries, so we no longer need to go to the QueryCache.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call!

assert(
targetId !== undefined,
'Tried to release nonexistent query: ' + query
);
const queryData = this.queryDataByTarget[targetId!]!;

// References for documents sent via Watch are automatically removed
// when we delete a query's target data from the reference delegate.
// Since this does not remove references for locally mutated documents,
// we have to remove the target associations for these documents
// manually.
const removed = this.localViewReferences.removeReferencesForId(targetId!);
delete this.queryDataByTarget[targetId!];
this.targetIdByQuery.delete(query);

if (!keepPersistedQueryData) {
return PersistencePromise.forEach(removed, (key: DocumentKey) =>
this.persistence.referenceDelegate.removeReference(txn, key)
).next(() => {
this.persistence.referenceDelegate.removeTarget(txn, queryData);
});
} else {
return PersistencePromise.resolve();
}
});
}

Expand Down