Skip to content

Merge #1073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged

Merge #1073

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 1 addition & 23 deletions packages/firestore/src/core/sync_engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,13 @@ import { Query } from './query';
import { SnapshotVersion } from './snapshot_version';
import { TargetIdGenerator } from './target_id_generator';
import { Transaction } from './transaction';
<<<<<<< HEAD
import {
BatchId,
MutationBatchState,
OnlineState,
OnlineStateSource,
ProtoByteString,
TargetId
} from './types';
=======
import { BatchId, OnlineState, TargetId } from './types';
>>>>>>> master
import {
AddedLimboDocument,
LimboDocumentChange,
Expand Down Expand Up @@ -272,13 +267,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
'applyChanges for new view should always return a snapshot'
);

<<<<<<< HEAD
const data = new QueryView(
query,
queryData.targetId,
queryData.resumeToken,
view
);
const data = new QueryView(query, queryData.targetId, view);
this.queryViewsByQuery.set(query, data);
this.queryViewsByTarget[queryData.targetId] = data;
return viewChange.snapshot!;
Expand Down Expand Up @@ -309,17 +298,6 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
);
}
return viewSnapshot;
=======
const data = new QueryView(query, queryData.targetId, view);
this.queryViewsByQuery.set(query, data);
this.queryViewsByTarget[queryData.targetId] = data;
this.viewHandler!([viewChange.snapshot!]);
this.remoteStore.listen(queryData);
});
})
.then(() => {
return queryData.targetId;
>>>>>>> master
});
});
}
Expand Down
49 changes: 9 additions & 40 deletions packages/firestore/src/local/indexeddb_mutation_queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,8 @@ import { LocalSerializer } from './local_serializer';
import { MutationQueue } from './mutation_queue';
import { PersistenceTransaction } from './persistence';
import { PersistencePromise } from './persistence_promise';
<<<<<<< HEAD
import { SimpleDb, SimpleDbStore } from './simple_db';
import { DocumentKeySet } from '../model/collections';
=======
import { SimpleDbStore } from './simple_db';
import { IndexedDbPersistence } from './indexeddb_persistence';
>>>>>>> master

/** A mutation queue for a specific user, backed by IndexedDB. */
export class IndexedDbMutationQueue implements MutationQueue {
Expand Down Expand Up @@ -443,36 +438,6 @@ export class IndexedDbMutationQueue implements MutationQueue {
}
uniqueBatchIDs = uniqueBatchIDs.add(batchID);
})
<<<<<<< HEAD
.next(() => {
const results: MutationBatch[] = [];
const promises: Array<PersistencePromise<void>> = [];
// TODO(rockwood): Implement this using iterate.
uniqueBatchIDs.forEach(batchId => {
promises.push(
mutationsStore(transaction)
.get(batchId)
.next(mutation => {
if (!mutation) {
fail(
'Dangling document-mutation reference found, ' +
'which points to ' +
batchId
);
}
assert(
mutation.userId === this.userId,
`Unexpected user '${
mutation.userId
}' for mutation batch ${batchId}`
);
results.push(this.serializer.fromDbMutationBatch(mutation!));
})
);
});
return PersistencePromise.waitFor(promises).next(() => results);
});
=======
.next(() => this.lookupMutationBatches(transaction, uniqueBatchIDs));
}

