Skip to content

Commit a875bbe

Browse files
Move Datastore to ComponentProvider (#3417)
1 parent 6d11f7f commit a875bbe

File tree

7 files changed

+80
-44
lines changed

7 files changed

+80
-44
lines changed

packages/firestore/lite/src/api/database.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,11 @@ export class Firestore
9292
if (!this._datastorePromise) {
9393
const settings = this._getSettings();
9494
const databaseInfo = this._makeDatabaseInfo(settings.host, settings.ssl);
95+
const serializer = newSerializer(databaseInfo.databaseId);
96+
const datastore = newDatastore(this._credentials, serializer);
9597
this._datastorePromise = newConnection(databaseInfo).then(connection => {
96-
const serializer = newSerializer(databaseInfo.databaseId);
97-
return newDatastore(connection, this._credentials, serializer);
98+
datastore.start(connection);
99+
return datastore;
98100
});
99101
}
100102

packages/firestore/src/core/component_provider.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { RemoteStore } from '../remote/remote_store';
3737
import { EventManager } from './event_manager';
3838
import { AsyncQueue } from '../util/async_queue';
3939
import { DatabaseId, DatabaseInfo } from './database_info';
40-
import { Datastore } from '../remote/datastore';
40+
import { Datastore, newDatastore } from '../remote/datastore';
4141
import { User } from '../auth/user';
4242
import { PersistenceSettings } from './firestore_client';
4343
import { debugAssert } from '../util/assert';
@@ -55,10 +55,11 @@ import {
5555
MemoryEagerDelegate,
5656
MemoryPersistence
5757
} from '../local/memory_persistence';
58-
import { newConnectivityMonitor } from '../platform/connection';
58+
import { newConnection, newConnectivityMonitor } from '../platform/connection';
5959
import { newSerializer } from '../platform/serializer';
6060
import { getDocument, getWindow } from '../platform/dom';
61-
61+
import { CredentialsProvider } from '../api/credentials';
62+
import { Connection } from '../remote/connection';
6263
const MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE =
6364
'You are using the memory-only build of Firestore. Persistence support is ' +
6465
'only available via the @firebase/firestore bundle or the ' +
@@ -67,7 +68,7 @@ const MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE =
6768
export interface ComponentConfiguration {
6869
asyncQueue: AsyncQueue;
6970
databaseInfo: DatabaseInfo;
70-
datastore: Datastore;
71+
credentials: CredentialsProvider;
7172
clientId: ClientId;
7273
initialUser: User;
7374
maxConcurrentLimboResolutions: number;
@@ -84,6 +85,7 @@ export interface ComponentProvider {
8485
localStore: LocalStore;
8586
syncEngine: SyncEngine;
8687
gcScheduler: GarbageCollectionScheduler | null;
88+
datastore: Datastore;
8789
remoteStore: RemoteStore;
8890
eventManager: EventManager;
8991

@@ -105,6 +107,7 @@ export class MemoryComponentProvider implements ComponentProvider {
105107
localStore!: LocalStore;
106108
syncEngine!: SyncEngine;
107109
gcScheduler!: GarbageCollectionScheduler | null;
110+
datastore!: Datastore;
108111
remoteStore!: RemoteStore;
109112
eventManager!: EventManager;
110113

@@ -114,6 +117,11 @@ export class MemoryComponentProvider implements ComponentProvider {
114117
await this.persistence.start();
115118
this.gcScheduler = this.createGarbageCollectionScheduler(cfg);
116119
this.localStore = this.createLocalStore(cfg);
120+
121+
this.datastore = this.createDatastore(cfg);
122+
const connection = await this.loadConnection(cfg);
123+
this.datastore.start(connection);
124+
117125
this.remoteStore = this.createRemoteStore(cfg);
118126
this.syncEngine = this.createSyncEngine(cfg);
119127
this.eventManager = this.createEventManager(cfg);
@@ -132,6 +140,10 @@ export class MemoryComponentProvider implements ComponentProvider {
132140
await this.remoteStore.applyPrimaryState(this.syncEngine.isPrimaryClient);
133141
}
134142

143+
protected loadConnection(cfg: ComponentConfiguration): Promise<Connection> {
144+
return newConnection(cfg.databaseInfo);
145+
}
146+
135147
createEventManager(cfg: ComponentConfiguration): EventManager {
136148
return new EventManager(this.syncEngine);
137149
}
@@ -160,10 +172,15 @@ export class MemoryComponentProvider implements ComponentProvider {
160172
return new MemoryPersistence(MemoryEagerDelegate.factory);
161173
}
162174

175+
createDatastore(cfg: ComponentConfiguration): Datastore {
176+
const serializer = newSerializer(cfg.databaseInfo.databaseId);
177+
return newDatastore(cfg.credentials, serializer);
178+
}
179+
163180
createRemoteStore(cfg: ComponentConfiguration): RemoteStore {
164181
return new RemoteStore(
165182
this.localStore,
166-
cfg.datastore,
183+
this.datastore,
167184
cfg.asyncQueue,
168185
onlineState =>
169186
this.syncEngine.applyOnlineStateChange(
@@ -182,7 +199,7 @@ export class MemoryComponentProvider implements ComponentProvider {
182199
return newSyncEngine(
183200
this.localStore,
184201
this.remoteStore,
185-
cfg.datastore,
202+
this.datastore,
186203
this.sharedClientState,
187204
cfg.initialUser,
188205
cfg.maxConcurrentLimboResolutions
@@ -218,7 +235,7 @@ export class IndexedDbComponentProvider extends MemoryComponentProvider {
218235
return newSyncEngine(
219236
this.localStore,
220237
this.remoteStore,
221-
cfg.datastore,
238+
this.datastore,
222239
this.sharedClientState,
223240
cfg.initialUser,
224241
cfg.maxConcurrentLimboResolutions
@@ -315,7 +332,7 @@ export class MultiTabIndexedDbComponentProvider extends IndexedDbComponentProvid
315332
const syncEngine = newMultiTabSyncEngine(
316333
this.localStore,
317334
this.remoteStore,
318-
cfg.datastore,
335+
this.datastore,
319336
this.sharedClientState,
320337
cfg.initialUser,
321338
cfg.maxConcurrentLimboResolutions

packages/firestore/src/core/firestore_client.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { GarbageCollectionScheduler, Persistence } from '../local/persistence';
2222
import { Document, NoDocument } from '../model/document';
2323
import { DocumentKey } from '../model/document_key';
2424
import { Mutation } from '../model/mutation';
25-
import { newDatastore } from '../remote/datastore';
2625
import { RemoteStore } from '../remote/remote_store';
2726
import { AsyncQueue, wrapInUserErrorIfRecoverable } from '../util/async_queue';
2827
import { Code, FirestoreError } from '../util/error';
@@ -47,8 +46,6 @@ import {
4746
ComponentProvider,
4847
MemoryComponentProvider
4948
} from './component_provider';
50-
import { newConnection } from '../platform/connection';
51-
import { newSerializer } from '../platform/serializer';
5249

5350
const LOG_TAG = 'FirestoreClient';
5451
const MAX_CONCURRENT_LIMBO_RESOLUTIONS = 100;
@@ -236,19 +233,11 @@ export class FirestoreClient {
236233
persistenceResult: Deferred<void>
237234
): Promise<void> {
238235
try {
239-
// TODO(mrschmidt): Ideally, ComponentProvider would also initialize
240-
// Datastore (without duplicating the initializing logic once per
241-
// provider).
242-
243-
const connection = await newConnection(this.databaseInfo);
244-
const serializer = newSerializer(this.databaseInfo.databaseId);
245-
const datastore = newDatastore(connection, this.credentials, serializer);
246-
247236
await componentProvider.initialize({
248237
asyncQueue: this.asyncQueue,
249238
databaseInfo: this.databaseInfo,
250-
datastore,
251239
clientId: this.clientId,
240+
credentials: this.credentials,
252241
initialUser: user,
253242
maxConcurrentLimboResolutions: MAX_CONCURRENT_LIMBO_RESOLUTIONS,
254243
persistenceSettings

packages/firestore/src/remote/datastore.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { Document, MaybeDocument } from '../model/document';
2020
import { DocumentKey } from '../model/document_key';
2121
import { Mutation } from '../model/mutation';
2222
import * as api from '../protos/firestore_proto_api';
23-
import { debugCast, hardAssert } from '../util/assert';
23+
import { debugAssert, debugCast, hardAssert } from '../util/assert';
2424
import { Code, FirestoreError } from '../util/error';
2525
import { Connection } from './connection';
2626
import {
@@ -46,28 +46,27 @@ import { Query } from '../core/query';
4646
* Cloud Datastore grpc API, which provides an interface that is more convenient
4747
* for the rest of the client SDK architecture to consume.
4848
*/
49-
export class Datastore {
50-
// Make sure that the structural type of `Datastore` is unique.
51-
// See https://github.com/microsoft/TypeScript/issues/5451
52-
private _ = undefined;
49+
export abstract class Datastore {
50+
abstract start(connection: Connection): void;
5351
}
5452

5553
/**
5654
* An implementation of Datastore that exposes additional state for internal
5755
* consumption.
5856
*/
5957
class DatastoreImpl extends Datastore {
58+
connection!: Connection;
6059
terminated = false;
6160

6261
constructor(
63-
readonly connection: Connection,
6462
readonly credentials: CredentialsProvider,
6563
readonly serializer: JsonProtoSerializer
6664
) {
6765
super();
6866
}
6967

70-
private verifyNotTerminated(): void {
68+
verifyInitialized(): void {
69+
debugAssert(!!this.connection, 'Datastore.start() not called');
7170
if (this.terminated) {
7271
throw new FirestoreError(
7372
Code.FAILED_PRECONDITION,
@@ -76,9 +75,14 @@ class DatastoreImpl extends Datastore {
7675
}
7776
}
7877

78+
start(connection: Connection): void {
79+
debugAssert(!this.connection, 'Datastore.start() already called');
80+
this.connection = connection;
81+
}
82+
7983
/** Gets an auth token and invokes the provided RPC. */
8084
invokeRPC<Req, Resp>(rpcName: string, request: Req): Promise<Resp> {
81-
this.verifyNotTerminated();
85+
this.verifyInitialized();
8286
return this.credentials
8387
.getToken()
8488
.then(token => {
@@ -97,7 +101,7 @@ class DatastoreImpl extends Datastore {
97101
rpcName: string,
98102
request: Req
99103
): Promise<Resp[]> {
100-
this.verifyNotTerminated();
104+
this.verifyInitialized();
101105
return this.credentials
102106
.getToken()
103107
.then(token => {
@@ -117,11 +121,10 @@ class DatastoreImpl extends Datastore {
117121
}
118122

119123
export function newDatastore(
120-
connection: Connection,
121124
credentials: CredentialsProvider,
122125
serializer: JsonProtoSerializer
123126
): Datastore {
124-
return new DatastoreImpl(connection, credentials, serializer);
127+
return new DatastoreImpl(credentials, serializer);
125128
}
126129

127130
export async function invokeCommitRpc(
@@ -200,6 +203,7 @@ export function newPersistentWriteStream(
200203
listener: WriteStreamListener
201204
): PersistentWriteStream {
202205
const datastoreImpl = debugCast(datastore, DatastoreImpl);
206+
datastoreImpl.verifyInitialized();
203207
return new PersistentWriteStream(
204208
queue,
205209
datastoreImpl.connection,
@@ -215,6 +219,7 @@ export function newPersistentWatchStream(
215219
listener: WatchStreamListener
216220
): PersistentListenStream {
217221
const datastoreImpl = debugCast(datastore, DatastoreImpl);
222+
datastoreImpl.verifyInitialized();
218223
return new PersistentListenStream(
219224
queue,
220225
datastoreImpl.connection,

packages/firestore/test/integration/util/internal_helpers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ export function withTestDatastore(
5555
const databaseInfo = getDefaultDatabaseInfo();
5656
return newConnection(databaseInfo).then(conn => {
5757
const serializer = newSerializer(databaseInfo.databaseId);
58-
const datastore = newDatastore(conn, credentialsProvider, serializer);
58+
const datastore = newDatastore(credentialsProvider, serializer);
59+
datastore.start(conn);
5960
return fn(datastore);
6061
});
6162
}

packages/firestore/test/unit/specs/spec_test_components.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ import {
6161
} from '../../../src/local/shared_client_state';
6262
import { WindowLike } from '../../../src/util/types';
6363
import { newSerializer } from '../../../src/platform/serializer';
64+
import { Datastore, newDatastore } from '../../../src/remote/datastore';
65+
import { JsonProtoSerializer } from '../../../src/remote/serializer';
6466

6567
/**
6668
* A test-only MemoryPersistence implementation that is able to inject
@@ -119,6 +121,7 @@ function failTransactionIfNeeded(
119121

120122
export class MockIndexedDbComponentProvider extends MultiTabIndexedDbComponentProvider {
121123
persistence!: MockIndexedDbPersistence;
124+
connection!: MockConnection;
122125

123126
constructor(
124127
private readonly window: WindowLike,
@@ -127,6 +130,19 @@ export class MockIndexedDbComponentProvider extends MultiTabIndexedDbComponentPr
127130
super();
128131
}
129132

133+
async loadConnection(cfg: ComponentConfiguration): Promise<Connection> {
134+
this.connection = new MockConnection(cfg.asyncQueue);
135+
return this.connection;
136+
}
137+
138+
createDatastore(cfg: ComponentConfiguration): Datastore {
139+
const serializer = new JsonProtoSerializer(
140+
cfg.databaseInfo.databaseId,
141+
/* useProto3Json= */ true
142+
);
143+
return newDatastore(cfg.credentials, serializer);
144+
}
145+
130146
createGarbageCollectionScheduler(
131147
cfg: ComponentConfiguration
132148
): GarbageCollectionScheduler | null {
@@ -176,11 +192,25 @@ export class MockIndexedDbComponentProvider extends MultiTabIndexedDbComponentPr
176192

177193
export class MockMemoryComponentProvider extends MemoryComponentProvider {
178194
persistence!: MockMemoryPersistence;
195+
connection!: MockConnection;
179196

180197
constructor(private readonly gcEnabled: boolean) {
181198
super();
182199
}
183200

201+
async loadConnection(cfg: ComponentConfiguration): Promise<Connection> {
202+
this.connection = new MockConnection(cfg.asyncQueue);
203+
return this.connection;
204+
}
205+
206+
createDatastore(cfg: ComponentConfiguration): Datastore {
207+
const serializer = new JsonProtoSerializer(
208+
cfg.databaseInfo.databaseId,
209+
/* useProto3Json= */ true
210+
);
211+
return newDatastore(cfg.credentials, serializer);
212+
}
213+
184214
createGarbageCollectionScheduler(
185215
cfg: ComponentConfiguration
186216
): GarbageCollectionScheduler | null {

packages/firestore/test/unit/specs/spec_test_runner.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import { DocumentKey } from '../../../src/model/document_key';
5151
import { JsonObject } from '../../../src/model/object_value';
5252
import { Mutation } from '../../../src/model/mutation';
5353
import * as api from '../../../src/protos/firestore_proto_api';
54-
import { Datastore, newDatastore } from '../../../src/remote/datastore';
5554
import { ExistenceFilter } from '../../../src/remote/existence_filter';
5655
import { RemoteStore } from '../../../src/remote/remote_store';
5756
import { mapCodeFromRpcCode } from '../../../src/remote/rpc_error';
@@ -178,7 +177,6 @@ abstract class TestRunner {
178177
private networkEnabled = true;
179178

180179
// Initialized asynchronously via start().
181-
private datastore!: Datastore;
182180
private localStore!: LocalStore;
183181
private remoteStore!: RemoteStore;
184182
private persistence!: MockMemoryPersistence | MockIndexedDbPersistence;
@@ -233,18 +231,11 @@ abstract class TestRunner {
233231
}
234232

235233
async start(): Promise<void> {
236-
this.connection = new MockConnection(this.queue);
237-
this.datastore = newDatastore(
238-
this.connection,
239-
new EmptyCredentialsProvider(),
240-
this.serializer
241-
);
242-
243234
const componentProvider = await this.initializeComponentProvider(
244235
{
245236
asyncQueue: this.queue,
246237
databaseInfo: this.databaseInfo,
247-
datastore: this.datastore,
238+
credentials: new EmptyCredentialsProvider(),
248239
clientId: this.clientId,
249240
initialUser: this.user,
250241
maxConcurrentLimboResolutions:
@@ -257,6 +248,7 @@ abstract class TestRunner {
257248
this.sharedClientState = componentProvider.sharedClientState;
258249
this.persistence = componentProvider.persistence;
259250
this.localStore = componentProvider.localStore;
251+
this.connection = componentProvider.connection;
260252
this.remoteStore = componentProvider.remoteStore;
261253
this.syncEngine = componentProvider.syncEngine;
262254
this.eventManager = componentProvider.eventManager;

0 commit comments

Comments
 (0)