Skip to content

Commit ffb8f6f

Browse files
Merge
2 parents c29af64 + 434d87e commit ffb8f6f

29 files changed

+803
-154
lines changed

.changeset/green-parents-compare.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@firebase/firestore": patch
3+
---
4+
5+
On browsers that support IndexedDB V3, we now invoke `transaction.commit()` to speed up data processing.

.changeset/lemon-ravens-sneeze.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@firebase/firestore": patch
3+
---
4+
5+
Queries are now send to the backend before the SDK starts local processing, which reduces overall Query latency.

packages/firestore-compat/tools/console.build.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,15 @@ const es5OutputOptions = {
5757
format: 'iife'
5858
};
5959

60-
const PREFIX = `
61-
goog.module('firestore');
62-
exports = eval(`;
63-
64-
const POSTFIX = ` + '${EXPORTNAME};');`;
60+
const POSTFIX = `window['${EXPORTNAME}']=${EXPORTNAME};`;
6561

6662
async function build() {
6763
const es5Bundle = await rollup.rollup(es5InputOptions);
6864
const {
6965
output: [{ code }]
7066
} = await es5Bundle.generate(es5OutputOptions);
7167

72-
const output = `${PREFIX}${JSON.stringify(String(code))}${POSTFIX}`;
68+
const output = `${String(code)}${POSTFIX}`;
7369

7470
if (!fs.existsSync(OUTPUT_FOLDER)) {
7571
fs.mkdirSync(OUTPUT_FOLDER);

packages/firestore/src/core/sync_engine_impl.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,9 @@ export async function syncEngineListen(
314314
syncEngineImpl.localStore,
315315
queryToTarget(query)
316316
);
317+
if (syncEngineImpl.isPrimaryClient) {
318+
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
319+
}
317320

318321
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(
319322
targetData.targetId
@@ -325,9 +328,6 @@ export async function syncEngineListen(
325328
targetId,
326329
status === 'current'
327330
);
328-
if (syncEngineImpl.isPrimaryClient) {
329-
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
330-
}
331331
}
332332

333333
return viewSnapshot;

packages/firestore/src/local/indexeddb_index_manager.ts

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

18+
import { User } from '../auth/user';
1819
import { Target } from '../core/target';
1920
import {
2021
documentKeySet,
@@ -31,26 +32,49 @@ import {
3132
encodeResourcePath
3233
} from './encoded_resource_path';
3334
import { IndexManager } from './index_manager';
34-
import { DbCollectionParent, DbCollectionParentKey } from './indexeddb_schema';
35+
import {
36+
DbCollectionParent,
37+
DbCollectionParentKey,
38+
DbIndexConfiguration,
39+
DbIndexConfigurationKey,
40+
DbIndexEntry,
41+
DbIndexEntryKey,
42+
DbIndexState,
43+
DbIndexStateKey
44+
} from './indexeddb_schema';
3545
import { getStore } from './indexeddb_transaction';
46+
import {
47+
fromDbIndexConfiguration,
48+
toDbIndexConfiguration,
49+
toDbIndexState
50+
} from './local_serializer';
3651
import { MemoryCollectionParentIndex } from './memory_index_manager';
3752
import { PersistencePromise } from './persistence_promise';
3853
import { PersistenceTransaction } from './persistence_transaction';
3954
import { SimpleDbStore } from './simple_db';
4055

4156
/**
4257
* A persisted implementation of IndexManager.
58+
*
59+
* PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
60+
* data as it supports multi-tab access.
4361
*/
4462
export class IndexedDbIndexManager implements IndexManager {
4563
/**
4664
* An in-memory copy of the index entries we've already written since the SDK
4765
* launched. Used to avoid re-writing the same entry repeatedly.
4866
*
49-
* This is *NOT* a complete cache of what's in persistence and so can never be used to
50-
* satisfy reads.
67+
* This is *NOT* a complete cache of what's in persistence and so can never be
68+
* used to satisfy reads.
5169
*/
5270
private collectionParentsCache = new MemoryCollectionParentIndex();
5371

72+
private uid: string;
73+
74+
constructor(private user: User) {
75+
this.uid = user.uid || '';
76+
}
77+
5478
/**
5579
* Adds a new entry to the collection parent index.
5680
*
@@ -114,16 +138,43 @@ export class IndexedDbIndexManager implements IndexManager {
114138
transaction: PersistenceTransaction,
115139
index: FieldIndex
116140
): PersistencePromise<void> {
117-
// TODO(indexing): Implement
118-
return PersistencePromise.resolve();
141+
// TODO(indexing): Verify that the auto-incrementing index ID works in
142+
// Safari & Firefox.
143+
const indexes = indexConfigurationStore(transaction);
144+
const dbIndex = toDbIndexConfiguration(index);
145+
delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
146+
return indexes.add(dbIndex).next();
119147
}
120148

121149
deleteFieldIndex(
122150
transaction: PersistenceTransaction,
123151
index: FieldIndex
124152
): PersistencePromise<void> {
125-
// TODO(indexing): Implement
126-
return PersistencePromise.resolve();
153+
const indexes = indexConfigurationStore(transaction);
154+
const states = indexStateStore(transaction);
155+
const entries = indexEntriesStore(transaction);
156+
return indexes
157+
.delete(index.indexId)
158+
.next(() =>
159+
states.delete(
160+
IDBKeyRange.bound(
161+
[index.indexId],
162+
[index.indexId + 1],
163+
/*lowerOpen=*/ false,
164+
/*upperOpen=*/ true
165+
)
166+
)
167+
)
168+
.next(() =>
169+
entries.delete(
170+
IDBKeyRange.bound(
171+
[index.indexId],
172+
[index.indexId + 1],
173+
/*lowerOpen=*/ false,
174+
/*upperOpen=*/ true
175+
)
176+
)
177+
);
127178
}
128179

