perf: reduce Neon compute usage with cron + caching optimizations
- Reduce cron frequency: changes 2min→4hr, keyword 15min→6hr, metrics hourly→daily - Add Prisma directUrl for connection pooling support - Add Vercel KV caching to /api/tools endpoint (graceful degradation if not configured) - Add X-Cache header to indicate cache hit/miss - Add NEON_COMPUTE_OPTIMIZATION.md with full strategy guide These changes should reduce Neon CU usage from 100+ to ~20-30 CU-hrs/month.
This commit is contained in:
parent
24bc48bcef
commit
e9a7a689a1
6 changed files with 363 additions and 9 deletions
|
|
@ -26,6 +26,7 @@
|
|||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@vercel/analytics": "^1.6.1",
|
||||
"@vercel/blob": "^2.0.0",
|
||||
"@vercel/kv": "^3.0.0",
|
||||
"ai": "6.0.3",
|
||||
"bm25": "^0.1.1",
|
||||
"d3": "^7.9.0",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { type Prisma, prisma } from '@tpmjs/db';
|
||||
import { kv } from '@vercel/kv';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { checkRateLimit } from '~/lib/rate-limit';
|
||||
|
||||
|
|
@ -6,6 +7,34 @@ export const runtime = 'nodejs';
|
|||
export const dynamic = 'force-dynamic';
|
||||
export const maxDuration = 60;
|
||||
|
||||
// Cache configuration
|
||||
const CACHE_TTL = 300; // 5 minutes
|
||||
const CACHE_PREFIX = 'tools:';
|
||||
|
||||
/**
|
||||
* Try to get cached response, returns null if KV not configured or cache miss
|
||||
*/
|
||||
async function getCached<T>(key: string): Promise<T | null> {
|
||||
try {
|
||||
if (!process.env.KV_REST_API_URL) return null;
|
||||
return await kv.get<T>(key);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set cache, silently fails if KV not configured
|
||||
*/
|
||||
async function setCache<T>(key: string, value: T, ttl: number): Promise<void> {
|
||||
try {
|
||||
if (!process.env.KV_REST_API_URL) return;
|
||||
await kv.set(key, value, { ex: ttl });
|
||||
} catch {
|
||||
// Silently ignore cache errors
|
||||
}
|
||||
}
|
||||
|
||||
// Constants
|
||||
const DEFAULT_LIMIT = 20;
|
||||
const MAX_LIMIT = 1000;
|
||||
|
|
@ -255,6 +284,24 @@ export async function GET(request: NextRequest) {
|
|||
const limitParam = searchParams.get('limit');
|
||||
const offsetParam = searchParams.get('offset');
|
||||
|
||||
// Build cache key from query params
|
||||
const cacheKey = `${CACHE_PREFIX}${searchParams.toString() || 'default'}`;
|
||||
|
||||
// Try cache first (only for simple queries without search)
|
||||
if (!query) {
|
||||
const cached = await getCached<ApiResponse>(cacheKey);
|
||||
if (cached) {
|
||||
return NextResponse.json(cached, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'X-Request-ID': requestId,
|
||||
'X-Cache': 'HIT',
|
||||
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Validate pagination parameters
|
||||
let limit: number;
|
||||
let offset: number;
|
||||
|
|
@ -344,11 +391,17 @@ export async function GET(request: NextRequest) {
|
|||
},
|
||||
};
|
||||
|
||||
// Cache response for non-search queries
|
||||
if (!query) {
|
||||
await setCache(cacheKey, response, CACHE_TTL);
|
||||
}
|
||||
|
||||
return NextResponse.json(response, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'X-Request-ID': requestId,
|
||||
'X-Processing-Time': `${processingTime}ms`,
|
||||
'X-Cache': 'MISS',
|
||||
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue