Skip to content

Implement BundleCache for IDB and memory. #3170

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 21 commits into from
Jun 14, 2020
Merged
Show file tree
Hide file tree
Changes from 13 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
39 changes: 39 additions & 0 deletions packages/firestore/src/core/bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Query } from './query';
import { SnapshotVersion } from './snapshot_version';

/**
* Represents a Firestore bundle saved by the SDK in its local storage.
*/
export interface Bundle {
readonly id: string;
readonly version: number;
// When the saved bundle is built from the server SDKs.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you point out in the documentation that this is to SnapshotVersion.MIN if the above is not true? In its current form, the comment raises the question why the type is not nullable.

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 am not sure what do you mean here, sorry!

Copy link
Contributor

Choose a reason for hiding this comment

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

Something like:

Set to the snapshot version of the bundle if created by the Server SDKs. Otherwise set to SnapshotVersion.MIN.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

readonly createTime: SnapshotVersion;
}

/**
* Represents a Query saved by the SDK in its local storage.
*/
export interface NamedQuery {
readonly name: string;
readonly query: Query;
// When the results for this query are read to the saved bundle.
Copy link
Contributor

Choose a reason for hiding this comment

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

/** The time at which the results for this query were read. */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

readonly readTime: SnapshotVersion;
}
3 changes: 2 additions & 1 deletion packages/firestore/src/core/component_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ export class MemoryComponentProvider implements ComponentProvider {
!cfg.persistenceSettings.durable,
'Can only start memory persistence'
);
return new MemoryPersistence(MemoryEagerDelegate.factory);
const serializer = cfg.platform.newSerializer(cfg.databaseInfo.databaseId);
return new MemoryPersistence(MemoryEagerDelegate.factory, serializer);
}

