Skip to content

Commit a4bcfa5

Browse files
committed
switch from documentType to document.createTime
1 parent e5a12c4 commit a4bcfa5

File tree

5 files changed

+62
-137
lines changed

5 files changed

+62
-137
lines changed

packages/firestore/src/local/indexeddb_remote_document_cache.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,7 @@ import { SortedSet } from '../util/sorted_set';
3636

3737
import { IndexManager } from './index_manager';
3838
import { dbDocumentSize } from './indexeddb_mutation_batch_impl';
39-
import {
40-
DbRemoteDocument,
41-
DbRemoteDocumentType,
42-
DbRemoteDocumentGlobal,
43-
DbRemoteDocumentCollectionIndexKey
44-
} from './indexeddb_schema';
39+
import { DbRemoteDocument, DbRemoteDocumentGlobal } from './indexeddb_schema';
4540
import {
4641
DbRemoteDocumentCollectionGroupIndex,
4742
DbRemoteDocumentDocumentKeyIndex,
@@ -50,6 +45,9 @@ import {
5045
DbRemoteDocumentKey,
5146
DbRemoteDocumentStore,
5247
DbRemoteDocumentCollectionIndex,
48+
DbRemoteDocumentCollectionIndexKey,
49+
MAX_CREATE_TIME,
50+
MIN_CREATE_TIME,
5351
DbTimestampKey
5452
} from './indexeddb_sentinels';
5553
import { getStore } from './indexeddb_transaction';
@@ -291,7 +289,7 @@ class IndexedDbRemoteDocumentCacheImpl implements IndexedDbRemoteDocumentCache {
291289
): PersistencePromise<MutableDocumentMap> {
292290
const collection = query.path;
293291
const startKey: DbRemoteDocumentCollectionIndexKey = [
294-
DbRemoteDocumentType.FoundDocument,
292+
MIN_CREATE_TIME,
295293
collection.popLast().toArray(),
296294
collection.lastSegment(),
297295
toDbTimestampKey(offset.readTime),
@@ -300,7 +298,7 @@ class IndexedDbRemoteDocumentCacheImpl implements IndexedDbRemoteDocumentCache {
300298
: offset.documentKey.path.lastSegment()
301299
];
302300
const endKey: DbRemoteDocumentCollectionIndexKey = [
303-
DbRemoteDocumentType.FoundDocument,
301+
MAX_CREATE_TIME,
304302
collection.popLast().toArray(),
305303
collection.lastSegment(),
306304
[Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],

packages/firestore/src/local/indexeddb_schema.ts

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -186,26 +186,6 @@ export interface DbUnknownDocument {
186186
version: DbTimestamp;
187187
}
188188

189-
/**
190-
* The "type" of a document stored in a `DbRemoteDocument`.
191-
*/
192-
export enum DbRemoteDocumentType {
193-
/**
194-
* The `noDocument` property of the `DbRemoteDocument` is set.
195-
*/
196-
NoDocument = 1,
197-
198-
/**
199-
* The `document` property of the `DbRemoteDocument` is set.
200-
*/
201-
FoundDocument = 2,
202-
203-
/**
204-
* The `unknownDocument` property of the `DbRemoteDocument` is set.
205-
*/
206-
UnknownDocument = 3
207-
}
208-
209189
/**
210190
* An object to be stored in the 'remoteDocuments' store in IndexedDb.
211191
* It represents either:
@@ -258,31 +238,8 @@ export interface DbRemoteDocument {
258238
* the write's commit version as their document version.
259239
*/
260240
hasCommittedMutations: boolean;
261-
/**
262-
* The "type" of document stored in this object.
263-
*
264-
* The value of this property _must_ be consistent with the semantics of the
265-
* `unknownDocument`, `noDocument`, and `document` properties. This property
266-
* is provided as an optimization to allow skipping entries in result sets
267-
* whose documents are not of the desired "type".
268-
*
269-
* This property was added in a schema migration at version 19. Documents
270-
* written prior to this version has this field set to 0 (zero).
271-
*/
272-
documentType: DbRemoteDocumentType;
273241
}
274242

275-
/**
276-
* A key in the `DbRemoteDocumentCollectionIndex` index.
277-
*/
278-
export type DbRemoteDocumentCollectionIndexKey = [
279-
/** document type */ DbRemoteDocumentType,
280-
/** path to collection */ string[],
281-
/** collection group */ string,
282-
/** read time */ DbTimestampKey,
283-
/** document ID */ string
284-
];
285-
286243
/**
287244
* Contains a single entry that has metadata about the remote document cache.
288245
*/

packages/firestore/src/local/indexeddb_schema_converter.ts

Lines changed: 4 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import {
4444
DbMutationQueue,
4545
DbRemoteDocument,
4646
DbRemoteDocumentGlobal,
47-
DbRemoteDocumentType,
4847
DbTarget,
4948
DbTargetDocument,
5049
DbTargetGlobal,
@@ -300,29 +299,7 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
300299

301300
if (fromVersion < 19 && toVersion >= 19) {
302301
p = p.next(() => {
303-
{
304-
const startTime = performance.now();
305-
console.log(
306-
'zzyzx createRemoteDocumentStoreDocumentTypeIndex starting'
307-
);
308-
createRemoteDocumentStoreDocumentTypeIndex(txn);
309-
console.log(
310-
'zzyzx createRemoteDocumentStoreDocumentTypeIndex completed: elapsedTime:',
311-
performance.now() - startTime
312-
);
313-
}
314-
{
315-
const startTime = performance.now();
316-
console.log('zzyzx backfillRemoteDocumentStoreDocumentType starting');
317-
return backfillRemoteDocumentStoreDocumentType(
318-
simpleDbTransaction
319-
).next(() => {
320-
console.log(
321-
'zzyzx backfillRemoteDocumentStoreDocumentType completed: elapsedTime:',
322-
performance.now() - startTime
323-
);
324-
});
325-
}
302+
createRemoteDocumentCollectionIndex(txn);
326303
});
327304
}
328305

@@ -525,8 +502,6 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
525502

526503
const path = extractKey(legacyDocument).path.toArray();
527504

528-
// Note: omit the "documentType" property because it will be populated
529-
// by schema migration 19.
530505
const dbRemoteDocument = {
531506
prefixPath: path.slice(0, path.length - 2),
532507
collectionGroup: path[path.length - 2],
@@ -536,7 +511,7 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
536511
noDocument: legacyDocument.noDocument,
537512
document: legacyDocument.document,
538513
hasCommittedMutations: !!legacyDocument.hasCommittedMutations
539-
} satisfies Omit<DbRemoteDocument, 'documentType'> as DbRemoteDocument;
514+
};
540515
writes.push(remoteDocumentStore.put(dbRemoteDocument));
541516
})
542517
.next(() => PersistencePromise.waitFor(writes));
@@ -830,30 +805,11 @@ function extractKey(remoteDoc: DbRemoteDocumentLegacy): DocumentKey {
830805
}
831806
}
832807

