Skip to content

Commit 6bb2e89

Browse files
authored
Implement SessionToken Persistence (#8391)
* Implement SessionToken Persistence * Add migration tests. * Fix migration test
1 parent 3f2c12a commit 6bb2e89

12 files changed

+365
-4
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { ByteString } from '../util/byte_string';
19+
20+
import { PersistencePromise } from './persistence_promise';
21+
import { PersistenceTransaction } from './persistence_transaction';
22+
23+
/**
24+
* General purpose cache for global values.
25+
*
26+
* Global state that cuts across components should be saved here. Following are contained herein:
27+
*
28+
* `sessionToken` tracks server interaction across Listen and Write streams. This facilitates cache
29+
* synchronization and invalidation.
30+
*/
31+
export interface GlobalsCache {
32+
/**
33+
* Gets session token.
34+
*/
35+
getSessionToken(
36+
transaction: PersistenceTransaction
37+
): PersistencePromise<ByteString>;
38+
39+
/**
40+
* Sets session token.
41+
*
42+
* @param sessionToken - The new session token.
43+
*/
44+
setSessionToken(
45+
transaction: PersistenceTransaction,
46+
sessionToken: ByteString
47+
): PersistencePromise<void>;
48+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { ByteString } from '../util/byte_string';
18+
19+
import { GlobalsCache } from './globals_cache';
20+
import { DbGlobals } from './indexeddb_schema';
21+
import { DbGlobalsStore, DbGlobalsKey } from './indexeddb_sentinels';
22+
import { getStore } from './indexeddb_transaction';
23+
import { PersistencePromise } from './persistence_promise';
24+
import { PersistenceTransaction } from './persistence_transaction';
25+
import { SimpleDbStore } from './simple_db';
26+
27+
export class IndexedDbGlobalsCache implements GlobalsCache {
28+
private globalsStore(
29+
txn: PersistenceTransaction
30+
): SimpleDbStore<DbGlobalsKey, DbGlobals> {
31+
return getStore<DbGlobalsKey, DbGlobals>(txn, DbGlobalsStore);
32+
}
33+
34+
getSessionToken(txn: PersistenceTransaction): PersistencePromise<ByteString> {
35+
const globals = this.globalsStore(txn);
36+
return globals.get('sessionToken').next(global => {
37+
const value = global?.value;
38+
return value
39+
? ByteString.fromUint8Array(value)
40+
: ByteString.EMPTY_BYTE_STRING;
41+
});
42+
}
43+
44+
setSessionToken(
45+
txn: PersistenceTransaction,
46+
sessionToken: ByteString
47+
): PersistencePromise<void> {
48+
const globals = this.globalsStore(txn);
49+
return globals.put({
50+
name: 'sessionToken',
51+
value: sessionToken.toUint8Array()
52+
});
53+
}
54+
}

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ import { DocumentLike, WindowLike } from '../util/types';
2929

3030
import { BundleCache } from './bundle_cache';
3131
import { DocumentOverlayCache } from './document_overlay_cache';
32+
import { GlobalsCache } from './globals_cache';
3233
import { IndexManager } from './index_manager';
3334
import { IndexedDbBundleCache } from './indexeddb_bundle_cache';
3435
import { IndexedDbDocumentOverlayCache } from './indexeddb_document_overlay_cache';
36+
import { IndexedDbGlobalsCache } from './indexeddb_globals_cache';
3537
import { IndexedDbIndexManager } from './indexeddb_index_manager';
3638
import { IndexedDbLruDelegateImpl } from './indexeddb_lru_delegate_impl';
3739
import { IndexedDbMutationQueue } from './indexeddb_mutation_queue';
@@ -188,6 +190,7 @@ export class IndexedDbPersistence implements Persistence {
188190
/** A listener to notify on primary state changes. */
189191
private primaryStateListener: PrimaryStateListener = _ => Promise.resolve();
190192

193+
private readonly globalsCache: IndexedDbGlobalsCache;
191194
private readonly targetCache: IndexedDbTargetCache;
192195
private readonly remoteDocumentCache: IndexedDbRemoteDocumentCache;
193196
private readonly bundleCache: IndexedDbBundleCache;
@@ -232,6 +235,7 @@ export class IndexedDbPersistence implements Persistence {
232235
this.schemaVersion,
233236
new SchemaConverter(this.serializer)
234237
);
238+
this.globalsCache = new IndexedDbGlobalsCache();
235239
this.targetCache = new IndexedDbTargetCache(
236240
this.referenceDelegate,
237241
this.serializer
@@ -708,6 +712,14 @@ export class IndexedDbPersistence implements Persistence {
708712
return this._started;
709713
}
710714

715+
getGlobalsCache(): GlobalsCache {
716+
debugAssert(
717+
this.started,
718+
'Cannot initialize GlobalsCache before persistence is started.'
719+
);
720+
return this.globalsCache;
721+
}
722+
711723
getMutationQueue(
712724
user: User,
713725
indexManager: IndexManager

packages/firestore/src/local/indexeddb_schema.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import { DbTimestampKey } from './indexeddb_sentinels';
5454
* 16. Parse timestamp strings before creating index entries.
5555
*/
5656

57-
export const SCHEMA_VERSION = 16;
57+
export const SCHEMA_VERSION = 17;
5858

5959
/**
6060
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
@@ -536,3 +536,13 @@ export interface DbDocumentOverlay {
536536
/** The overlay mutation. */
537537
overlayMutation: ProtoWrite;
538538
}
539+
540+
/**
541+
* An object containing global name/value pair.
542+
*/
543+
export interface DbGlobals {
544+
/** Name is a globally unique identifier for a value. */
545+
name: string;
546+
/** Value is a general purpose storage for global data. */
547+
value: Uint8Array;
548+
}

packages/firestore/src/local/indexeddb_schema_converter.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ import {
6868
DbDocumentOverlayCollectionPathOverlayIndexPath,
6969
DbDocumentOverlayKeyPath,
7070
DbDocumentOverlayStore,
71+
DbGlobalsKeyPath,
72+
DbGlobalsStore,
7173
DbIndexConfigurationCollectionGroupIndex,
7274
DbIndexConfigurationCollectionGroupIndexPath,
7375
DbIndexConfigurationKeyPath,
@@ -269,6 +271,12 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
269271
});
270272
}
271273

274+
if (fromVersion < 17 && toVersion >= 17) {
275+
p = p.next(() => {
276+
createGlobalsStore(db);
277+
});
278+
}
279+
272280
return p;
273281
}
274282

@@ -748,6 +756,12 @@ function createDocumentOverlayStore(db: IDBDatabase): void {
748756
);
749757
}
750758

