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 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
42 changes: 42 additions & 0 deletions packages/firestore/src/core/bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @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;
/**
* Set to the snapshot version of the bundle if created by the Server SDKs.
* Otherwise set to SnapshotVersion.MIN.
*/
readonly createTime: SnapshotVersion;
}

/**
* Represents a Query saved by the SDK in its local storage.
*/
export interface NamedQuery {
readonly name: string;
readonly query: Query;
/** The time at which the results for this query were read. */
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
61 changes: 61 additions & 0 deletions packages/firestore/src/local/bundle_cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @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.
*/
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>;
}
111 changes: 111 additions & 0 deletions packages/firestore/src/local/indexeddb_bundle_cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* @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 {
fromDbBundle,
fromDbNamedQuery,
LocalSerializer,
toDbBundle,
toDbNamedQuery
} 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 fromDbBundle(this.serializer, bundle);
}
return undefined;
});
}

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

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

saveNamedQuery(
transaction: PersistenceTransaction,
query: bundleProto.NamedQuery
): PersistencePromise<void> {
return namedQueriesStore(transaction).put(
toDbNamedQuery(this.serializer, 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