mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
339 lines
10 KiB
TypeScript
339 lines
10 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2018 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { Query } from '../../../src/core/query';
|
|
import { Document } from '../../../src/model/document';
|
|
import { doc, filter, orderBy, query } from '../../util/helpers';
|
|
|
|
import { describeSpec, specTest } from './describe_spec';
|
|
import { spec } from './spec_builder';
|
|
|
|
/** The number of iterations for the benchmark spec tests. */
|
|
const STEP_COUNT = 10;
|
|
|
|
describeSpec(
|
|
`Performance Tests [${STEP_COUNT} iterations]:`,
|
|
['benchmark'],
|
|
() => {
|
|
specTest('Insert a new document', [], () => {
|
|
const steps = spec().ensureManualLruGC();
|
|
for (let i = 0; i < STEP_COUNT; ++i) {
|
|
steps
|
|
.userSets(`collection/{i}`, { doc: i })
|
|
.writeAcks(`collection/{i}`, i + 1); // Prevent zero version
|
|
}
|
|
return steps;
|
|
});
|
|
|
|
specTest(
|
|
'Start a listen, write a document, ack the write, handle watch snapshot, unlisten',
|
|
[],
|
|
() => {
|
|
let currentVersion = 1;
|
|
const steps = spec().ensureManualLruGC();
|
|
|
|
for (let i = 0; i < STEP_COUNT; ++i) {
|
|
const query1 = query(`collection/${i}`);
|
|
const docLocal = doc(`collection/${i}`, 0, {
|
|
doc: i
|
|
}).setHasLocalMutations();
|
|
const docRemote = doc(`collection/${i}`, ++currentVersion, {
|
|
doc: i
|
|
});
|
|
|
|
steps
|
|
.userListens(query1)
|
|
.userSets(`collection/${i}`, { doc: i })
|
|
.expectEvents(query1, {
|
|
added: [docLocal],
|
|
fromCache: true,
|
|
hasPendingWrites: true
|
|
})
|
|
.writeAcks(`collection/${i}`, docRemote.version.toMicroseconds())
|
|
.watchAcksFull(query1, ++currentVersion, docRemote)
|
|
.expectEvents(query1, { metadata: [docRemote] })
|
|
.userUnlistens(query1)
|
|
.watchRemoves(query1);
|
|
}
|
|
return steps;
|
|
}
|
|
);
|
|
|
|
specTest('Write 100 documents and raise a snapshot', [], () => {
|
|
const cachedDocumentCount = 100;
|
|
9;
|
|
const query1 = query(`collection`, orderBy('v'));
|
|
const steps = spec().ensureManualLruGC();
|
|
const docs: Document[] = [];
|
|
|
|
for (let i = 0; i < cachedDocumentCount; ++i) {
|
|
steps.userSets(`collection/${i}`, { v: i });
|
|
docs.push(doc(`collection/${i}`, 0, { v: i }).setHasLocalMutations());
|
|
}
|
|
|
|
for (let i = 1; i <= STEP_COUNT; ++i) {
|
|
steps
|
|
.userListens(query1)
|
|
.expectEvents(query1, {
|
|
added: docs,
|
|
fromCache: true,
|
|
hasPendingWrites: true
|
|
})
|
|
.userUnlistens(query1);
|
|
}
|
|
|
|
return steps;
|
|
});
|
|
|
|
specTest('Update a single document', [], () => {
|
|
const steps = spec()
|
|
.ensureManualLruGC()
|
|
.userSets(`collection/doc`, { v: 0 });
|
|
|
|
for (let i = 1; i <= STEP_COUNT; ++i) {
|
|
steps
|
|
.userPatches(`collection/doc`, { v: i })
|
|
.writeAcks(`collection/doc`, i);
|
|
}
|
|
return steps;
|
|
});
|
|
|
|
specTest(
|
|
'Update a document and wait for snapshot with existing listen',
|
|
[],
|
|
() => {
|
|
const query1 = query(`collection/doc`);
|
|
|
|
let currentVersion = 1;
|
|
const steps = spec().ensureManualLruGC();
|
|
|
|
let docLocal = doc(`collection/doc`, 0, {
|
|
v: 0
|
|
}).setHasLocalMutations();
|
|
let docRemote = doc(`collection/doc`, ++currentVersion, { v: 0 });
|
|
let lastRemoteVersion = currentVersion;
|
|
|
|
steps
|
|
.userListens(query1)
|
|
.userSets(`collection/doc`, { v: 0 })
|
|
.expectEvents(query1, {
|
|
added: [docLocal],
|
|
fromCache: true,
|
|
hasPendingWrites: true
|
|
})
|
|
.writeAcks(`collection/doc`, docRemote.version.toMicroseconds())
|
|
.watchAcksFull(query1, ++currentVersion, docRemote)
|
|
.expectEvents(query1, { metadata: [docRemote] });
|
|
|
|
for (let i = 1; i <= STEP_COUNT; ++i) {
|
|
docLocal = doc(`collection/doc`, lastRemoteVersion, {
|
|
v: i
|
|
}).setHasLocalMutations();
|
|
docRemote = doc(`collection/doc`, ++currentVersion, { v: i });
|
|
lastRemoteVersion = currentVersion;
|
|
|
|
steps
|
|
.userPatches(`collection/doc`, { v: i })
|
|
.expectEvents(query1, {
|
|
modified: [docLocal],
|
|
hasPendingWrites: true
|
|
})
|
|
.writeAcks(`collection/doc`, docRemote.version.toMicroseconds())
|
|
.watchSends({ affects: [query1] }, docRemote)
|
|
.watchSnapshots(++currentVersion)
|
|
.expectEvents(query1, { metadata: [docRemote] });
|
|
}
|
|
return steps;
|
|
}
|
|
);
|
|
|
|
specTest(
|
|
'Process 100 documents from Watch and wait for snapshot',
|
|
[],
|
|
() => {
|
|
const documentsPerStep = 100;
|
|
|
|
const query1 = query(`collection`, orderBy('v'));
|
|
const steps = spec().ensureManualLruGC();
|
|
|
|
let currentVersion = 1;
|
|
|
|
steps
|
|
.userListens(query1)
|
|
.watchAcksFull(query1, currentVersion)
|
|
.expectEvents(query1, {});
|
|
|
|
for (let i = 1; i <= STEP_COUNT; ++i) {
|
|
const docs: Document[] = [];
|
|
|
|
for (let j = 0; j < documentsPerStep; ++j) {
|
|
docs.push(
|
|
doc(`collection/${j}`, ++currentVersion, { v: currentVersion })
|
|
);
|
|
}
|
|
|
|
const changeType = i === 1 ? 'added' : 'modified';
|
|
|
|
steps
|
|
.watchSends({ affects: [query1] }, ...docs)
|
|
.watchSnapshots(++currentVersion)
|
|
.expectEvents(query1, { [changeType]: docs });
|
|
}
|
|
|
|
return steps;
|
|
}
|
|
);
|
|
|
|
specTest(
|
|
'Process 100 documents from Watch and wait for snapshot, then unlisten and wait for a ' +
|
|
'cached snapshot',
|
|
[],
|
|
() => {
|
|
const documentsPerStep = 100;
|
|
|
|
let currentVersion = 1;
|
|
const steps = spec().ensureManualLruGC();
|
|
|
|
for (let i = 1; i <= STEP_COUNT; ++i) {
|
|
const collPath = `collection/${i}/coll`;
|
|
const query1 = query(collPath, orderBy('v'));
|
|
|
|
const docs: Document[] = [];
|
|
for (let j = 0; j < documentsPerStep; ++j) {
|
|
docs.push(doc(`${collPath}/${j}`, ++currentVersion, { v: j }));
|
|
}
|
|
|
|
steps
|
|
.userListens(query1)
|
|
.watchAcksFull(query1, ++currentVersion, ...docs)
|
|
.expectEvents(query1, { added: docs })
|
|
.userUnlistens(query1)
|
|
.watchRemoves(query1)
|
|
.userListens(query1, {
|
|
resumeToken: 'resume-token-' + currentVersion
|
|
})
|
|
.expectEvents(query1, { added: docs, fromCache: true })
|
|
.watchAcksFull(query1, ++currentVersion)
|
|
.expectEvents(query1, {})
|
|
.userUnlistens(query1)
|
|
.watchRemoves(query1);
|
|
}
|
|
|
|
return steps;
|
|
}
|
|
);
|
|
|
|
specTest('Process 25 target updates and wait for snapshot', [], () => {
|
|
const queriesPerStep = 25;
|
|
|
|
let currentVersion = 1;
|
|
const steps = spec().ensureManualLruGC();
|
|
|
|
for (let i = 1; i <= STEP_COUNT; ++i) {
|
|
// We use a different subcollection for each iteration to ensure
|
|
// that we use distinct and non-overlapping collection queries.
|
|
const collPath = `collection/${i}/coll`;
|
|
const matchingDoc = doc(`${collPath}/matches`, ++currentVersion, {
|
|
val: -1
|
|
});
|
|
|
|
const queries: Query[] = [];
|
|
|
|
// Create `queriesPerStep` listens, each against collPath but with a
|
|
// unique query constraint.
|
|
for (let j = 0; j < queriesPerStep; ++j) {
|
|
const query1 = query(collPath, filter('val', '<=', j));
|
|
queries.push(query1);
|
|
steps.userListens(query1).watchAcks(query1);
|
|
}
|
|
|
|
steps
|
|
.watchSends({ affects: queries }, matchingDoc)
|
|
.watchSnapshots(++currentVersion);
|
|
|
|
// Registers the snapshot expectations with the spec runner.
|
|
for (const query of queries) {
|
|
steps.expectEvents(query, {
|
|
added: [matchingDoc],
|
|
fromCache: true
|
|
});
|
|
}
|
|
|
|
// Unlisten and clean up the query.
|
|
for (const query of queries) {
|
|
steps.userUnlistens(query).watchRemoves(query);
|
|
}
|
|
}
|
|
|
|
return steps;
|
|
});
|
|
|
|
specTest(
|
|
'Add 500 documents, issue 10 queries that return 10 documents each, unlisten',
|
|
[],
|
|
() => {
|
|
const documentCount = 500;
|
|
const matchingCount = 10;
|
|
const queryCount = 10;
|
|
|
|
const steps = spec().ensureManualLruGC();
|
|
|
|
const collPath = `collection`;
|
|
const query1 = query(collPath, orderBy('val'));
|
|
steps.userListens(query1).watchAcks(query1);
|
|
|
|
const allDocs: Document[] = [];
|
|
|
|
let currentVersion = 1;
|
|
// Create `documentCount` documents.
|
|
for (let j = 0; j < documentCount; ++j) {
|
|
const document = doc(`${collPath}/doc${j}`, ++currentVersion, {
|
|
val: j
|
|
});
|
|
allDocs.push(document);
|
|
steps.watchSends({ affects: [query1] }, document);
|
|
}
|
|
|
|
steps.watchCurrents(query1, `current-version-${++currentVersion}`);
|
|
steps.watchSnapshots(currentVersion);
|
|
steps.expectEvents(query1, { added: allDocs });
|
|
steps.userUnlistens(query1).watchRemoves(query1);
|
|
|
|
for (let i = 1; i <= STEP_COUNT; ++i) {
|
|
// Create `queryCount` listens, each against collPath but with a
|
|
// unique query constraint.
|
|
for (let j = 0; j < queryCount; ++j) {
|
|
const partialQuery = query(
|
|
collPath,
|
|
filter('val', '>=', j * matchingCount),
|
|
filter('val', '<', (j + 1) * matchingCount)
|
|
);
|
|
steps.userListens(partialQuery);
|
|
steps.expectEvents(partialQuery, {
|
|
added: allDocs.slice(j * matchingCount, (j + 1) * matchingCount),
|
|
fromCache: true
|
|
});
|
|
steps.userUnlistens(partialQuery);
|
|
}
|
|
}
|
|
|
|
return steps;
|
|
}
|
|
);
|
|
}
|
|
);
|