759+
function createGlobalsStore(db: IDBDatabase): void {
760+
db.createObjectStore(DbGlobalsStore, {
761+
keyPath: DbGlobalsKeyPath
762+
});
763+
}
764+
751765
function extractKey(remoteDoc: DbRemoteDocumentLegacy): DocumentKey {
752766
if (remoteDoc.document) {
753767
return new DocumentKey(

packages/firestore/src/local/indexeddb_sentinels.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ export const DbTargetDocumentDocumentTargetsKeyPath = ['path', 'targetId'];
220220
* The type to represent the single allowed key for the DbTargetGlobal store.
221221
*/
222222
export type DbTargetGlobalKey = typeof DbTargetGlobalKey;
223+
223224
/**
224225
* The key string used for the single object that exists in the
225226
* DbTargetGlobal store.
@@ -371,6 +372,14 @@ export const DbDocumentOverlayCollectionGroupOverlayIndexPath = [
371372
'largestBatchId'
372373
];
373374

375+
/** Name of the IndexedDb object store. */
376+
export const DbGlobalsStore = 'globals';
377+
378+
export const DbGlobalsKeyPath = 'name';
379+
380+
/** Names of global values */
381+
export type DbGlobalsKey = 'sessionToken';
382+
374383
// Visible for testing
375384
export const V1_STORES = [
376385
DbMutationQueueStore,
@@ -415,6 +424,7 @@ export const V15_STORES = [
415424
DbIndexEntryStore
416425
];
417426
export const V16_STORES = V15_STORES;
427+
export const V17_STORES = [...V15_STORES, DbGlobalsStore];
418428

419429
/**
420430
* The list of all default IndexedDB stores used throughout the SDK. This is
@@ -425,7 +435,9 @@ export const ALL_STORES = V12_STORES;
425435

426436
/** Returns the object stores for the provided schema. */
427437
export function getObjectStores(schemaVersion: number): string[] {
428-
if (schemaVersion === 16) {
438+
if (schemaVersion === 17) {
439+
return V17_STORES;
440+
} else if (schemaVersion === 16) {
429441
return V16_STORES;
430442
} else if (schemaVersion === 15) {
431443
return V15_STORES;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { ByteString } from '../util/byte_string';
18+
19+
import { GlobalsCache } from './globals_cache';
20+
import { PersistencePromise } from './persistence_promise';
21+
import { PersistenceTransaction } from './persistence_transaction';
22+
23+
export class MemoryGlobalsCache implements GlobalsCache {
24+
private sessionToken: ByteString = ByteString.EMPTY_BYTE_STRING;
25+
26+
getSessionToken(
27+
transaction: PersistenceTransaction
28+
): PersistencePromise<ByteString> {
29+
return PersistencePromise.resolve(this.sessionToken);
30+
}
31+
32+
setSessionToken(
33+
transaction: PersistenceTransaction,
34+
sessionToken: ByteString
35+
): PersistencePromise<void> {
36+
this.sessionToken = sessionToken;
37+
return PersistencePromise.resolve();
38+
}
39+
}

packages/firestore/src/local/memory_persistence.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { ObjectMap } from '../util/obj_map';
2929

3030
import { DocumentOverlayCache } from './document_overlay_cache';
3131
import { encodeResourcePath } from './encoded_resource_path';
32+
import { GlobalsCache } from './globals_cache';
3233
import { IndexManager } from './index_manager';
3334
import { LocalSerializer } from './local_serializer';
3435
import {
@@ -40,6 +41,7 @@ import {
4041
import { newLruGarbageCollector } from './lru_garbage_collector_impl';
4142
import { MemoryBundleCache } from './memory_bundle_cache';
4243
import { MemoryDocumentOverlayCache } from './memory_document_overlay_cache';
44+
import { MemoryGlobalsCache } from './memory_globals_cache';
4345
import { MemoryIndexManager } from './memory_index_manager';
4446
import { MemoryMutationQueue } from './memory_mutation_queue';
4547
import {
@@ -71,6 +73,7 @@ export class MemoryPersistence implements Persistence {
7173
* persisting values.
7274
*/
7375
private readonly indexManager: MemoryIndexManager;
76+
private readonly globalsCache: MemoryGlobalsCache;
7477
private mutationQueues: { [user: string]: MemoryMutationQueue } = {};
7578
private overlays: { [user: string]: MemoryDocumentOverlayCache } = {};
7679
private readonly remoteDocumentCache: MemoryRemoteDocumentCache;
@@ -94,6 +97,7 @@ export class MemoryPersistence implements Persistence {
9497
serializer: JsonProtoSerializer
9598
) {
9699
this._started = true;
100+
this.globalsCache = new MemoryGlobalsCache();
97101
this.referenceDelegate = referenceDelegateFactory(this);
98102
this.targetCache = new MemoryTargetCache(this);
99103
const sizer = (doc: Document): number =>
@@ -150,6 +154,10 @@ export class MemoryPersistence implements Persistence {
150154
return queue;
151155
}
152156

157+
getGlobalsCache(): GlobalsCache {
158+
return this.globalsCache;
159+
}
160+
153161
getTargetCache(): MemoryTargetCache {
154162
return this.targetCache;
155163
}

packages/firestore/src/local/persistence.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { DocumentKey } from '../model/document_key';
2121

2222
import { BundleCache } from './bundle_cache';
2323
import { DocumentOverlayCache } from './document_overlay_cache';
24+
import { GlobalsCache } from './globals_cache';
2425
import { IndexManager } from './index_manager';
2526
import { MutationQueue } from './mutation_queue';
2627
import { PersistencePromise } from './persistence_promise';
@@ -167,6 +168,11 @@ export interface Persistence {
167168
*/
168169
setNetworkEnabled(networkEnabled: boolean): void;
169170

171+
/**
172+
* Returns GlobalCache representing a general purpose cache for global values.
173+
*/
174+
getGlobalsCache(): GlobalsCache;
175+
170176
/**
171177
* Returns a MutationQueue representing the persisted mutations for the
172178
* given user.

0 commit comments

Comments
 (0)