Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 1 addition & 40 deletions apps/web/src/lib/drizzle.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
pool,
db,
selectReplicaUrl,
selectUsageReplicaUrl,
shouldExitOnPoolError,
} from '@/lib/drizzle';
import { pool, db, selectReplicaUrl, shouldExitOnPoolError } from '@/lib/drizzle';

describe('drizzle', () => {
describe('pool', () => {
Expand Down Expand Up @@ -112,39 +106,6 @@ describe('drizzle', () => {
).toBe('postgres://eu-2');
});

it('uses the dedicated usage replica when configured', () => {
expect(
selectUsageReplicaUrl({
primaryUrl,
nodeEnv: 'production',
usageReplicaUrl: 'postgres://eu-2',
fallbackReplicaUrl: 'postgres://eu-1',
})
).toBe('postgres://eu-2');
});

it('falls back to the standard replica when the usage replica is unset', () => {
expect(
selectUsageReplicaUrl({
primaryUrl,
nodeEnv: 'production',
usageReplicaUrl: undefined,
fallbackReplicaUrl: 'postgres://eu-1',
})
).toBe('postgres://eu-1');
});

it('uses the primary for usage reads in local development', () => {
expect(
selectUsageReplicaUrl({
primaryUrl,
nodeEnv: 'development',
usageReplicaUrl: 'postgres://eu-2',
fallbackReplicaUrl: 'postgres://eu-1',
})
).toBe(primaryUrl);
});

it('falls back to the primary when the regional replica is unavailable', () => {
expect(
selectReplicaUrl({
Expand Down
80 changes: 6 additions & 74 deletions apps/web/src/lib/drizzle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,48 +68,21 @@ export function selectReplicaUrl({
/**
* Get the read replica URL based on deployment region.
* - US deployments use the US replica (POSTGRES_REPLICA_US_URL) for lower latency
* - EU deployments use POSTGRES_REPLICA_EU_URL. POSTGRES_REPLICA_EU_URL_2 is
* reserved for usage-analytics scans so those queries stay off this pool.
* - EU deployments randomly select one of two EU replicas to split read traffic
* across ~2,200 concurrent Vercel instances (~50/50 statistical distribution)
* - Falls back to primary if no replica URL is configured for the region
*/
function getReplicaUrl(): string {
if (NODE_ENV === 'development') return postgresUrl;
const euReplicaUrl = getEnvVariable('POSTGRES_REPLICA_EU_URL');
return selectReplicaUrl({
primaryUrl: postgresUrl,
nodeEnv: NODE_ENV,
vercelRegion: VERCEL_REGION,
usReplicaUrl: getEnvVariable('POSTGRES_REPLICA_US_URL'),
euReplicaUrls: euReplicaUrl ? [euReplicaUrl] : [],
});
}

/**
* Replica used by /usage analytics. Always POSTGRES_REPLICA_EU_URL_2 in
* production so heavy microdollar_usage scans do not compete with ordinary
* readDb traffic. Falls back to the standard replica, then primary.
*/
export function selectUsageReplicaUrl({
primaryUrl,
nodeEnv,
usageReplicaUrl,
fallbackReplicaUrl,
}: {
primaryUrl: string;
nodeEnv: string | undefined;
usageReplicaUrl: string | undefined;
fallbackReplicaUrl: string;
}): string {
if (nodeEnv === 'development') return primaryUrl;
return usageReplicaUrl || fallbackReplicaUrl;
}

function getUsageReplicaUrl(): string {
return selectUsageReplicaUrl({
primaryUrl: postgresUrl,
nodeEnv: NODE_ENV,
usageReplicaUrl: getEnvVariable('POSTGRES_REPLICA_EU_URL_2'),
fallbackReplicaUrl: getReplicaUrl(),
euReplicaUrls: [
getEnvVariable('POSTGRES_REPLICA_EU_URL'),
getEnvVariable('POSTGRES_REPLICA_EU_URL_2'),
].filter(Boolean) as string[],
});
}

Expand Down Expand Up @@ -148,35 +121,13 @@ const replica = usesSeparateReplica
: primary;
const replicaPool = replica.pool;

const usageReplicaUrl = getUsageReplicaUrl();
export const usesDedicatedUsageReplica =
usageReplicaUrl !== postgresUrl && usageReplicaUrl !== replicaUrl;

const usageReplica = usesDedicatedUsageReplica
? createDrizzleClient({
connectionString: usageReplicaUrl,
poolConfig: {
...sharedPoolConfig,
max: 2,
application_name: `${appName}-usage-replica`,
},
logger: !!DEBUG_QUERY_LOGGING,
})
: usageReplicaUrl === replicaUrl
? replica
: primary;
const usageReplicaPool = usageReplica.pool;

// Attach pools to ensure idle connections close before suspension
// Skip in test environment as it interferes with Jest's cleanup
if (process.env.NODE_ENV !== 'test') {
attachDatabasePool(pool);
if (usesSeparateReplica) {
attachDatabasePool(replicaPool);
}
if (usesDedicatedUsageReplica) {
attachDatabasePool(usageReplicaPool);
}
}

/**
Expand Down Expand Up @@ -223,15 +174,6 @@ if (usesSeparateReplica) {
});
}

if (usesDedicatedUsageReplica) {
usageReplicaPool.on('error', (err: Error) => {
console.error('Unexpected error on idle client (usage-replica)', err);
if (shouldExitOnPoolError('replica')) {
process.exit(-1);
}
});
}

// Pool observability is handled centrally by /api/cron/db-pool-metrics,
// which scrapes PgBouncer metrics from the Supabase Prometheus endpoint
// for all databases (primary + replicas) every minute.
Expand All @@ -254,13 +196,6 @@ const primaryDb = primary.db;
*/
export const readDb = replica.db;

/**
* Read replica reserved for /usage analytics scans.
* Points at POSTGRES_REPLICA_EU_URL_2 in production so those queries do not
* share the standard readDb pool. Falls back to readDb, then primary.
*/
export const usageReadDb = usageReplica.db;

/**
* Default database instance - connects to the primary database.
* Use this for writes and for reads that need strong consistency.
Expand Down Expand Up @@ -290,9 +225,6 @@ export async function closeAllDrizzleConnections(): Promise<void> {
if (usesSeparateReplica) {
await replicaPool.end();
}
if (usesDedicatedUsageReplica) {
await usageReplicaPool.end();
}
}

export type DrizzleTransaction = Parameters<Parameters<typeof db.transaction>[0]>[0];
Expand Down
Loading