Skip to content

Commit 82de1e1

Browse files
committed
more work
1 parent a5d0e4b commit 82de1e1

File tree

2 files changed

+174
-6
lines changed

2 files changed

+174
-6
lines changed

packages/firestore/src/api/persistent_cache_index_performance_experiment.ts

Lines changed: 135 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,139 @@
1717

1818
import { User } from '../auth/user';
1919
import { DatabaseId } from '../core/database_info';
20+
import { FieldFilter, Operator } from '../core/filter';
21+
import { QueryImpl, queryToTarget } from '../core/query';
22+
import { SnapshotVersion } from '../core/snapshot_version';
23+
import { IndexManager } from '../local/index_manager';
2024
import { IndexedDbIndexManager } from '../local/indexeddb_index_manager';
2125
import { IndexedDbPersistence } from '../local/indexeddb_persistence';
2226
import { LocalDocumentsView } from '../local/local_documents_view';
2327
import { LruParams } from '../local/lru_garbage_collector';
28+
import { Persistence } from '../local/persistence';
29+
import { PersistencePromise } from '../local/persistence_promise';
2430
import { QueryEngine } from '../local/query_engine';
31+
import { documentKeySet, documentMap } from '../model/collections';
32+
import { DocumentKey } from '../model/document_key';
33+
import { IndexOffset } from '../model/field_index';
34+
import { ObjectValue } from '../model/object_value';
35+
import { FieldPath, ResourcePath } from '../model/path';
2536
import { getDocument, getWindow } from '../platform/dom';
2637
import { JsonProtoSerializer } from '../remote/serializer';
2738
import { AsyncQueueImpl } from '../util/async_queue_impl';
2839
import { AutoId } from '../util/misc';
2940

30-
export function runPersistentCacheIndexPerformanceExperiment(
41+
import { Timestamp } from './timestamp';
42+
43+
44+
interface ExperimentConfig {
45+
/** The number of documents to create in the collection. */
46+
documentCount: number;
47+
/** The number of fields in each document. */
48+
fieldCount: number;
49+
/** The number of documents that match the query. */
50+
documentMatchCount: number;
51+
}
52+
53+
export async function runPersistentCacheIndexPerformanceExperiment(
54+
config: ExperimentConfig,
3155
log: (...args: unknown[]) => unknown
32-
): void {
33-
const { queryEngine } = createTestObjects();
34-
log('Created QueryEngine', queryEngine);
56+
): Promise<void> {
57+
const { persistence, indexManager, queryEngine } = await createTestObjects();
58+
const collectionId = AutoId.newId();
59+
60+
const query = createQuery(collectionId, 'matches', Operator.EQUAL, true);
61+
const target = queryToTarget(query);
62+
await persistence.runTransaction('createTargetIndexes', 'readwrite', txn => {
63+
log('createTargetIndexes()');
64+
return indexManager.createTargetIndexes(txn, queryToTarget(query));
65+
});
66+
67+
await persistence.runTransaction('populate collection', 'readwrite', txn => {
68+
log('populate collection');
69+
const documentIds: string[] = [];
70+
for (let i = 0; i < config.documentCount; i++) {
71+
documentIds.push(AutoId.newId());
72+
}
73+
const matchingDocumentIds = new Set<string>();
74+
while (matchingDocumentIds.size < config.documentMatchCount) {
75+
const matchingDocumentIdIndex = Math.floor(
76+
Math.random() * documentIds.length
77+
);
78+
matchingDocumentIds.add(documentIds[matchingDocumentIdIndex]);
79+
}
80+
const documents: Array<{ documentId: string; value: ObjectValue }> = [];
81+
for (const documentId of documentIds) {
82+
const value = ObjectValue.empty();
83+
for (let fieldIndex = 0; fieldIndex < config.fieldCount; fieldIndex++) {
84+
const fieldPath = new FieldPath([AutoId.newId()]);
85+
value.set(fieldPath, { stringValue: `field${fieldIndex}` });
86+
}
87+
if (matchingDocumentIds.has(documentId)) {
88+
value.set(new FieldPath(['matches']), { booleanValue: true });
89+
}
90+
documents.push({ documentId, value });
91+
}
92+
return PersistencePromise.forEach(
93+
documents,
94+
(documentInfo: { documentId: string; value: ObjectValue }) => {
95+
const { documentId, value } = documentInfo;
96+
const documentKey = DocumentKey.fromSegments([
97+
collectionId,
98+
documentId
99+
]);
100+
const changeBuffer = persistence
101+
.getRemoteDocumentCache()
102+
.newChangeBuffer();
103+
return changeBuffer.getEntry(txn, documentKey).next(document => {
104+
changeBuffer.addEntry(
105+
document.convertToFoundDocument(
106+
SnapshotVersion.fromTimestamp(Timestamp.fromMillis(1)),
107+
value
108+
)
109+
);
110+
return changeBuffer
111+
.apply(txn)
112+
.next(() =>
113+
indexManager.updateIndexEntries(txn, documentMap(document))
114+
)
115+
.next(() =>
116+
indexManager.updateCollectionGroup(
117+
txn,
118+
collectionId,
119+
new IndexOffset(document.readTime, document.key, -1)
120+
)
121+
);
122+
});
123+
}
124+
);
125+
});
126+
127+
const queryResult = await persistence.runTransaction(
128+
'populate collection',
129+
'readwrite',
130+
txn => {
131+
log('getDocumentsMatchingQuery()');
132+
return queryEngine.getDocumentsMatchingQuery(
133+
txn,
134+
query,
135+
SnapshotVersion.min(),
136+
documentKeySet()
137+
);
138+
}
139+
);
140+
141+
log(`getDocumentsMatchingQuery() returned ${queryResult.size} documents`);
142+
143+
await persistence.shutdown();
35144
}
36145

37146
interface TestObjects {
147+
persistence: Persistence;
148+
indexManager: IndexManager;
38149
queryEngine: QueryEngine;
39150
}
40151

41-
function createTestObjects(): TestObjects {
152+
async function createTestObjects(): Promise<TestObjects> {
42153
const databaseId = new DatabaseId(/*projectId=*/ AutoId.newId());
43154
const user = new User(/*uid=*/ null);
44155
const persistence = new IndexedDbPersistence(
@@ -60,6 +171,8 @@ function createTestObjects(): TestObjects {
60171
/*forceOwningTab=*/ false
61172
);
62173

174+
await persistence.start();
175+
63176
const remoteDocumentCache = persistence.getRemoteDocumentCache();
64177
const indexManager = new IndexedDbIndexManager(user, databaseId);
65178
const mutationQueue = persistence.getMutationQueue(user, indexManager);
@@ -73,5 +186,21 @@ function createTestObjects(): TestObjects {
73186
const queryEngine = new QueryEngine();
74187
queryEngine.initialize(localDocumentView, indexManager);
75188

76-
return { queryEngine };
189+
return { persistence, indexManager, queryEngine };
190+
}
191+
192+
function createQuery(
193+
path: string,
194+
field: string,
195+
op: Operator,
196+
value: boolean
197+
): QueryImpl {
198+
const fieldPath = FieldPath.fromServerFormat(field);
199+
const filter = FieldFilter.create(fieldPath, op, { booleanValue: value });
200+
return new QueryImpl(
201+
/*path=*/ ResourcePath.fromString(path),
202+
/*collectionGroup=*/ null,
203+
/*explicitOrderBy=*/ [],
204+
/*filters=*/ [filter]
205+
);
77206
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
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+
19+
import { runPersistentCacheIndexPerformanceExperiment } from '../util/firebase_export';
20+
import {
21+
apiDescribe
22+
} from '../util/helpers';
23+
24+
apiDescribe('experiment', persistence => {
25+
it.only('run experiment', function () {
26+
if (persistence.storage === 'indexeddb') {
27+
return runPersistentCacheIndexPerformanceExperiment(
28+
{
29+
documentCount: 100,
30+
documentMatchCount: 5,
31+
fieldCount: 31
32+
},
33+
console.log
34+
);
35+
} else {
36+
this.skip();
37+
}
38+
});
39+
});

0 commit comments

Comments
 (0)