Skip to content

Commit

Permalink
Improve performance by using getAll on IDBIndex. (#6975)
Browse files Browse the repository at this point in the history
* Improve performance by using getAll on IDBIndex.

* Improve performance by using getAll on IDBIndex.

* IndexedDBShim does not implement GetAll on indexes correctly.

* Pretty
  • Loading branch information
tom-andersen authored Dec 5, 2023
1 parent ac10cc3 commit 8586373
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 59 deletions.
9 changes: 6 additions & 3 deletions packages/firestore/src/local/simple_db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,12 @@ export class SimpleDbStore<
): PersistencePromise<ValueType[]> {
const iterateOptions = this.options(indexOrRange, range);
// Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
// 20% faster. Unfortunately, getAll() does not support custom indices.
if (!iterateOptions.index && typeof this.store.getAll === 'function') {
const request = this.store.getAll(iterateOptions.range);
// 20% faster.
const store = iterateOptions.index
? this.store.index(iterateOptions.index)
: this.store;
if (typeof store.getAll === 'function') {
const request = store.getAll(iterateOptions.range);
return new PersistencePromise((resolve, reject) => {
request.onerror = (event: Event) => {
reject((event.target as IDBRequest).error!);
Expand Down
117 changes: 61 additions & 56 deletions packages/firestore/test/unit/local/simple_db.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,67 +531,72 @@ describe('SimpleDb', () => {
);
});

it('correctly sorts keys with nested arrays', async function (this: Context) {
// This test verifies that the sorting in IndexedDb matches
// `dbKeyComparator()`

const keys = [
'a/a/a/a/a/a/a/a/a/a',
'a/b/a/a/a/a/a/a/a/b',
'b/a/a/a/a/a/a/a/a/a',
'b/b/a/a/a/a/a/a/a/b',
'b/b/a/a/a/a/a/a',
'b/b/b/a/a/a/a/b',
'c/c/a/a/a/a',
'd/d/a/a',
'e/e'
].map(k => DocumentKey.fromPath(k));

interface ValueType {
prefixPath: string[];
collectionId: string;
documentId: string;
}

const expectedOrder = [...keys];
expectedOrder.sort(dbKeyComparator);
// Note: This tests is failing under `IndexedDBShim`.
// eslint-disable-next-line no-restricted-properties
(isIndexedDbMock() ? it.skip : it)(
'correctly sorts keys with nested arrays',
async function (this: Context) {
// This test verifies that the sorting in IndexedDb matches
// `dbKeyComparator()`

const keys = [
'a/a/a/a/a/a/a/a/a/a',
'a/b/a/a/a/a/a/a/a/b',
'b/a/a/a/a/a/a/a/a/a',
'b/b/a/a/a/a/a/a/a/b',
'b/b/a/a/a/a/a/a',
'b/b/b/a/a/a/a/b',
'c/c/a/a/a/a',
'd/d/a/a',
'e/e'
].map(k => DocumentKey.fromPath(k));

interface ValueType {
prefixPath: string[];
collectionId: string;
documentId: string;
}

const actualOrder = await db.runTransaction(
this.test!.fullTitle(),
'readwrite',
['docs'],
txn => {
const store = txn.store<string[], ValueType>('docs');

const writes = keys.map(k => {
const path = k.path.toArray();
return store.put(k.path.toArray(), {
prefixPath: path.slice(0, path.length - 2),
collectionId: path[path.length - 2],
documentId: path[path.length - 1]
const expectedOrder = [...keys];
expectedOrder.sort(dbKeyComparator);

const actualOrder = await db.runTransaction(
this.test!.fullTitle(),
'readwrite',
['docs'],
txn => {
const store = txn.store<string[], ValueType>('docs');

const writes = keys.map(k => {
const path = k.path.toArray();
return store.put(k.path.toArray(), {
prefixPath: path.slice(0, path.length - 2),
collectionId: path[path.length - 2],
documentId: path[path.length - 1]
});
});
});

return PersistencePromise.waitFor(writes).next(() =>
store
.loadAll('path')
.next(keys =>
keys.map(k =>
DocumentKey.fromSegments([
...k.prefixPath,
k.collectionId,
k.documentId
])
return PersistencePromise.waitFor(writes).next(() =>
store
.loadAll('path')
.next(keys =>
keys.map(k =>
DocumentKey.fromSegments([
...k.prefixPath,
k.collectionId,
k.documentId
])
)
)
)
);
}
);
);
}
);

expect(actualOrder.map(k => k.toString())).to.deep.equal(
expectedOrder.map(k => k.toString())
);
});
expect(actualOrder.map(k => k.toString())).to.deep.equal(
expectedOrder.map(k => k.toString())
);
}
);

it('retries transactions', async function (this: Context) {
let attemptCount = 0;
Expand Down

0 comments on commit 8586373

Please sign in to comment.