833-
function createRemoteDocumentStoreDocumentTypeIndex(txn: IDBTransaction): void {
808+
function createRemoteDocumentCollectionIndex(txn: IDBTransaction): void {
834809
txn
835810
.objectStore(DbRemoteDocumentStore)
836811
.createIndex(
837812
DbRemoteDocumentCollectionIndex,
838-
DbRemoteDocumentCollectionIndexPath,
839-
{ unique: false }
813+
DbRemoteDocumentCollectionIndexPath
840814
);
841815
}
842-
843-
function backfillRemoteDocumentStoreDocumentType(
844-
txn: SimpleDbTransaction
845-
): PersistencePromise<void> {
846-
const remoteDocumentStore = txn.store<DbRemoteDocumentKey, DbRemoteDocument>(
847-
DbRemoteDocumentStore
848-
);
849-
850-
return remoteDocumentStore.iterate((key, doc) => {
851-
doc.documentType = doc.noDocument
852-
? DbRemoteDocumentType.NoDocument
853-
: doc.unknownDocument
854-
? DbRemoteDocumentType.UnknownDocument
855-
: DbRemoteDocumentType.FoundDocument;
856-
857-
return remoteDocumentStore.put(doc);
858-
});
859-
}

packages/firestore/src/local/indexeddb_sentinels.ts

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,25 +173,58 @@ export const DbRemoteDocumentCollectionGroupIndexPath = [
173173
];
174174

175175
/**
176-
* An index that provides access to documents by collection, read time, and
177-
* document type.
176+
* An index that provides access to documents by the primary key, but filtering
177+
* out all but "known" documents.
178178
*
179179
* This index is used as an optimization in getDocumentsMatchingQuery() to
180180
* avoid visiting "tombstone" entries which are impossible to be part of the
181-
* query result anyway.
181+
* query result anyway. For background information and motivation, see
182+
* https://github.com/firebase/firebase-android-sdk/issues/7295.
183+
*
184+
* This "filtering" is achieved as a side effect of including a property from
185+
* the "document" property in the index's key. Since the "document" property
186+
* is _only_ present for "found" documents, indexing one of its keys has the
187+
* convenient side effect of excluding all entries that lack a "document"
188+
* property. The "createTime" sub-property was chosen for several reasons:
189+
*
190+
* 1. Its length is strictly bounded. This provides a predictable O(1) size of
191+
* the index entries relative to the size of the primary key. This is because
192+
* the "createTime" values are RFC3339 strings (e.g. "2023-09-24T15:30:00Z").
193+
* 2. It is easy to pick a "smallest" and "largest" value for "createTime",
194+
* which is critical when creating bounded IndexedDB key ranges. Namely,
195+
* "" (the empty string) is smaller than any value and "\uffff" is larger
196+
* than any value, guaranteed. This cannot be said about arbitrary strings.
182197
*
183-
* See https://github.com/firebase/firebase-android-sdk/issues/7295.
184198
*/
185199
export const DbRemoteDocumentCollectionIndex = 'collectionIndex';
186200

187201
export const DbRemoteDocumentCollectionIndexPath = [
188-
'documentType',
202+
'document.createTime',
189203
'prefixPath',
190204
'collectionGroup',
191205
'readTime',
192206
'documentId'
193207
];
194208

209+
export const MIN_CREATE_TIME = '' as const;
210+
export const MAX_CREATE_TIME = '\uFFFF' as const;
211+
212+
/**
213+
* A key in the `DbRemoteDocumentCollectionIndex` index.
214+
*
215+
* Note that the "document.createTime" entry is restricted to the min and max
216+
* values as they are only intended to be used for _filtering_ entries that
217+
* contain the "document" property and are not intended for general-purpose
218+
* filtering.
219+
*/
220+
export type DbRemoteDocumentCollectionIndexKey = [
221+
/** document.createTime */ typeof MIN_CREATE_TIME | typeof MAX_CREATE_TIME,
222+
/** path to collection */ string[],
223+
/** collection group */ string,
224+
/** read time */ DbTimestampKey,
225+
/** document ID */ string
226+
];
227+
195228
export const DbRemoteDocumentGlobalStore = 'remoteDocumentGlobal';
196229

197230
export const DbRemoteDocumentGlobalKey = 'remoteDocumentGlobalKey';

packages/firestore/src/local/local_serializer.ts

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ import {
6666
DbNamedQuery,
6767
DbQuery,
6868
DbRemoteDocument,
69-
DbRemoteDocumentType,
7069
DbTarget,
7170
DbTimestamp
7271
} from './indexeddb_schema';
@@ -118,48 +117,30 @@ export function toDbRemoteDocument(
118117
document: MutableDocument
119118
): DbRemoteDocument {
120119
const key = document.key;
121-
return {
120+
const remoteDoc: DbRemoteDocument = {
122121
prefixPath: key.getCollectionPath().popLast().toArray(),
123122
collectionGroup: key.collectionGroup,
124123
documentId: key.path.lastSegment(),
125124
readTime: toDbTimestampKey(document.readTime),
126-
hasCommittedMutations: document.hasCommittedMutations,
127-
...toDbRemoteDocumentDocumentSpecificComponents(localSerializer, document)
125+
hasCommittedMutations: document.hasCommittedMutations
128126
};
129-
}
130127

131-
function toDbRemoteDocumentDocumentSpecificComponents(
132-
localSerializer: LocalSerializer,
133-
document: MutableDocument
134-
): Pick<
135-
DbRemoteDocument,
136-
'documentType' | 'document' | 'noDocument' | 'unknownDocument'
137-
> {
138128
if (document.isFoundDocument()) {
139-
return {
140-
documentType: DbRemoteDocumentType.FoundDocument,
141-
document: toDocument(localSerializer.remoteSerializer, document)
129+
remoteDoc.document = toDocument(localSerializer.remoteSerializer, document);
130+
} else if (document.isNoDocument()) {
131+
remoteDoc.noDocument = {
132+
path: key.path.toArray(),
133+
readTime: toDbTimestamp(document.version)
142134
};
143-
}
144-
if (document.isNoDocument()) {
145-
return {
146-
documentType: DbRemoteDocumentType.NoDocument,
147-
noDocument: {
148-
path: document.key.path.toArray(),
149-
readTime: toDbTimestamp(document.version)
150-
}
151-
};
152-
}
153-
if (document.isUnknownDocument()) {
154-
return {
155-
documentType: DbRemoteDocumentType.UnknownDocument,
156-
unknownDocument: {
157-
path: document.key.path.toArray(),
158-
version: toDbTimestamp(document.version)
159-
}
135+
} else if (document.isUnknownDocument()) {
136+
remoteDoc.unknownDocument = {
137+
path: key.path.toArray(),
138+
version: toDbTimestamp(document.version)
160139
};
140+
} else {
141+
return fail(0xe230, 'Unexpected Document', { document });
161142
}
162-
fail(0xe230, 'Unexpected Document', { document });
143+
return remoteDoc;
163144
}
164145

165146
export function toDbTimestampKey(

0 commit comments

Comments
 (0)