Expand All @@ -483,25 +448,29 @@ export class IndexedDbMutationQueue implements MutationQueue {
const results: MutationBatch[] = [];
const promises: Array<PersistencePromise<void>> = [];
// TODO(rockwood): Implement this using iterate.
batchIDs.forEach(batchID => {
const mutationKey = this.keyForBatchId(batchID);
batchIDs.forEach(batchId => {
promises.push(
mutationsStore(transaction)
.get(mutationKey)
.get(batchId)
.next(mutation => {
if (mutation === null) {
fail(
'Dangling document-mutation reference found, ' +
'which points to ' +
mutationKey
batchId
);
}
assert(
mutation.userId === this.userId,
`Unexpected user '${
mutation.userId
}' for mutation batch ${batchId}`
);
results.push(this.serializer.fromDbMutationBatch(mutation!));
})
);
});
return PersistencePromise.waitFor(promises).next(() => results);
>>>>>>> master
}

removeMutationBatches(
Expand Down
114 changes: 37 additions & 77 deletions packages/firestore/src/local/indexeddb_persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,19 @@ import {
} from './indexeddb_schema';
import { LocalSerializer } from './local_serializer';
import { MutationQueue } from './mutation_queue';
<<<<<<< HEAD
import {
Persistence,
PersistenceTransaction,
PrimaryStateListener
} from './persistence';
=======
import { Persistence, PersistenceTransaction } from './persistence';
>>>>>>> master
import { PersistencePromise } from './persistence_promise';
import { QueryCache } from './query_cache';
import { RemoteDocumentCache } from './remote_document_cache';
import { SimpleDb, SimpleDbStore, SimpleDbTransaction } from './simple_db';
<<<<<<< HEAD
import { Platform } from '../platform/platform';
import { AsyncQueue, TimerId } from '../util/async_queue';
import { ClientId } from './shared_client_state';
import { CancelablePromise } from '../util/promise';
=======
>>>>>>> master

const LOG_TAG = 'IndexedDbPersistence';

Expand Down Expand Up @@ -86,17 +79,15 @@ const UNSUPPORTED_PLATFORM_ERROR_MSG =
' IndexedDB or is known to have an incomplete implementation. Offline' +
' persistence has been disabled.';

<<<<<<< HEAD
// The format of the LocalStorage key that stores zombied client is:
// firestore_zombie_<persistence_prefix>_<instance_key>
const ZOMBIED_CLIENTS_KEY_PREFIX = 'firestore_zombie';
=======

export class IndexedDbTransaction extends PersistenceTransaction {
constructor(readonly simpleDbTransaction: SimpleDbTransaction) {
super();
}
}
>>>>>>> master

/**
* An IndexedDB-backed instance of Persistence. Data is stored persistently
Expand Down Expand Up @@ -494,14 +485,10 @@ export class IndexedDbPersistence implements Persistence {

runTransaction<T>(
action: string,
<<<<<<< HEAD
requirePrimaryLease: boolean,
transactionOperation: (
transaction: PersistenceTransaction
transaction: IndexedDbTransaction
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be PersistenceTransaction. The same mistake exists in master, which I will fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will backport your fix to master in the next merge.

) => PersistencePromise<T>
=======
operation: (transaction: IndexedDbTransaction) => PersistencePromise<T>
>>>>>>> master
): Promise<T> {
// TODO(multitab): Consider removing `requirePrimaryLease` and exposing
// three different write modes (readonly, readwrite, readwrite_primary).
Expand All @@ -513,52 +500,47 @@ export class IndexedDbPersistence implements Persistence {

// Do all transactions as readwrite against all object stores, since we
// are the only reader/writer.
<<<<<<< HEAD
return this.simpleDb.runTransaction('readwrite', ALL_STORES, txn => {
if (requirePrimaryLease) {
// While we merely verify that we have (or can acquire) the lease
// immediately, we wait to extend the primary lease until after
// executing transactionOperation(). This ensures that even if the
// transactionOperation takes a long time, we'll use a recent
// leaseTimestampMs in the extended (or newly acquired) lease.
return this.canActAsPrimary(txn)
.next(canActAsPrimary => {
if (!canActAsPrimary) {
// TODO(multitab): Handle this gracefully and transition back to
// secondary state.
log.error(
`Failed to obtain primary lease for action '${action}'.`
);
this.isPrimary = false;
this.queue.enqueue(() => this.primaryStateListener(false));
throw new FirestoreError(
Code.FAILED_PRECONDITION,
PRIMARY_LEASE_LOST_ERROR_MSG
);
}
return transactionOperation(txn);
})
.next(result => {
return this.acquireOrExtendPrimaryLease(txn).next(() => result);
});
} else {
return this.verifyAllowTabSynchronization(txn).next(() =>
transactionOperation(txn)
);
}
});
=======
return this.simpleDb.runTransaction(
'readwrite',
ALL_STORES,
simpleDbTxn => {
// Verify that we still have the owner lease as part of every transaction.
return this.ensureOwnerLease(simpleDbTxn).next(() =>
operation(new IndexedDbTransaction(simpleDbTxn))
);
if (requirePrimaryLease) {
// While we merely verify that we have (or can acquire) the lease
// immediately, we wait to extend the primary lease until after
// executing transactionOperation(). This ensures that even if the
// transactionOperation takes a long time, we'll use a recent
// leaseTimestampMs in the extended (or newly acquired) lease.
return this.canActAsPrimary(simpleDbTxn)
.next(canActAsPrimary => {
if (!canActAsPrimary) {
// TODO(multitab): Handle this gracefully and transition back to
// secondary state.
log.error(
`Failed to obtain primary lease for action '${action}'.`
);
this.isPrimary = false;
this.queue.enqueue(() => this.primaryStateListener(false));
throw new FirestoreError(
Code.FAILED_PRECONDITION,
PRIMARY_LEASE_LOST_ERROR_MSG
);
}
return transactionOperation(
new IndexedDbTransaction(simpleDbTxn)
);
})
.next(result => {
return this.acquireOrExtendPrimaryLease(simpleDbTxn).next(
() => result
);
});
} else {
return this.verifyAllowTabSynchronization(simpleDbTxn).next(() =>
transactionOperation(new IndexedDbTransaction(simpleDbTxn))
);
}
}
);
>>>>>>> master
}

/**
Expand Down Expand Up @@ -659,7 +641,6 @@ export class IndexedDbPersistence implements Persistence {
return true;
}

<<<<<<< HEAD
private attachVisibilityHandler(): void {
if (
this.document !== null &&
Expand All @@ -671,27 +652,6 @@ export class IndexedDbPersistence implements Persistence {
return this.updateClientMetadataAndTryBecomePrimary();
});
};
=======
/**
* Schedules a recurring timer to update the owner lease timestamp to prevent
* other tabs from taking the lease.
*/
private scheduleOwnerLeaseRefreshes(): void {
// NOTE: This doesn't need to be scheduled on the async queue and doing so
// would increase the chances of us not refreshing on time if the queue is
// backed up for some reason.
this.ownerLeaseRefreshHandle = setInterval(() => {
const txResult = this.simpleDb.runTransaction(
'readwrite',
ALL_STORES,
txn => {
// NOTE: We don't need to validate the current owner contents, since
// runTransaction does that automatically.
const store = txn.store<DbOwnerKey, DbOwner>(DbOwner.store);
return store.put('owner', new DbOwner(this.ownerId, Date.now()));
}
);
>>>>>>> master

this.document.addEventListener(
'visibilitychange',
Expand Down
18 changes: 0 additions & 18 deletions packages/firestore/src/local/indexeddb_query_cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,13 @@ import { PersistenceTransaction } from './persistence';
import { PersistencePromise } from './persistence_promise';
import { QueryCache } from './query_cache';
import { QueryData } from './query_data';
<<<<<<< HEAD
import { SimpleDb, SimpleDbStore } from './simple_db';
import { TargetIdGenerator } from '../core/target_id_generator';
=======
import { SimpleDbStore } from './simple_db';
import { IndexedDbPersistence } from './indexeddb_persistence';
>>>>>>> master

export class IndexedDbQueryCache implements QueryCache {
constructor(private serializer: LocalSerializer) {}

<<<<<<< HEAD
=======
/**
* The last received snapshot version. We store this separately from the
* metadata to avoid the extra conversion to/from DbTimestamp.
*/
private lastRemoteSnapshotVersion = SnapshotVersion.MIN;

/**
* A cached copy of the metadata for the query cache.
*/
private metadata: DbTargetGlobal = null;

>>>>>>> master
/** The garbage collector to notify about potential garbage keys. */
private garbageCollector: GarbageCollector | null = null;

Expand Down
16 changes: 4 additions & 12 deletions packages/firestore/src/local/indexeddb_remote_document_cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,20 @@ import {
import { Document, MaybeDocument, NoDocument } from '../model/document';
import { DocumentKey } from '../model/document_key';

<<<<<<< HEAD
import {
DbRemoteDocument,
DbRemoteDocumentKey,
DbRemoteDocumentChanges,
DbRemoteDocumentChangesKey
} from './indexeddb_schema';
=======
import { DbRemoteDocument, DbRemoteDocumentKey } from './indexeddb_schema';
import { IndexedDbPersistence } from './indexeddb_persistence';
>>>>>>> master
import { LocalSerializer } from './local_serializer';
import { PersistenceTransaction } from './persistence';
import { PersistencePromise } from './persistence_promise';
import { RemoteDocumentCache } from './remote_document_cache';
<<<<<<< HEAD
import { SimpleDb, SimpleDbStore } from './simple_db';
import { SnapshotVersion } from '../core/snapshot_version';
import { assert } from '../util/assert';
=======
import { SimpleDbStore } from './simple_db';
>>>>>>> master

export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
/** The last id read by `getNewDocumentChanges()`. */
Expand Down Expand Up @@ -213,10 +205,10 @@ function remoteDocumentsStore(
function documentChangesStore(
txn: PersistenceTransaction
): SimpleDbStore<DbRemoteDocumentChangesKey, DbRemoteDocumentChanges> {
return SimpleDb.getStore<DbRemoteDocumentChangesKey, DbRemoteDocumentChanges>(
txn,
DbRemoteDocumentChanges.store
);
return IndexedDbPersistence.getStore<
DbRemoteDocumentChangesKey,
DbRemoteDocumentChanges
>(txn, DbRemoteDocumentChanges.store);
}

function dbKey(docKey: DocumentKey): DbRemoteDocumentKey {
Expand Down
Loading