Skip to content

Commit 3bcf756

Browse files
Merge
2 parents 8556bcc + 08907db commit 3bcf756

23 files changed

+750
-233
lines changed

packages/firestore/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# Unreleased
2+
- [changed] Improved how Firestore handles idle queries to reduce the cost of
3+
re-listening within 30 minutes.
4+
- [changed] Improved offline performance with many outstanding writes.
5+
6+
# 0.6.0
27
- [fixed] Fixed an issue where queries returned fewer results than they should,
38
caused by documents that were cached as deleted when they should not have
49
been (firebase/firebase-ios-sdk#1548). Because some cache data is cleared,

packages/firestore/src/core/sync_engine.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { Query } from './query';
4545
import { SnapshotVersion } from './snapshot_version';
4646
import { TargetIdGenerator } from './target_id_generator';
4747
import { Transaction } from './transaction';
48+
<<<<<<< HEAD
4849
import {
4950
BatchId,
5051
MutationBatchState,
@@ -53,6 +54,9 @@ import {
5354
ProtoByteString,
5455
TargetId
5556
} from './types';
57+
=======
58+
import { BatchId, OnlineState, TargetId } from './types';
59+
>>>>>>> master
5660
import {
5761
AddedLimboDocument,
5862
LimboDocumentChange,
@@ -88,12 +92,6 @@ class QueryView {
8892
* stream to identify this query.
8993
*/
9094
public targetId: TargetId,
91-
/**
92-
* An identifier from the datastore backend that indicates the last state
93-
* of the results that was received. This can be used to indicate where
94-
* to continue receiving new doc changes for the query.
95-
*/
96-
public resumeToken: ProtoByteString,
9795
/**
9896
* The view is responsible for computing the final merged truth of what
9997
* docs are in the query. It gets notified of local and remote changes,
@@ -274,6 +272,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
274272
'applyChanges for new view should always return a snapshot'
275273
);
276274

275+
<<<<<<< HEAD
277276
const data = new QueryView(
278277
query,
279278
queryData.targetId,
@@ -310,6 +309,17 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
310309
);
311310
}
312311
return viewSnapshot;
312+
=======
313+
const data = new QueryView(query, queryData.targetId, view);
314+
this.queryViewsByQuery.set(query, data);
315+
this.queryViewsByTarget[queryData.targetId] = data;
316+
this.viewHandler!([viewChange.snapshot!]);
317+
this.remoteStore.listen(queryData);
318+
});
319+
})
320+
.then(() => {
321+
return queryData.targetId;
322+
>>>>>>> master
313323
});
314324
});
315325
}

packages/firestore/src/local/indexeddb_mutation_queue.ts

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Timestamp } from '../api/timestamp';
1818
import { User } from '../auth/user';
1919
import { Query } from '../core/query';
2020
import { BatchId, ProtoByteString } from '../core/types';
21+
import { DocumentKeySet } from '../model/collections';
2122
import { DocumentKey } from '../model/document_key';
2223
import { Mutation } from '../model/mutation';
2324
import { BATCHID_UNKNOWN, MutationBatch } from '../model/mutation_batch';
@@ -40,8 +41,13 @@ import { LocalSerializer } from './local_serializer';
4041
import { MutationQueue } from './mutation_queue';
4142
import { PersistenceTransaction } from './persistence';
4243
import { PersistencePromise } from './persistence_promise';
44+
<<<<<<< HEAD
4345
import { SimpleDb, SimpleDbStore } from './simple_db';
4446
import { DocumentKeySet } from '../model/collections';
47+
=======
48+
import { SimpleDbStore } from './simple_db';
49+
import { IndexedDbPersistence } from './indexeddb_persistence';
50+
>>>>>>> master
4551

4652
/** A mutation queue for a specific user, backed by IndexedDB. */
4753
export class IndexedDbMutationQueue implements MutationQueue {
@@ -342,6 +348,50 @@ export class IndexedDbMutationQueue implements MutationQueue {
342348
.next(() => results);
343349
}
344350

351+
getAllMutationBatchesAffectingDocumentKeys(
352+
transaction: PersistenceTransaction,
353+
documentKeys: DocumentKeySet
354+
): PersistencePromise<MutationBatch[]> {
355+
let uniqueBatchIDs = new SortedSet<BatchId>(primitiveComparator);
356+
357+
const promises: Array<PersistencePromise<void>> = [];
358+
documentKeys.forEach(documentKey => {
359+
const indexStart = DbDocumentMutation.prefixForPath(
360+
this.userId,
361+
documentKey.path
362+
);
363+
const range = IDBKeyRange.lowerBound(indexStart);
364+
365+
const promise = documentMutationsStore(transaction).iterate(
366+
{ range },
367+
(indexKey, _, control) => {
368+
const [userID, encodedPath, batchID] = indexKey;
369+
370+
// Only consider rows matching exactly the specific key of
371+
// interest. Note that because we order by path first, and we
372+
// order terminators before path separators, we'll encounter all
373+
// the index rows for documentKey contiguously. In particular, all
374+
// the rows for documentKey will occur before any rows for
375+
// documents nested in a subcollection beneath documentKey so we
376+
// can stop as soon as we hit any such row.
377+
const path = EncodedResourcePath.decode(encodedPath);
378+
if (userID !== this.userId || !documentKey.path.isEqual(path)) {
379+
control.done();
380+
return;
381+
}
382+
383+
uniqueBatchIDs = uniqueBatchIDs.add(batchID);
384+
}
385+
);
386+
387+
promises.push(promise);
388+
});
389+
390+
return PersistencePromise.waitFor(promises).next(() =>
391+
this.lookupMutationBatches(transaction, uniqueBatchIDs)
392+
);
393+
}
394+
345395
getAllMutationBatchesAffectingQuery(
346396
transaction: PersistenceTransaction,
347397
query: Query
@@ -393,6 +443,7 @@ export class IndexedDbMutationQueue implements MutationQueue {
393443
}
394444
uniqueBatchIDs = uniqueBatchIDs.add(batchID);
395445
})
446+
<<<<<<< HEAD
396447
.next(() => {
397448
const results: MutationBatch[] = [];
398449
const promises: Array<PersistencePromise<void>> = [];
@@ -421,6 +472,36 @@ export class IndexedDbMutationQueue implements MutationQueue {
421472
});
422473
return PersistencePromise.waitFor(promises).next(() => results);
423474
});
475+
=======
476+
.next(() => this.lookupMutationBatches(transaction, uniqueBatchIDs));
477+
}
478+
479+
private lookupMutationBatches(
480+
transaction: PersistenceTransaction,
481+
batchIDs: SortedSet<BatchId>
482+
): PersistencePromise<MutationBatch[]> {
483+
const results: MutationBatch[] = [];
484+
const promises: Array<PersistencePromise<void>> = [];
485+
// TODO(rockwood): Implement this using iterate.
486+
batchIDs.forEach(batchID => {
487+
const mutationKey = this.keyForBatchId(batchID);
488+
promises.push(
489+
mutationsStore(transaction)
490+
.get(mutationKey)
491+
.next(mutation => {
492+
if (mutation === null) {
493+
fail(
494+
'Dangling document-mutation reference found, ' +
495+
'which points to ' +
496+
mutationKey
497+
);
498+
}
499+
results.push(this.serializer.fromDbMutationBatch(mutation!));
500+
})
501+
);
502+
});
503+
return PersistencePromise.waitFor(promises).next(() => results);
504+
>>>>>>> master
424505
}
425506

426507
removeMutationBatches(
@@ -567,7 +648,7 @@ function convertStreamToken(token: ProtoByteString): string {
567648
function mutationsStore(
568649
txn: PersistenceTransaction
569650
): SimpleDbStore<DbMutationBatchKey, DbMutationBatch> {
570-
return SimpleDb.getStore<DbMutationBatchKey, DbMutationBatch>(
651+
return IndexedDbPersistence.getStore<DbMutationBatchKey, DbMutationBatch>(
571652
txn,
572653
DbMutationBatch.store
573654
);
@@ -579,10 +660,10 @@ function mutationsStore(
579660
function documentMutationsStore(
580661
txn: PersistenceTransaction
581662
): SimpleDbStore<DbDocumentMutationKey, DbDocumentMutation> {
582-
return SimpleDb.getStore<DbDocumentMutationKey, DbDocumentMutation>(
583-
txn,
584-
DbDocumentMutation.store
585-
);
663+
return IndexedDbPersistence.getStore<
664+
DbDocumentMutationKey,
665+
DbDocumentMutation
666+
>(txn, DbDocumentMutation.store);
586667
}
587668

588669
/**
@@ -591,7 +672,7 @@ function documentMutationsStore(
591672
function mutationQueuesStore(
592673
txn: PersistenceTransaction
593674
): SimpleDbStore<DbMutationQueueKey, DbMutationQueue> {
594-
return SimpleDb.getStore<DbMutationQueueKey, DbMutationQueue>(
675+
return IndexedDbPersistence.getStore<DbMutationQueueKey, DbMutationQueue>(
595676
txn,
596677
DbMutationQueue.store
597678
);

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import { User } from '../auth/user';
1818
import { DatabaseInfo } from '../core/database_info';
1919
import { JsonProtoSerializer } from '../remote/serializer';
20-
import { assert } from '../util/assert';
20+
import { assert, fail } from '../util/assert';
2121
import { Code, FirestoreError } from '../util/error';
2222
import * as log from '../util/log';
2323

@@ -35,19 +35,26 @@ import {
3535
} from './indexeddb_schema';
3636
import { LocalSerializer } from './local_serializer';
3737
import { MutationQueue } from './mutation_queue';
38+
<<<<<<< HEAD
3839
import {
3940
Persistence,
4041
PersistenceTransaction,
4142
PrimaryStateListener
4243
} from './persistence';
44+
=======
45+
import { Persistence, PersistenceTransaction } from './persistence';
46+
>>>>>>> master
4347
import { PersistencePromise } from './persistence_promise';
4448
import { QueryCache } from './query_cache';
4549
import { RemoteDocumentCache } from './remote_document_cache';
4650
import { SimpleDb, SimpleDbStore, SimpleDbTransaction } from './simple_db';
51+
<<<<<<< HEAD
4752
import { Platform } from '../platform/platform';
4853
import { AsyncQueue, TimerId } from '../util/async_queue';
4954
import { ClientId } from './shared_client_state';
5055
import { CancelablePromise } from '../util/promise';
56+
=======
57+
>>>>>>> master
5158

5259
const LOG_TAG = 'IndexedDbPersistence';
5360

@@ -79,9 +86,17 @@ const UNSUPPORTED_PLATFORM_ERROR_MSG =
7986
' IndexedDB or is known to have an incomplete implementation. Offline' +
8087
' persistence has been disabled.';
8188

89+
<<<<<<< HEAD
8290
// The format of the LocalStorage key that stores zombied client is:
8391
// firestore_zombie_<persistence_prefix>_<instance_key>
8492
const ZOMBIED_CLIENTS_KEY_PREFIX = 'firestore_zombie';
93+
=======
94+
export class IndexedDbTransaction extends PersistenceTransaction {
95+
constructor(readonly simpleDbTransaction: SimpleDbTransaction) {
96+
super();
97+
}
98+
}
99+
>>>>>>> master
85100

86101
/**
87102
* An IndexedDB-backed instance of Persistence. Data is stored persistently
@@ -115,6 +130,17 @@ const ZOMBIED_CLIENTS_KEY_PREFIX = 'firestore_zombie';
115130
* TODO(multitab): Update this comment with multi-tab changes.
116131
*/
117132
export class IndexedDbPersistence implements Persistence {
133+
static getStore<Key extends IDBValidKey, Value>(
134+
txn: PersistenceTransaction,
135+
store: string
136+
): SimpleDbStore<Key, Value> {
137+
if (txn instanceof IndexedDbTransaction) {
138+
return SimpleDb.getStore<Key, Value>(txn.simpleDbTransaction, store);
139+
} else {
140+
fail('IndexedDbPersistence must use instances of IndexedDbTransaction');
141+
}
142+
}
143+
118144
/**
119145
* The name of the main (and currently only) IndexedDB database. this name is
120146
* appended to the prefix provided to the IndexedDbPersistence constructor.
@@ -468,10 +494,14 @@ export class IndexedDbPersistence implements Persistence {
468494

469495
runTransaction<T>(
470496
action: string,
497+
<<<<<<< HEAD
471498
requirePrimaryLease: boolean,
472499
transactionOperation: (
473500
transaction: PersistenceTransaction
474501
) => PersistencePromise<T>
502+
=======
503+
operation: (transaction: IndexedDbTransaction) => PersistencePromise<T>
504+
>>>>>>> master
475505
): Promise<T> {
476506
// TODO(multitab): Consider removing `requirePrimaryLease` and exposing
477507
// three different write modes (readonly, readwrite, readwrite_primary).
@@ -483,6 +513,7 @@ export class IndexedDbPersistence implements Persistence {
483513

484514
// Do all transactions as readwrite against all object stores, since we
485515
// are the only reader/writer.
516+
<<<<<<< HEAD
486517
return this.simpleDb.runTransaction('readwrite', ALL_STORES, txn => {
487518
if (requirePrimaryLease) {
488519
// While we merely verify that we have (or can acquire) the lease
@@ -516,6 +547,18 @@ export class IndexedDbPersistence implements Persistence {
516547
);
517548
}
518549
});
550+
=======
551+
return this.simpleDb.runTransaction(
552+
'readwrite',
553+
ALL_STORES,
554+
simpleDbTxn => {
555+
// Verify that we still have the owner lease as part of every transaction.
556+
return this.ensureOwnerLease(simpleDbTxn).next(() =>
557+
operation(new IndexedDbTransaction(simpleDbTxn))
558+
);
559+
}
560+
);
561+
>>>>>>> master
519562
}
520563

521564
/**
@@ -616,6 +659,7 @@ export class IndexedDbPersistence implements Persistence {
616659
return true;
617660
}
618661

662+
<<<<<<< HEAD
619663
private attachVisibilityHandler(): void {
620664
if (
621665
this.document !== null &&
@@ -627,6 +671,27 @@ export class IndexedDbPersistence implements Persistence {
627671
return this.updateClientMetadataAndTryBecomePrimary();
628672
});
629673
};
674+
=======
675+
/**
676+
* Schedules a recurring timer to update the owner lease timestamp to prevent
677+
* other tabs from taking the lease.
678+
*/
679+
private scheduleOwnerLeaseRefreshes(): void {
680+
// NOTE: This doesn't need to be scheduled on the async queue and doing so
681+
// would increase the chances of us not refreshing on time if the queue is
682+
// backed up for some reason.
683+
this.ownerLeaseRefreshHandle = setInterval(() => {
684+
const txResult = this.simpleDb.runTransaction(
685+
'readwrite',
686+
ALL_STORES,
687+
txn => {
688+
// NOTE: We don't need to validate the current owner contents, since
689+
// runTransaction does that automatically.
690+
const store = txn.store<DbOwnerKey, DbOwner>(DbOwner.store);
691+
return store.put('owner', new DbOwner(this.ownerId, Date.now()));
692+
}
693+
);
694+
>>>>>>> master
630695

631696
this.document.addEventListener(
632697
'visibilitychange',

0 commit comments

Comments
 (0)