perf: lazy load heavy dependencies and db drivers

Move database drivers and heavy server-side modules to be loaded
lazily via require() and dynamic import(). This reduces the initial
memory footprint and improves cold start performance for serverless
functions by avoiding loading unused dependencies on every request.
This commit is contained in:
Richard R 2026-02-16 16:10:03 -07:00
parent 9931c7d0d8
commit fc24b31cc3
2 changed files with 29 additions and 16 deletions

View file

@ -1,14 +1,16 @@
import { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';
import { drizzle as drizzleSqlite } from 'drizzle-orm/better-sqlite3';
import { sql } from 'drizzle-orm';
import { Pool } from 'pg';
import Database from 'better-sqlite3';
import path from 'path';
import fs from 'fs';
import * as schema from './schema';
import * as authSchemaSqlite from './schema_auth_sqlite';
import * as authSchemaPostgres from './schema_auth_postgres';
// Database driver modules are loaded lazily via require() inside getDrizzleDB()
// to avoid loading the unused driver (~15-20 MB each) in every serverless function.
// require() is used instead of dynamic import() because getDrizzleDB() must remain
// synchronous for the SQLite code path.
const UNCLAIMED_USER_ID = 'unclaimed';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -62,12 +64,20 @@ function getDrizzleDB() {
dbIsPostgres = !!process.env.POSTGRES_URL;
if (dbIsPostgres) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { drizzle: drizzlePg } = require('drizzle-orm/node-postgres');
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.POSTGRES_URL,
});
dbInstance = drizzlePg(pool, { schema: { ...schema, ...authSchemaPostgres } });
} else {
// Fallback to SQLite
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { drizzle: drizzleSqlite } = require('drizzle-orm/better-sqlite3');
// eslint-disable-next-line @typescript-eslint/no-require-imports
const Database = require('better-sqlite3');
const dbPath = path.join(process.cwd(), 'docstore', 'sqlite3.db');
const dir = path.dirname(dbPath);
if (!fs.existsSync(dir)) {

View file

@ -5,17 +5,13 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { db } from "@/db";
import { rateLimiter } from "@/lib/server/rate-limiter";
import { isAuthEnabled, isAnonymousAuthSessionsEnabled } from "@/lib/server/auth-config";
import { deleteUserStorageData } from "@/lib/server/user-data-cleanup";
import * as authSchemaSqlite from "@/db/schema_auth_sqlite";
import * as authSchemaPostgres from "@/db/schema_auth_postgres";
import {
transferUserAudiobooks,
transferUserDocuments,
transferUserPreferences,
transferUserProgress,
} from "@/lib/server/claim-data";
// Heavy modules (S3 SDK, blobstore, rate-limiter, claim-data) are loaded
// lazily via dynamic import() inside the beforeDelete / onLinkAccount
// callbacks to avoid inflating every serverless function that touches auth.
// ...
@ -80,6 +76,7 @@ const createAuth = () => betterAuth({
enabled: true,
beforeDelete: async (user) => {
try {
const { deleteUserStorageData } = await import('@/lib/server/user-data-cleanup');
await deleteUserStorageData(user.id, null);
} catch (error) {
console.error('[auth] Failed to clean up user storage before deletion:', error);
@ -123,6 +120,12 @@ const createAuth = () => betterAuth({
newUserEmail: newUser.user.email,
});
// Lazy-load heavy modules only when account linking actually happens
const [{ rateLimiter }, claimData] = await Promise.all([
import('@/lib/server/rate-limiter'),
import('@/lib/server/claim-data'),
]);
// Transfer rate limiting data (TTS char counts) from anonymous user to authenticated user
try {
await rateLimiter.transferAnonymousUsage(anonymousUser.user.id, newUser.user.id);
@ -134,7 +137,7 @@ const createAuth = () => betterAuth({
// Transfer audiobooks from anonymous user to new authenticated user
try {
const transferred = await transferUserAudiobooks(anonymousUser.user.id, newUser.user.id);
const transferred = await claimData.transferUserAudiobooks(anonymousUser.user.id, newUser.user.id);
if (transferred > 0) {
console.log(`Successfully transferred ${transferred} audiobook(s) from anonymous user ${anonymousUser.user.id} to user ${newUser.user.id}`);
}
@ -145,7 +148,7 @@ const createAuth = () => betterAuth({
// Transfer documents from anonymous user to new authenticated user
try {
const transferred = await transferUserDocuments(anonymousUser.user.id, newUser.user.id);
const transferred = await claimData.transferUserDocuments(anonymousUser.user.id, newUser.user.id);
if (transferred > 0) {
console.log(`Successfully transferred ${transferred} document(s) from anonymous user ${anonymousUser.user.id} to user ${newUser.user.id}`);
}
@ -156,7 +159,7 @@ const createAuth = () => betterAuth({
// Transfer preferences from anonymous user to new authenticated user
try {
const transferred = await transferUserPreferences(anonymousUser.user.id, newUser.user.id);
const transferred = await claimData.transferUserPreferences(anonymousUser.user.id, newUser.user.id);
if (transferred > 0) {
console.log(`Successfully transferred preferences from anonymous user ${anonymousUser.user.id} to user ${newUser.user.id}`);
}
@ -167,7 +170,7 @@ const createAuth = () => betterAuth({
// Transfer reading progress from anonymous user to new authenticated user
try {
const transferred = await transferUserProgress(anonymousUser.user.id, newUser.user.id);
const transferred = await claimData.transferUserProgress(anonymousUser.user.id, newUser.user.id);
if (transferred > 0) {
console.log(`Successfully transferred ${transferred} progress row(s) from anonymous user ${anonymousUser.user.id} to user ${newUser.user.id}`);
}