Skip to content

Commit 3635f53

Browse files
Add performance spec tests for queries against large collections (firebase#1802)
1 parent 22a5302 commit 3635f53

File tree

2 files changed

+76
-5
lines changed

2 files changed

+76
-5
lines changed

packages/firestore/test/unit/specs/describe_spec.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { ExclusiveTestFunction, PendingTestFunction } from 'mocha';
19+
1820
import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence';
1921
import { assert } from '../../../src/util/assert';
2022
import { addEqualityMatcher } from '../../util/equality_matcher';
@@ -48,6 +50,7 @@ const KNOWN_TAGS = [
4850

4951
// TODO(mrschmidt): Make this configurable with mocha options.
5052
const RUN_BENCHMARK_TESTS = false;
53+
const BENCHMARK_TEST_TIMEOUT_MS = 10 * 1000;
5154

5255
// The format of one describeSpec written to a JSON file.
5356
interface SpecOutputFormat {
@@ -79,7 +82,10 @@ export function setSpecJSONHandler(writer: (json: string) => void): void {
7982
}
8083

8184
/** Gets the test runner based on the specified tags. */
82-
function getTestRunner(tags, persistenceEnabled): Function {
85+
function getTestRunner(
86+
tags,
87+
persistenceEnabled
88+
): ExclusiveTestFunction | PendingTestFunction {
8389
if (tags.indexOf(NO_WEB_TAG) >= 0) {
8490
return it.skip;
8591
} else if (
@@ -102,6 +108,15 @@ function getTestRunner(tags, persistenceEnabled): Function {
102108
}
103109
}
104110

111+
/** If required, returns a custom test timeout for long-running tests */
112+
function getTestTimeout(tags): number | undefined {
113+
if (tags.indexOf(BENCHMARK_TAG) >= 0) {
114+
return BENCHMARK_TEST_TIMEOUT_MS;
115+
} else {
116+
return undefined;
117+
}
118+
}
119+
105120
/**
106121
* Like it(), but for spec tests.
107122
* @param name A name to give the test.
@@ -153,9 +168,10 @@ export function specTest(
153168
for (const usePersistence of persistenceModes) {
154169
const spec = builder();
155170
const runner = getTestRunner(tags, usePersistence);
171+
const timeout = getTestTimeout(tags);
156172
const mode = usePersistence ? '(Persistence)' : '(Memory)';
157173
const fullName = `${mode} ${name}`;
158-
runner(fullName, async () => {
174+
const queuedTest = runner(fullName, async () => {
159175
const start = Date.now();
160176
await spec.runAsTest(fullName, usePersistence);
161177
const end = Date.now();
@@ -164,6 +180,10 @@ export function specTest(
164180
console.log(`Runtime: ${end - start} ms.`);
165181
}
166182
});
183+
184+
if (timeout !== undefined) {
185+
queuedTest.timeout(timeout);
186+
}
167187
}
168188
} else {
169189
assert(

packages/firestore/test/unit/specs/perf_spec.test.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describeSpec(
6666
fromCache: true,
6767
hasPendingWrites: true
6868
})
69-
.writeAcks(`collection/${i}`, ++currentVersion)
69+
.writeAcks(`collection/${i}`, docRemote.version.toMicroseconds())
7070
.watchAcksFull(query, ++currentVersion, docRemote)
7171
.expectEvents(query, { metadata: [docRemote] })
7272
.userUnlistens(query)
@@ -143,7 +143,7 @@ describeSpec(
143143
fromCache: true,
144144
hasPendingWrites: true
145145
})
146-
.writeAcks(`collection/doc`, ++currentVersion)
146+
.writeAcks(`collection/doc`, docRemote.version.toMicroseconds())
147147
.watchAcksFull(query, ++currentVersion, docRemote)
148148
.expectEvents(query, { metadata: [docRemote] });
149149

@@ -163,7 +163,7 @@ describeSpec(
163163
modified: [docLocal],
164164
hasPendingWrites: true
165165
})
166-
.writeAcks(`collection/doc`, ++currentVersion)
166+
.writeAcks(`collection/doc`, docRemote.version.toMicroseconds())
167167
.watchSends({ affects: [query] }, docRemote)
168168
.watchSnapshots(++currentVersion)
169169
.expectEvents(query, { metadata: [docRemote] });
@@ -292,5 +292,56 @@ describeSpec(
292292

293293
return steps;
294294
});
295+
296+
specTest(
297+
'Add 500 documents, issue 10 queries that return 10 documents each, unlisten',
298+
[],
299+
() => {
300+
const documentCount = 500;
301+
const matchingCount = 10;
302+
const queryCount = 10;
303+
304+
const steps = spec().withGCEnabled(false);
305+
306+
const collPath = `collection`;
307+
const query = Query.atPath(path(collPath)).addOrderBy(orderBy('val'));
308+
steps.userListens(query).watchAcks(query);
309+
310+
const allDocs: Document[] = [];
311+
312+
let currentVersion = 1;
313+
// Create `documentCount` documents.
314+
for (let j = 0; j < documentCount; ++j) {
315+
const document = doc(`${collPath}/doc${j}`, ++currentVersion, {
316+
val: j
317+
});
318+
allDocs.push(document);
319+
steps.watchSends({ affects: [query] }, document);
320+
}
321+
322+
steps.watchCurrents(query, `current-version-${++currentVersion}`);
323+
steps.watchSnapshots(currentVersion);
324+
steps.expectEvents(query, { added: allDocs });
325+
steps.userUnlistens(query).watchRemoves(query);
326+
327+
for (let i = 1; i <= STEP_COUNT; ++i) {
328+
// Create `queryCount` listens, each against collPath but with a
329+
// unique query constraint.
330+
for (let j = 0; j < queryCount; ++j) {
331+
const partialQuery = Query.atPath(path(collPath))
332+
.addFilter(filter('val', '>=', j * matchingCount))
333+
.addFilter(filter('val', '<', (j + 1) * matchingCount));
334+
steps.userListens(partialQuery);
335+
steps.expectEvents(partialQuery, {
336+
added: allDocs.slice(j * matchingCount, (j + 1) * matchingCount),
337+
fromCache: true
338+
});
339+
steps.userUnlistens(partialQuery);
340+
}
341+
}
342+
343+
return steps;
344+
}
345+
);
295346
}
296347
);

0 commit comments

Comments
 (0)