diff --git a/crates/bindings-typescript/src/sdk/db_connection_builder.ts b/crates/bindings-typescript/src/sdk/db_connection_builder.ts index 35f7bd828be..a891dd3cb6b 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_builder.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_builder.ts @@ -20,6 +20,7 @@ export class DbConnectionBuilder< #emitter: EventEmitter = new EventEmitter(); #compression: 'gzip' | 'none' = 'gzip'; #lightMode: boolean = false; + #confirmedReads?: boolean; #createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn; /** @@ -107,6 +108,31 @@ export class DbConnectionBuilder< return this; } + /** + * Sets the connection to use confirmed reads. + * + * When enabled, the server will send query results only after they are + * confirmed to be durable. + * + * What durable means depends on the server configuration: a single node + * server may consider a transaction durable once it is `fsync`'ed to disk, + * whereas a cluster may require that some number of replicas have + * acknowledge that they have stored the transactions. + * + * Note that enabling confirmed reads will increase the latency between a + * reducer call and the corresponding subscription update arriving at the + * client. + * + * If this method is not called, not preference is sent to the server, and + * the server will choose the default. + * + * @param confirmedReads `true` to enable confirmed reads, `false` to disable. + */ + withConfirmedReads(confirmedReads: boolean): this { + this.#confirmedReads = confirmedReads; + return this; + } + /** * Register a callback to be invoked upon authentication with the database. * @@ -228,6 +254,7 @@ export class DbConnectionBuilder< emitter: this.#emitter, compression: this.#compression, lightMode: this.#lightMode, + confirmedReads: this.#confirmedReads, createWSFn: this.#createWSFn, remoteModule: this.remoteModule, }) diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index 6181c84cab2..f483848c83c 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -100,6 +100,7 @@ type DbConnectionConfig = { createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn; compression: 'gzip' | 'none'; lightMode: boolean; + confirmedReads?: boolean; }; export class DbConnectionImpl< @@ -177,6 +178,7 @@ export class DbConnectionImpl< createWSFn, compression, lightMode, + confirmedReads, }: DbConnectionConfig) { stdbLogger('info', 'Connecting to SpacetimeDB WS...'); @@ -212,6 +214,7 @@ export class DbConnectionImpl< authToken: token, compression: compression, lightMode: lightMode, + confirmedReads: confirmedReads, }) .then(v => { this.ws = v; diff --git a/crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts b/crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts index 153e55d3bd2..1edfdc6d0d6 100644 --- a/crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts +++ b/crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts @@ -73,6 +73,7 @@ export class WebsocketDecompressAdapter { authToken, compression, lightMode, + confirmedReads, }: { url: URL; wsProtocol: string; @@ -80,6 +81,7 @@ export class WebsocketDecompressAdapter { authToken?: string; compression: 'gzip' | 'none'; lightMode: boolean; + confirmedReads?: boolean; }): Promise { const headers = new Headers(); @@ -115,6 +117,9 @@ export class WebsocketDecompressAdapter { if (lightMode) { databaseUrl.searchParams.set('light', 'true'); } + if (confirmedReads !== undefined) { + databaseUrl.searchParams.set('confirmed', confirmedReads.toString()); + } const ws = new WS(databaseUrl.toString(), wsProtocol);