mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-06 07:26:43 -04:00
Small typescript sdk optimizations (#4640)
# Description of Changes Was doing some profiling and found some low hanging optimizations for the ts client. Namely caching table primary keys and reusing the writer for reducer args. # API and ABI breaking changes None # Expected complexity level and risk 1 # Testing ...
This commit is contained in:
@@ -158,6 +158,10 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
||||
#reducerCallInfo = new Map<number, { name: string; args: object }>();
|
||||
#procedureCallbacks = new Map<number, ProcedureCallback>();
|
||||
#rowDeserializers: Record<string, Deserializer<any>>;
|
||||
#rowIdMetadata: Record<
|
||||
string,
|
||||
{ primaryKeyColName?: string; primaryKeyColType?: AlgebraicType }
|
||||
>;
|
||||
#reducerArgsSerializers: Record<
|
||||
string,
|
||||
{ serialize: Serializer<any>; deserialize: Deserializer<any> }
|
||||
@@ -205,6 +209,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
||||
this.#emitter = emitter;
|
||||
|
||||
this.#rowDeserializers = Object.create(null);
|
||||
this.#rowIdMetadata = Object.create(null);
|
||||
this.#sourceNameToTableDef = Object.create(null);
|
||||
for (const table of Object.values(remoteModule.tables)) {
|
||||
this.#rowDeserializers[table.sourceName] = ProductType.makeDeserializer(
|
||||
@@ -213,6 +218,15 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
||||
this.#sourceNameToTableDef[table.sourceName] = table as Values<
|
||||
RemoteModule['tables']
|
||||
>;
|
||||
const primaryKeyColumn = Object.entries(table.columns).find(
|
||||
([, column]) => column.columnMetadata.isPrimaryKey
|
||||
);
|
||||
this.#rowIdMetadata[table.sourceName] = primaryKeyColumn
|
||||
? {
|
||||
primaryKeyColName: primaryKeyColumn[0],
|
||||
primaryKeyColType: primaryKeyColumn[1].typeBuilder.algebraicType,
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
||||
this.#reducerArgsSerializers = Object.create(null);
|
||||
@@ -302,8 +316,6 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
||||
#makeReducers(def: RemoteModule): ReducersView<RemoteModule> {
|
||||
const out: Record<string, unknown> = {};
|
||||
|
||||
const writer = new BinaryWriter(1024);
|
||||
|
||||
for (const reducer of def.reducers) {
|
||||
const reducerName = reducer.name;
|
||||
const key = reducer.accessorName;
|
||||
@@ -312,6 +324,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
||||
this.#reducerArgsSerializers[reducerName];
|
||||
|
||||
(out as any)[key] = (params: InferTypeOfRow<typeof reducer.params>) => {
|
||||
const writer = this.#reducerArgsEncoder;
|
||||
writer.clear();
|
||||
serializeArgs(writer, params);
|
||||
const argsBuffer = writer.getBuffer();
|
||||
@@ -428,20 +441,13 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
||||
const rows: Operation[] = [];
|
||||
|
||||
const deserializeRow = this.#rowDeserializers[tableName];
|
||||
const table = this.#sourceNameToTableDef[tableName];
|
||||
// TODO: performance
|
||||
const columnsArray = Object.entries(table.columns);
|
||||
const primaryKeyColumnEntry = columnsArray.find(
|
||||
col => col[1].columnMetadata.isPrimaryKey
|
||||
);
|
||||
const { primaryKeyColName, primaryKeyColType } =
|
||||
this.#rowIdMetadata[tableName];
|
||||
let previousOffset = 0;
|
||||
while (reader.remaining > 0) {
|
||||
const row = deserializeRow(reader);
|
||||
let rowId: ComparablePrimitive | undefined = undefined;
|
||||
if (primaryKeyColumnEntry !== undefined) {
|
||||
const primaryKeyColName = primaryKeyColumnEntry[0];
|
||||
const primaryKeyColType =
|
||||
primaryKeyColumnEntry[1].typeBuilder.algebraicType;
|
||||
if (primaryKeyColName !== undefined && primaryKeyColType !== undefined) {
|
||||
rowId = AlgebraicType.intoMapKey(
|
||||
primaryKeyColType,
|
||||
row[primaryKeyColName]
|
||||
@@ -548,6 +554,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
||||
}
|
||||
}
|
||||
|
||||
#reducerArgsEncoder = new BinaryWriter(1024);
|
||||
#clientMessageEncoder = new BinaryWriter(1024);
|
||||
#sendMessage(message: ClientMessage): void {
|
||||
const writer = this.#clientMessageEncoder;
|
||||
@@ -906,7 +913,8 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
||||
_paramsType: ProductType,
|
||||
params: object
|
||||
): Promise<void> {
|
||||
const writer = new BinaryWriter(1024);
|
||||
const writer = this.#reducerArgsEncoder;
|
||||
writer.clear();
|
||||
this.#reducerArgsSerializers[reducerName].serialize(writer, params);
|
||||
const argsBuffer = writer.getBuffer();
|
||||
return this.callReducer(reducerName, argsBuffer, params);
|
||||
|
||||
@@ -66,6 +66,7 @@ export class TableCacheImpl<
|
||||
TableName extends TableNamesOf<RemoteModule>,
|
||||
> implements ClientTableCoreImplementable<RemoteModule, TableName>
|
||||
{
|
||||
private readonly hasPrimaryKey: boolean;
|
||||
private rows: Map<
|
||||
ComparablePrimitive,
|
||||
[RowType<TableDefForTableName<RemoteModule, TableName>>, number]
|
||||
@@ -83,6 +84,9 @@ export class TableCacheImpl<
|
||||
this.tableDef = tableDef;
|
||||
this.rows = new Map();
|
||||
this.emitter = new EventEmitter();
|
||||
this.hasPrimaryKey = Object.values(this.tableDef.columns).some(
|
||||
col => col.columnMetadata.isPrimaryKey === true
|
||||
);
|
||||
// Build index views from the resolved runtime index metadata.
|
||||
//
|
||||
// We intentionally use `resolvedIndexes` rather than `indexes`:
|
||||
@@ -281,11 +285,7 @@ export class TableCacheImpl<
|
||||
return pendingCallbacks;
|
||||
}
|
||||
|
||||
// TODO: performance
|
||||
const hasPrimaryKey = Object.values(this.tableDef.columns).some(
|
||||
col => col.columnMetadata.isPrimaryKey === true
|
||||
);
|
||||
if (hasPrimaryKey) {
|
||||
if (this.hasPrimaryKey) {
|
||||
const insertMap = new Map<
|
||||
ComparablePrimitive,
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user