Skip to content

Commit ef12eed

Browse files
committed
Implement SessionToken Persistence
1 parent 14b7720 commit ef12eed

11 files changed

+329
-2
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 { PersistenceTransaction } from './persistence_transaction';
19+
import { PersistencePromise } from './persistence_promise';
20+
import { ByteString } from '../util/byte_string';
21+
22+
/**
23+
* General purpose cache for global values.
24+
*
25+
* Global state that cuts across components should be saved here. Following are contained herein:
26+
*
27+
* `sessionToken` tracks server interaction across Listen and Write streams. This facilitates cache
28+
* synchronization and invalidation.
29+
*/
30+
export interface GlobalsCache {
31+
/**
32+
* Gets session token.
33+
*/
34+
getSessionToken(
35+
transaction: PersistenceTransaction
36+
): PersistencePromise<ByteString>;
37+
38+
/**
39+
* Sets session token.
40+
*
41+
* @param sessionToken - The new session token.
42+
*/
43+
setSessionToken(
44+
transaction: PersistenceTransaction,
45+
sessionToken: ByteString
46+
): PersistencePromise<void>;
47+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
import { GlobalsCache } from './globals_cache';
19+
import { PersistencePromise } from './persistence_promise';
20+
import { PersistenceTransaction } from './persistence_transaction';
21+
import { SimpleDbStore } from './simple_db';
22+
import { DbGlobalsStore, DbGlobalsKey } from './indexeddb_sentinels';
23+
import { DbGlobals } from './indexeddb_schema';
24+
import { getStore } from './indexeddb_transaction';
25+
26+
export class IndexedDbGlobalsCache implements GlobalsCache {
27+
private globalsStore(
28+
txn: PersistenceTransaction
29+
): SimpleDbStore<DbGlobalsKey, DbGlobals> {
30+
return getStore<DbGlobalsKey, DbGlobals>(txn, DbGlobalsStore);
31+
}
32+
33+
getSessionToken(txn: PersistenceTransaction): PersistencePromise<ByteString> {
34+
const globals = this.globalsStore(txn);
35+
return globals.get('sessionToken').next(global => {
36+
const value = global?.value;
37+
return value
38+
? ByteString.fromUint8Array(value)
39+
: ByteString.EMPTY_BYTE_STRING;
40+
});
41+
}
42+
43+
setSessionToken(
44+
txn: PersistenceTransaction,
45+
sessionToken: ByteString
46+
): PersistencePromise<void> {
47+
const globals = this.globalsStore(txn);
48+
return globals.put({
49+
name: 'sessionToken',
50+
value: sessionToken.toUint8Array()
51+
});
52+
}
53+
}

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ import {
6969
SimpleDb,
7070
SimpleDbStore
7171
} from './simple_db';
72+
import { IndexedDbGlobalsCache } from './indexeddb_globals_cache';
73+
import { GlobalsCache } from './globals_cache';
7274

7375
const LOG_TAG = 'IndexedDbPersistence';
7476

@@ -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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
import { GlobalsCache } from './globals_cache';
19+
import { PersistencePromise } from './persistence_promise';
20+
import { PersistenceTransaction } from './persistence_transaction';
21+
22+
export class MemoryGlobalsCache implements GlobalsCache {
23+
private sessionToken: ByteString = ByteString.EMPTY_BYTE_STRING;
24+
25+
getSessionToken(
26+
transaction: PersistenceTransaction
27+
): PersistencePromise<ByteString> {
28+
return PersistencePromise.resolve(this.sessionToken);
29+
}
30+
31+
setSessionToken(
32+
transaction: PersistenceTransaction,
33+
sessionToken: ByteString
34+
): PersistencePromise<void> {
35+
this.sessionToken = sessionToken;
36+
return PersistencePromise.resolve();
37+
}
38+
}

packages/firestore/src/local/memory_persistence.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ import {
5656
} from './persistence_transaction';
5757
import { ReferenceSet } from './reference_set';
5858
import { TargetData } from './target_data';
59+
import { GlobalsCache } from './globals_cache';
60+
import { MemoryGlobalsCache } from './memory_globals_cache';
5961

6062
const LOG_TAG = 'MemoryPersistence';
6163
/**
@@ -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
@@ -31,6 +31,7 @@ import {
3131
import { RemoteDocumentCache } from './remote_document_cache';
3232
import { TargetCache } from './target_cache';
3333
import { TargetData } from './target_data';
34+
import { GlobalsCache } from './globals_cache';
3435

3536
/**
3637
* Callback type for primary state notifications. This callback can be
@@ -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)