export const postgresTuningProfiles = { 'shared-host': { name: 'shared-host', label: '4c16g shared-host', description: '4 vCPU / 16GB RAM, PostgreSQL shares the host with API, worker, Nginx and monitoring agents.', settings: { max_connections: { recommended: '80', min: 40, max: 120, type: 'number', rationale: '4 vCPU should use application/pooler limits instead of many direct active connections.', }, shared_buffers: { recommended: '3GB', min: '2GB', max: '4GB', type: 'bytes', restart: true, rationale: 'Keep PostgreSQL buffers large enough while leaving RAM for OS cache and sibling services.', }, effective_cache_size: { recommended: '10GB', min: '8GB', max: '12GB', type: 'bytes', rationale: 'Planner estimate only; reflects shared buffers plus OS cache on a 16GB shared host.', }, work_mem: { recommended: '16MB', min: '8MB', max: '32MB', type: 'bytes', rationale: 'This is per sort/hash operation, not a global cap.', }, maintenance_work_mem: { recommended: '512MB', min: '256MB', max: '1GB', type: 'bytes', rationale: 'Helps index builds, vacuum and imports without overcommitting memory.', }, autovacuum_work_mem: { recommended: '256MB', min: '128MB', max: '512MB', type: 'bytes', rationale: 'Avoid letting multiple autovacuum workers inherit an overly high maintenance_work_mem.', }, wal_buffers: { recommended: '16MB', min: '8MB', max: '64MB', type: 'bytes', restart: true, rationale: 'Enough WAL buffering for steady learning writes and import bursts.', }, min_wal_size: { recommended: '1GB', min: '1GB', max: '4GB', type: 'bytes', rationale: 'Reserve WAL for short write spikes and batch jobs.', }, max_wal_size: { recommended: '6GB', min: '4GB', max: '12GB', type: 'bytes', rationale: 'Reduce checkpoint pressure while keeping crash recovery bounded.', }, checkpoint_timeout: { recommended: '10min', min: '5min', max: '15min', type: 'duration', rationale: 'Avoid overly frequent checkpoints on write bursts.', }, checkpoint_completion_target: { recommended: '0.9', min: 0.8, max: 0.95, type: 'number', rationale: 'Spread checkpoint I/O without pushing completion too close to the next checkpoint.', }, effective_io_concurrency: { recommended: '100', min: 50, max: 300, type: 'number', rationale: 'SSD cloud disk starting point; validate against the actual disk class.', }, random_page_cost: { recommended: '1.1', min: 1, max: 1.5, type: 'number', rationale: 'Reflect SSD random I/O and encourage reasonable index scans.', }, jit: { recommended: 'off', exact: 'off', type: 'text', rationale: 'Question-bank API queries are mostly short OLTP requests where JIT planning cost is usually not worth it.', }, log_min_duration_statement: { recommended: '500ms', min: '100ms', max: '1000ms', type: 'duration', rationale: 'Capture slow SQL during launch without logging every normal request.', }, idle_in_transaction_session_timeout: { recommended: '60s', min: '30s', max: '120s', type: 'duration', rationale: 'Kill idle transactions before they block migrations and writes.', }, statement_timeout: { recommended: '30s', min: '5s', max: '60s', type: 'duration', rationale: 'API requests should fail boundedly; import jobs can override per session.', }, lock_timeout: { recommended: '5s', min: '1s', max: '10s', type: 'duration', rationale: 'Ordinary API requests should not wait a long time behind locks.', }, }, }, 'dedicated-db': { name: 'dedicated-db', label: '4c16g dedicated PostgreSQL', description: '4 vCPU / 16GB RAM where PostgreSQL is the only heavy service on the host.', settings: { max_connections: { recommended: '120', min: 60, max: 150, type: 'number' }, shared_buffers: { recommended: '4GB', min: '3GB', max: '5GB', type: 'bytes', restart: true }, effective_cache_size: { recommended: '12GB', min: '10GB', max: '14GB', type: 'bytes' }, work_mem: { recommended: '16MB', min: '8MB', max: '32MB', type: 'bytes' }, maintenance_work_mem: { recommended: '768MB', min: '512MB', max: '1536MB', type: 'bytes' }, autovacuum_work_mem: { recommended: '256MB', min: '128MB', max: '512MB', type: 'bytes' }, wal_buffers: { recommended: '16MB', min: '8MB', max: '64MB', type: 'bytes', restart: true }, min_wal_size: { recommended: '2GB', min: '1GB', max: '4GB', type: 'bytes' }, max_wal_size: { recommended: '8GB', min: '4GB', max: '16GB', type: 'bytes' }, checkpoint_timeout: { recommended: '15min', min: '5min', max: '20min', type: 'duration' }, checkpoint_completion_target: { recommended: '0.9', min: 0.8, max: 0.95, type: 'number' }, effective_io_concurrency: { recommended: '100', min: 50, max: 300, type: 'number' }, random_page_cost: { recommended: '1.1', min: 1, max: 1.5, type: 'number' }, jit: { recommended: 'off', exact: 'off', type: 'text' }, log_min_duration_statement: { recommended: '500ms', min: '100ms', max: '1000ms', type: 'duration' }, idle_in_transaction_session_timeout: { recommended: '60s', min: '30s', max: '120s', type: 'duration' }, statement_timeout: { recommended: '30s', min: '5s', max: '60s', type: 'duration' }, lock_timeout: { recommended: '5s', min: '1s', max: '10s', type: 'duration' }, }, }, }; export const defaultPostgresTuningProfile = 'shared-host'; export function getPostgresTuningProfile(name = defaultPostgresTuningProfile) { const profile = postgresTuningProfiles[name]; if (!profile) { throw new Error(`Unknown PostgreSQL tuning profile: ${name}`); } return profile; } export function getPostgresTuningSettingNames(profileName = defaultPostgresTuningProfile) { return Object.keys(getPostgresTuningProfile(profileName).settings); }