perf: gateway + DB + frontend optimizations (P8-003)
- DB client: configure connection pool (max=20, idle_timeout=30s, connect_timeout=5s) - DB schema: add missing indexes for auth sessions, accounts, conversations, agent_logs - DB schema: promote preferences(user_id,key) to UNIQUE index for ON CONFLICT upsert - Drizzle migration: 0003_p8003_perf_indexes.sql - preferences.service: replace 2-query SELECT+INSERT/UPDATE with single-round-trip upsert - conversations repo: add ORDER BY + LIMIT to findAll (200) and findMessages (500) - session-gc.service: make onModuleInit fire-and-forget (removes cold-start TTFB block) - next.config.ts: enable compress, productionBrowserSourceMaps:false, image avif/webp - docs/PERFORMANCE.md: full profiling report and change impact notes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
import { eq, and, type Db, preferences as preferencesTable } from '@mosaic/db';
|
||||
import { eq, and, sql, type Db, preferences as preferencesTable } from '@mosaic/db';
|
||||
import { DB } from '../database/database.module.js';
|
||||
|
||||
export const PLATFORM_DEFAULTS: Record<string, unknown> = {
|
||||
@@ -88,25 +88,24 @@ export class PreferencesService {
|
||||
}
|
||||
|
||||
private async upsertPref(userId: string, key: string, value: unknown): Promise<void> {
|
||||
const existing = await this.db
|
||||
.select({ id: preferencesTable.id })
|
||||
.from(preferencesTable)
|
||||
.where(and(eq(preferencesTable.userId, userId), eq(preferencesTable.key, key)))
|
||||
.limit(1);
|
||||
|
||||
if (existing.length > 0) {
|
||||
await this.db
|
||||
.update(preferencesTable)
|
||||
.set({ value: value as never, updatedAt: new Date() })
|
||||
.where(and(eq(preferencesTable.userId, userId), eq(preferencesTable.key, key)));
|
||||
} else {
|
||||
await this.db.insert(preferencesTable).values({
|
||||
// Single-round-trip upsert using INSERT … ON CONFLICT DO UPDATE.
|
||||
// Previously this was two queries (SELECT + INSERT/UPDATE), which doubled
|
||||
// the DB round-trips and introduced a TOCTOU window under concurrent writes.
|
||||
await this.db
|
||||
.insert(preferencesTable)
|
||||
.values({
|
||||
userId,
|
||||
key,
|
||||
value: value as never,
|
||||
mutable: true,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: [preferencesTable.userId, preferencesTable.key],
|
||||
set: {
|
||||
value: sql`excluded.value`,
|
||||
updatedAt: sql`now()`,
|
||||
},
|
||||
});
|
||||
}
|
||||
this.logger.debug(`Upserted preference "${key}" for user ${userId}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user