129180
getDocumentsMatchingTarget(
@@ -147,24 +198,71 @@ export class IndexedDbIndexManager implements IndexManager {
147198
transaction: PersistenceTransaction,
148199
collectionGroup?: string
149200
): PersistencePromise<FieldIndex[]> {
150-
// TODO(indexing): Implement
151-
return PersistencePromise.resolve<FieldIndex[]>([]);
201+
const indexes = indexConfigurationStore(transaction);
202+
const states = indexStateStore(transaction);
203+
204+
return (
205+
collectionGroup
206+
? indexes.loadAll(
207+
DbIndexConfiguration.collectionGroupIndex,
208+
IDBKeyRange.bound(collectionGroup, collectionGroup)
209+
)
210+
: indexes.loadAll()
211+
).next(indexConfigs => {
212+
const result: FieldIndex[] = [];
213+
return PersistencePromise.forEach(
214+
indexConfigs,
215+
(indexConfig: DbIndexConfiguration) => {
216+
return states
217+
.get([indexConfig.indexId!, this.uid])
218+
.next(indexState => {
219+
result.push(fromDbIndexConfiguration(indexConfig, indexState));
220+
});
221+
}
222+
).next(() => result);
223+
});
152224
}
153225

154226
getNextCollectionGroupToUpdate(
155227
transaction: PersistenceTransaction
156228
): PersistencePromise<string | null> {
157-
// TODO(indexing): Implement
158-
return PersistencePromise.resolve<string | null>(null);
229+
return this.getFieldIndexes(transaction).next(indexes => {
230+
if (indexes.length === 0) {
231+
return null;
232+
}
233+
indexes.sort(
234+
(l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber
235+
);
236+
return indexes[0].collectionGroup;
237+
});
159238
}
160239

161240
updateCollectionGroup(
162241
transaction: PersistenceTransaction,
163242
collectionGroup: string,
164243
offset: IndexOffset
165244
): PersistencePromise<void> {
166-
// TODO(indexing): Implement
167-
return PersistencePromise.resolve();
245+
const indexes = indexConfigurationStore(transaction);
246+
const states = indexStateStore(transaction);
247+
return this.getNextSequenceNumber(transaction).next(nextSequenceNumber =>
248+
indexes
249+
.loadAll(
250+
DbIndexConfiguration.collectionGroupIndex,
251+
IDBKeyRange.bound(collectionGroup, collectionGroup)
252+
)
253+
.next(configs =>
254+
PersistencePromise.forEach(configs, (config: DbIndexConfiguration) =>
255+
states.put(
256+
toDbIndexState(
257+
config.indexId!,
258+
this.user,
259+
nextSequenceNumber,
260+
offset
261+
)
262+
)
263+
)
264+
)
265+
);
168266
}
169267

170268
updateIndexEntries(
@@ -174,6 +272,26 @@ export class IndexedDbIndexManager implements IndexManager {
174272
// TODO(indexing): Implement
175273
return PersistencePromise.resolve();
176274
}
275+
276+
private getNextSequenceNumber(
277+
transaction: PersistenceTransaction
278+
): PersistencePromise<number> {
279+
let nextSequenceNumber = 1;
280+
const states = indexStateStore(transaction);
281+
return states
282+
.iterate(
283+
{
284+
index: DbIndexState.sequenceNumberIndex,
285+
reverse: true,
286+
range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
287+
},
288+
(_, state, controller) => {
289+
controller.done();
290+
nextSequenceNumber = state.sequenceNumber + 1;
291+
}
292+
)
293+
.next(() => nextSequenceNumber);
294+
}
177295
}
178296

179297
/**
@@ -188,3 +306,33 @@ function collectionParentsStore(
188306
DbCollectionParent.store
189307
);
190308
}
309+
310+
/**
311+
* Helper to get a typed SimpleDbStore for the index entry object store.
312+
*/
313+
function indexEntriesStore(
314+
txn: PersistenceTransaction
315+
): SimpleDbStore<DbIndexEntryKey, DbIndexEntry> {
316+
return getStore<DbIndexEntryKey, DbIndexEntry>(txn, DbIndexEntry.store);
317+
}
318+
319+
/**
320+
* Helper to get a typed SimpleDbStore for the index configuration object store.
321+
*/
322+
function indexConfigurationStore(
323+
txn: PersistenceTransaction
324+
): SimpleDbStore<DbIndexConfigurationKey, DbIndexConfiguration> {
325+
return getStore<DbIndexConfigurationKey, DbIndexConfiguration>(
326+
txn,
327+
DbIndexConfiguration.store
328+
);
329+
}
330+
331+
/**
332+
* Helper to get a typed SimpleDbStore for the index state object store.
333+
*/
334+
function indexStateStore(
335+
txn: PersistenceTransaction
336+
): SimpleDbStore<DbIndexStateKey, DbIndexState> {
337+
return getStore<DbIndexStateKey, DbIndexState>(txn, DbIndexState.store);
338+
}

0 commit comments

Comments
 (0)