createRemoteStore(cfg: ComponentConfiguration): RemoteStore {
Expand Down
60 changes: 60 additions & 0 deletions packages/firestore/src/local/bundle_cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { PersistenceTransaction } from './persistence';
import { PersistencePromise } from './persistence_promise';
import * as bundleProto from '../protos/firestore_bundle_proto';
import { Bundle, NamedQuery } from '../core/bundle';

/**
* Provides interfaces to save and read Firestore bundles.
*/
export interface BundleCache {
/**
* Gets a saved `Bundle` for a given `bundleId`, returns undefined if
* no bundles are found under the given id.
*/
getBundle(
transaction: PersistenceTransaction,
bundleId: string
): PersistencePromise<Bundle | undefined>;

/**
* Saves a `BundleMetadata` from a bundle into local storage, using its id as the persistent key.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Wrap at 80 characters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

*/
saveBundleMetadata(
transaction: PersistenceTransaction,
metadata: bundleProto.BundleMetadata
): PersistencePromise<void>;

/**
* Gets a saved `NamedQuery` for the given query name. Returns undefined if
* no queries are found under the given name.
*/
getNamedQuery(
transaction: PersistenceTransaction,
queryName: string
): PersistencePromise<NamedQuery | undefined>;

/**
* Saves a `NamedQuery` from a bundle, using its name as the persistent key.
*/
saveNamedQuery(
transaction: PersistenceTransaction,
query: bundleProto.NamedQuery
): PersistencePromise<void>;
}
105 changes: 105 additions & 0 deletions packages/firestore/src/local/indexeddb_bundle_cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { PersistenceTransaction } from './persistence';
import { PersistencePromise } from './persistence_promise';
import * as bundleProto from '../protos/firestore_bundle_proto';
import { BundleCache } from './bundle_cache';
import {
DbBundle,
DbBundlesKey,
DbNamedQuery,
DbNamedQueriesKey
} from './indexeddb_schema';
import { SimpleDbStore } from './simple_db';
import { IndexedDbPersistence } from './indexeddb_persistence';
import { LocalSerializer } from './local_serializer';
import { Bundle, NamedQuery } from '../core/bundle';

export class IndexedDbBundleCache implements BundleCache {
constructor(private serializer: LocalSerializer) {}

getBundle(
transaction: PersistenceTransaction,
bundleId: string
): PersistencePromise<Bundle | undefined> {
return bundlesStore(transaction)
.get(bundleId)
.next(bundle => {
if (bundle) {
return this.serializer.fromDbBundle(bundle!);
Copy link
Contributor

Choose a reason for hiding this comment

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

You should be able to drop the bang.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}
return undefined;
});
}

saveBundleMetadata(
transaction: PersistenceTransaction,
bundleMetadata: bundleProto.BundleMetadata
): PersistencePromise<void> {
return bundlesStore(transaction).put(
this.serializer.toDbBundle(bundleMetadata)
);
}

getNamedQuery(
transaction: PersistenceTransaction,
queryName: string
): PersistencePromise<NamedQuery | undefined> {
return namedQueriesStore(transaction)
.get(queryName)
.next(query => {
if (query) {
return this.serializer.fromDbNamedQuery(query!);
Copy link
Contributor

Choose a reason for hiding this comment

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

You should be able to drop the bang.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}
return undefined;
});
}

saveNamedQuery(
transaction: PersistenceTransaction,
query: bundleProto.NamedQuery
): PersistencePromise<void> {
return namedQueriesStore(transaction).put(
this.serializer.toDbNamedQuery(query)
);
}
}

/**
* Helper to get a typed SimpleDbStore for the bundles object store.
*/
function bundlesStore(
txn: PersistenceTransaction
): SimpleDbStore<DbBundlesKey, DbBundle> {
return IndexedDbPersistence.getStore<DbBundlesKey, DbBundle>(
txn,
DbBundle.store
);
}

/**
* Helper to get a typed SimpleDbStore for the namedQueries object store.
*/
function namedQueriesStore(
txn: PersistenceTransaction
): SimpleDbStore<DbNamedQueriesKey, DbNamedQuery> {
return IndexedDbPersistence.getStore<DbNamedQueriesKey, DbNamedQuery>(
txn,
DbNamedQuery.store
);
}
11 changes: 11 additions & 0 deletions packages/firestore/src/local/indexeddb_persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
EncodedResourcePath,
encodeResourcePath
} from './encoded_resource_path';
import { IndexedDbBundleCache } from './indexeddb_bundle_cache';
import { IndexedDbIndexManager } from './indexeddb_index_manager';
import {
IndexedDbMutationQueue,
Expand Down Expand Up @@ -226,6 +227,7 @@ export class IndexedDbPersistence implements Persistence {
private readonly targetCache: IndexedDbTargetCache;
private readonly indexManager: IndexedDbIndexManager;
private readonly remoteDocumentCache: IndexedDbRemoteDocumentCache;
private readonly bundleCache: IndexedDbBundleCache;
private readonly webStorage: Storage;
readonly referenceDelegate: IndexedDbLruDelegate;

Expand Down Expand Up @@ -259,6 +261,7 @@ export class IndexedDbPersistence implements Persistence {
this.serializer,
this.indexManager
);
this.bundleCache = new IndexedDbBundleCache(this.serializer);
if (platform.window && platform.window.localStorage) {
this.window = platform.window;
this.webStorage = this.window.localStorage;
Expand Down Expand Up @@ -763,6 +766,14 @@ export class IndexedDbPersistence implements Persistence {
return this.indexManager;
}

getBundleCache(): IndexedDbBundleCache {
debugAssert(
this.started,
'Cannot initialize BundleCache before persistence is started.'
);
return this.bundleCache;
}

runTransaction<T>(
action: string,
mode: PersistenceTransactionMode,
Expand Down
18 changes: 9 additions & 9 deletions packages/firestore/src/local/indexeddb_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1092,11 +1092,11 @@ export type DbBundlesKey = string;
/**
* A object representing a bundle loaded by the SDK.
*/
export class DbBundles {
export class DbBundle {
/** Name of the IndexedDb object store. */
static store = 'bundles';

static keyPath = ['bundleId'];
static keyPath = 'bundleId';

constructor(
/** The ID of the loaded bundle. */
Expand All @@ -1109,8 +1109,8 @@ export class DbBundles {
}

function createBundlesStore(db: IDBDatabase): void {
db.createObjectStore(DbBundles.store, {
keyPath: DbBundles.keyPath
db.createObjectStore(DbBundle.store, {
keyPath: DbBundle.keyPath
});
}

Expand All @@ -1119,11 +1119,11 @@ export type DbNamedQueriesKey = string;
/**
* A object representing a named query loaded by the SDK via a bundle.
*/
export class DbNamedQueries {
export class DbNamedQuery {
/** Name of the IndexedDb object store. */
static store = 'namedQueries';

static keyPath = ['name'];
static keyPath = 'name';

constructor(
/** The name of the query. */
Expand All @@ -1136,8 +1136,8 @@ export class DbNamedQueries {
}

function createNamedQueriesStore(db: IDBDatabase): void {
db.createObjectStore(DbNamedQueries.store, {
keyPath: DbNamedQueries.keyPath
db.createObjectStore(DbNamedQuery.store, {
keyPath: DbNamedQuery.keyPath
});
}

Expand Down Expand Up @@ -1174,7 +1174,7 @@ export const V8_STORES = [...V6_STORES, DbCollectionParent.store];

// V10 does not change the set of stores.

export const V11_STORES = [...V8_STORES, DbCollectionParent.store];
export const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];

/**
* The list of all default IndexedDB stores used throughout the SDK. This is
Expand Down
Loading