Skip to content

Commit 69bd1fc

Browse files
Expose persistence stats to LocalStoreTest (take 2)
1 parent 0433b82 commit 69bd1fc

File tree

3 files changed

+269
-6
lines changed

3 files changed

+269
-6
lines changed

packages/firestore/src/local/local_documents_view.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ import { RemoteDocumentCache } from './remote_document_cache';
4848
*/
4949
export class LocalDocumentsView {
5050
constructor(
51-
private remoteDocumentCache: RemoteDocumentCache,
52-
private mutationQueue: MutationQueue,
53-
private indexManager: IndexManager
51+
readonly remoteDocumentCache: RemoteDocumentCache,
52+
readonly mutationQueue: MutationQueue,
53+
readonly indexManager: IndexManager
5454
) {}
5555

5656
/**
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { QueryEngine } from '../../../src/local/query_engine';
19+
import { LocalDocumentsView } from '../../../src/local/local_documents_view';
20+
import { PersistenceTransaction } from '../../../src/local/persistence';
21+
import { Query } from '../../../src/core/query';
22+
import { SnapshotVersion } from '../../../src/core/snapshot_version';
23+
import { PersistencePromise } from '../../../src/local/persistence_promise';
24+
import { DocumentMap } from '../../../src/model/collections';
25+
import { RemoteDocumentCache } from '../../../src/local/remote_document_cache';
26+
import { MutationQueue } from '../../../src/local/mutation_queue';
27+
28+
/**
29+
* A test-only query engine that forwards all calls to the query engine
30+
* provided at construction time, but provides access to the number of
31+
* documents and mutations read.
32+
*/
33+
export class ForwardingCountingQueryEngine implements QueryEngine {
34+
/**
35+
* The number of mutations returned by the MutationQueue (since the last call
36+
* to `resetCounts()`)
37+
*/
38+
mutationsRead = 0;
39+
40+
/**
41+
* The number of documents returned by the RemoteDocumentCache (since the
42+
* last call to `resetCounts()`)
43+
*/
44+
documentsRead = 0;
45+
46+
constructor(private readonly queryEngine: QueryEngine) {}
47+
48+
resetCounts() : void {
49+
this.mutationsRead = 0;
50+
this.documentsRead = 0;
51+
}
52+
53+
getDocumentsMatchingQuery(
54+
transaction: PersistenceTransaction,
55+
query: Query,
56+
sinceReadTime: SnapshotVersion
57+
): PersistencePromise<DocumentMap> {
58+
return this.queryEngine.getDocumentsMatchingQuery(
59+
transaction,
60+
query,
61+
sinceReadTime
62+
);
63+
}
64+
65+
setLocalDocumentsView(localDocuments: LocalDocumentsView): void {
66+
const view = new LocalDocumentsView(
67+
this.wrapRemoteDocumentCache(localDocuments.remoteDocumentCache),
68+
this.wrapMutationQueue(localDocuments.mutationQueue),
69+
localDocuments.indexManager
70+
);
71+
72+
return this.queryEngine.setLocalDocumentsView(view);
73+
}
74+
75+
private wrapRemoteDocumentCache(
76+
target: RemoteDocumentCache
77+
): RemoteDocumentCache {
78+
return {
79+
getDocumentsMatchingQuery: (transaction, query, sinceReadTime) => {
80+
return target
81+
.getDocumentsMatchingQuery(transaction, query, sinceReadTime)
82+
.next(result => {
83+
this.documentsRead += result.size;
84+
return result;
85+
});
86+
},
87+
getEntries: (transaction, documentKeys) => {
88+
return target.getEntries(transaction, documentKeys).next(result => {
89+
this.documentsRead += result.size;
90+
return result;
91+
});
92+
},
93+
getEntry: (transaction, documentKey) => {
94+
return target.getEntry(transaction, documentKey).next(result => {
95+
this.documentsRead += result ? 1 : 0;
96+
return result;
97+
});
98+
},
99+
getNewDocumentChanges: target.getNewDocumentChanges,
100+
getSize: target.getSize,
101+
newChangeBuffer: target.newChangeBuffer
102+
};
103+
}
104+
105+
private wrapMutationQueue(target: MutationQueue): MutationQueue {
106+
return {
107+
acknowledgeBatch: target.acknowledgeBatch,
108+
addMutationBatch: target.addMutationBatch,
109+
checkEmpty: target.checkEmpty,
110+
getAllMutationBatches: transaction => {
111+
return target.getAllMutationBatches(transaction).next(result => {
112+
this.mutationsRead += result.length;
113+
return result;
114+
});
115+
},
116+
getAllMutationBatchesAffectingDocumentKey: (transaction, documentKey) => {
117+
return target
118+
.getAllMutationBatchesAffectingDocumentKey(transaction, documentKey)
119+
.next(result => {
120+
this.mutationsRead += result.length;
121+
return result;
122+
});
123+
},
124+
getAllMutationBatchesAffectingDocumentKeys: (
125+
transaction,
126+
documentKeys
127+
) => {
128+
return target
129+
.getAllMutationBatchesAffectingDocumentKeys(transaction, documentKeys)
130+
.next(result => {
131+
this.mutationsRead += result.length;
132+
return result;
133+
});
134+
},
135+
getAllMutationBatchesAffectingQuery: (transaction, query) => {
136+
return target
137+
.getAllMutationBatchesAffectingQuery(transaction, query)
138+
.next(result => {
139+
this.mutationsRead += result.length;
140+
return result;
141+
});
142+
},
143+
getHighestUnacknowledgedBatchId: target.getHighestUnacknowledgedBatchId,
144+
getLastStreamToken: target.getLastStreamToken,
145+
getNextMutationBatchAfterBatchId: target.getNextMutationBatchAfterBatchId,
146+
lookupMutationBatch: target.lookupMutationBatch,
147+
lookupMutationKeys: target.lookupMutationKeys,
148+
performConsistencyCheck: target.performConsistencyCheck,
149+
removeCachedMutationKeys: target.removeCachedMutationKeys,
150+
removeMutationBatch: target.removeMutationBatch,
151+
setLastStreamToken: target.setLastStreamToken
152+
};
153+
}
154+
}

packages/firestore/test/unit/local/local_store.test.ts

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import {
5959
docAddedRemoteEvent,
6060
docUpdateRemoteEvent,
6161
expectEqual,
62+
filter,
6263
key,
6364
localViewChanges,
6465
mapAsArray,
@@ -73,13 +74,27 @@ import {
7374

7475
import { FieldValue, IntegerValue } from '../../../src/model/field_value';
7576
import * as persistenceHelpers from './persistence_test_helpers';
77+
import { ForwardingCountingQueryEngine } from './forwarding_counting_query_engine';
7678

7779
class LocalStoreTester {
7880
private promiseChain: Promise<void> = Promise.resolve();
7981
private lastChanges: MaybeDocumentMap | null = null;
8082
private lastTargetId: TargetId | null = null;
8183
private batches: MutationBatch[] = [];
82-
constructor(public localStore: LocalStore, readonly gcIsEager: boolean) {}
84+
85+
constructor(
86+
public localStore: LocalStore,
87+
private readonly queryEngine: ForwardingCountingQueryEngine,
88+
readonly gcIsEager: boolean
89+
) {}
90+
91+
private prepareNextStep(): void {
92+
this.promiseChain = this.promiseChain.then(() => {
93+
this.lastChanges = null;
94+
this.lastTargetId = null;
95+
this.queryEngine.resetCounts();
96+
});
97+
}
8398

8499
after(
85100
op: Mutation | Mutation[] | RemoteEvent | LocalViewChanges
@@ -96,6 +111,8 @@ class LocalStoreTester {
96111
}
97112

98113
afterMutations(mutations: Mutation[]): LocalStoreTester {
114+
this.prepareNextStep();
115+
99116
this.promiseChain = this.promiseChain
100117
.then(() => {
101118
return this.localStore.localWrite(mutations);
@@ -110,6 +127,8 @@ class LocalStoreTester {
110127
}
111128

112129
afterRemoteEvent(remoteEvent: RemoteEvent): LocalStoreTester {
130+
this.prepareNextStep();
131+
113132
this.promiseChain = this.promiseChain
114133
.then(() => {
115134
return this.localStore.applyRemoteEvent(remoteEvent);
@@ -121,6 +140,8 @@ class LocalStoreTester {
121140
}
122141

123142
afterViewChanges(viewChanges: LocalViewChanges): LocalStoreTester {
143+
this.prepareNextStep();
144+
124145
this.promiseChain = this.promiseChain.then(() =>
125146
this.localStore.notifyLocalViewChanges([viewChanges])
126147
);
@@ -131,6 +152,8 @@ class LocalStoreTester {
131152
documentVersion: TestSnapshotVersion;
132153
transformResult?: FieldValue;
133154
}): LocalStoreTester {
155+
this.prepareNextStep();
156+
134157
this.promiseChain = this.promiseChain
135158
.then(() => {
136159
const batch = this.batches.shift()!;
@@ -161,6 +184,8 @@ class LocalStoreTester {
161184
}
162185

163186
afterRejectingMutation(): LocalStoreTester {
187+
this.prepareNextStep();
188+
164189
this.promiseChain = this.promiseChain
165190
.then(() => {
166191
return this.localStore.rejectBatch(this.batches.shift()!.batchId);
@@ -172,6 +197,8 @@ class LocalStoreTester {
172197
}
173198

174199
afterAllocatingQuery(query: Query): LocalStoreTester {
200+
this.prepareNextStep();
201+
175202
this.promiseChain = this.promiseChain.then(() => {
176203
return this.localStore.allocateQuery(query).then(result => {
177204
this.lastTargetId = result.targetId;
@@ -181,6 +208,8 @@ class LocalStoreTester {
181208
}
182209

183210
afterReleasingQuery(query: Query): LocalStoreTester {
211+
this.prepareNextStep();
212+
184213
this.promiseChain = this.promiseChain.then(() => {
185214
return this.localStore.releaseQuery(
186215
query,
@@ -190,6 +219,42 @@ class LocalStoreTester {
190219
return this;
191220
}
192221

222+
afterExecutingQuery(query: Query): LocalStoreTester {
223+
this.prepareNextStep();
224+
225+
this.promiseChain = this.promiseChain.then(() => {
226+
return this.localStore.executeQuery(query).then(results => {
227+
this.lastChanges = results;
228+
});
229+
});
230+
return this;
231+
}
232+
233+
/**
234+
* Asserts the expected number of mutations and documents read by
235+
* the MutationQueue and the RemoteDocumentCache.
236+
*/
237+
toHaveRead(expectedCount: {
238+
mutations?: number;
239+
remoteDocuments?: number;
240+
}): LocalStoreTester {
241+
this.promiseChain = this.promiseChain.then(() => {
242+
if (expectedCount.mutations !== undefined) {
243+
expect(this.queryEngine.mutationsRead).to.be.eq(
244+
expectedCount.mutations,
245+
'Mutations read'
246+
);
247+
}
248+
if (expectedCount.remoteDocuments !== undefined) {
249+
expect(this.queryEngine.documentsRead).to.be.eq(
250+
expectedCount.remoteDocuments,
251+
'Remote documents read'
252+
);
253+
}
254+
});
255+
return this;
256+
}
257+
193258
toReturnTargetId(id: TargetId): LocalStoreTester {
194259
this.promiseChain = this.promiseChain.then(() => {
195260
expect(this.lastTargetId).to.equal(id);
@@ -310,10 +375,16 @@ function genericLocalStoreTests(
310375
): void {
311376
let persistence: Persistence;
312377
let localStore: LocalStore;
378+
let countingQueryEngine: ForwardingCountingQueryEngine;
313379

314380
beforeEach(async () => {
315381
persistence = await getPersistence();
316-
localStore = new LocalStore(persistence, queryEngine, User.UNAUTHENTICATED);
382+
countingQueryEngine = new ForwardingCountingQueryEngine(queryEngine);
383+
localStore = new LocalStore(
384+
persistence,
385+
countingQueryEngine,
386+
User.UNAUTHENTICATED
387+
);
317388
});
318389

319390
afterEach(async () => {
@@ -322,7 +393,7 @@ function genericLocalStoreTests(
322393
});
323394

324395
function expectLocalStore(): LocalStoreTester {
325-
return new LocalStoreTester(localStore, gcIsEager);
396+
return new LocalStoreTester(localStore, countingQueryEngine, gcIsEager);
326397
}
327398

328399
it('handles SetMutation', () => {
@@ -940,6 +1011,44 @@ function genericLocalStoreTests(
9401011
]);
9411012
});
9421013

1014+
it('reads all documents for initial collection queries', () => {
1015+
const firstQuery = Query.atPath(path('foo'));
1016+
const secondQuery = Query.atPath(path('foo')).addFilter(
1017+
filter('matches', '==', true)
1018+
);
1019+
1020+
return expectLocalStore()
1021+
.afterAllocatingQuery(firstQuery)
1022+
.toReturnTargetId(2)
1023+
.after(
1024+
docAddedRemoteEvent(
1025+
[
1026+
doc('foo/bar', 10, { matches: true }),
1027+
doc('foo/baz', 20, { matches: true })
1028+
],
1029+
[2]
1030+
)
1031+
)
1032+
.toReturnChanged(
1033+
doc('foo/bar', 10, { matches: true }),
1034+
doc('foo/baz', 20, { matches: true })
1035+
)
1036+
.after(setMutation('foo/bonk', { matches: true }))
1037+
.toReturnChanged(
1038+
doc('foo/bonk', 0, { matches: true }, { hasLocalMutations: true })
1039+
)
1040+
.afterAllocatingQuery(secondQuery)
1041+
.toReturnTargetId(4)
1042+
.afterExecutingQuery(secondQuery)
1043+
.toReturnChanged(
1044+
doc('foo/bar', 10, { matches: true }),
1045+
doc('foo/baz', 20, { matches: true }),
1046+
doc('foo/bonk', 0, { matches: true }, { hasLocalMutations: true })
1047+
)
1048+
.toHaveRead({ remoteDocuments: 2, mutations: 1 })
1049+
.finish();
1050+
});
1051+
9431052
it('persists resume tokens', async () => {
9441053
if (gcIsEager) {
9451054
return;

0 commit comments

Comments
 (0)