Common Issues
PostgreSQL Troubleshooting
Solutions for PostgreSQL-specific issues with FraiseQL.
Connection Issues
Section titled “Connection Issues”Connection Refused
Section titled “Connection Refused”Problem: FATAL: could not connect to server: Connection refused
Solutions:
# 1. Check PostgreSQL is runningsudo systemctl status postgresql# Or for Dockerdocker-compose logs postgres
# 2. Verify port 5432 is opensudo netstat -tlnp | grep 5432# Or: sudo lsof -i :5432
# 3. Check postgresql.conf# Verify: listen_addresses = '*'sudo nano /etc/postgresql/16/main/postgresql.conf
# 4. Reload configurationsudo systemctl reload postgresql
# 5. Test connectionpsql -h localhost -U postgres -c "SELECT 1"Authentication Failed
Section titled “Authentication Failed”Problem: FATAL: password authentication failed for user "fraiseql"
Solutions:
# 1. Verify user existssudo -u postgres psql -c "\du"
# 2. Reset passwordsudo -u postgres psql -c "ALTER USER fraiseql WITH PASSWORD 'newpassword';"
# 3. Check pg_hba.conf (password authentication method)sudo nano /etc/postgresql/16/main/pg_hba.conf# Make sure line has: md5 or scram-sha-256
# 4. Reload authenticationsudo systemctl reload postgresql
# 5. Test with new passwordPGPASSWORD=newpassword psql -h localhost -U fraiseql -d fraiseql -c "SELECT 1"SSL Certificate Error
Section titled “SSL Certificate Error”Problem: SSL: CERTIFICATE_VERIFY_FAILED
Solutions:
# 1. Disable SSL for local developmentDATABASE_URL=postgresql://user:pass@localhost/db?sslmode=disable
# 2. For production, use proper certificateDATABASE_URL=postgresql://user:pass@host/db?sslmode=require&sslcert=/path/to/client-cert.pem&sslkey=/path/to/client-key.pem&sslrootcert=/path/to/ca-cert.pem
# 3. Check certificate validityopenssl x509 -in ca-cert.pem -noout -datesopenssl x509 -in ca-cert.pem -noout -text
# 4. Verify PostgreSQL SSL configuration# In postgresql.confssl = onssl_cert_file = '/path/to/cert.pem'ssl_key_file = '/path/to/key.pem'
# 5. Reload configurationsudo systemctl reload postgresqlToo Many Connections
Section titled “Too Many Connections”Problem: FATAL: too many connections for role "fraiseql"
Solutions:
# 1. Check current connectionssudo -u postgres psql -c "SELECT datname, count(*) FROM pg_stat_activity GROUP BY datname;"
# 2. Kill idle connectionssudo -u postgres psql -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND query_start < NOW() - INTERVAL '10 minutes';"
# 3. Check max connectionssudo -u postgres psql -c "SHOW max_connections;"
# 4. Increase max connectionssudo nano /etc/postgresql/16/main/postgresql.conf# max_connections = 200
# 5. Restart PostgreSQLsudo systemctl restart postgresql
# 6. Increase FraiseQL connection poolPGBOUNCER_MAX_POOL_SIZE=30Query Performance Issues
Section titled “Query Performance Issues”Slow Queries
Section titled “Slow Queries”Problem: p95 latency > 500ms
Solutions:
# 1. Enable slow query loggingsudo -u postgres psql -d fraiseql -c "ALTER SYSTEM SET log_min_duration_statement = 500;"sudo -u postgres psql -c "SELECT pg_reload_conf();"
# 2. View slow query logsudo tail -f /var/log/postgresql/postgresql.log | grep "duration:"
# 3. Find slow queries in pg_stat_statementssudo -u postgres psql -d fraiseql -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"sudo -u postgres psql -d fraiseql -c "SELECT mean_exec_time, calls, query FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;"
# 4. Analyze slow querysudo -u postgres psql -d fraiseql -c "EXPLAIN ANALYZE SELECT ... ;"
# 5. Add missing indexessudo -u postgres psql -d fraiseql -c "CREATE INDEX idx_posts_user_id ON posts(user_id);"Full Table Scan
Section titled “Full Table Scan”Problem: Query slowly scans entire table
Solutions:
# 1. Check for missing indexEXPLAIN (ANALYZE, BUFFERS)SELECT * FROM posts WHERE user_id = 123;
# 2. If "Seq Scan" (sequential scan), add indexCREATE INDEX idx_posts_user_id ON posts(user_id);
# 3. Analyze tables so query planner has statsANALYZE posts;
# 4. Check index is being usedREINDEX INDEX idx_posts_user_id;
# 5. Vacuum to update table statsVACUUM ANALYZE posts;Slow Joins
Section titled “Slow Joins”Problem: Queries with multiple JOINs are slow
Solutions:
# 1. Create composite index for JOIN columnsCREATE INDEX idx_posts_user_publishedON posts(user_id, published)WHERE published = true;
# 2. Check join selectivityEXPLAIN SELECT * FROM posts pJOIN comments c ON p.id = c.post_idWHERE p.user_id = 123;
# 3. Consider materialized viewCREATE MATERIALIZED VIEW v_posts_with_comments ASSELECT p.id, p.title, COUNT(c.id) as comment_countFROM posts pLEFT JOIN comments c ON p.id = c.post_idGROUP BY p.id, p.title;
REFRESH MATERIALIZED VIEW v_posts_with_comments;Data Issues
Section titled “Data Issues”CASCADE DELETE Not Working
Section titled “CASCADE DELETE Not Working”Problem: Deleting parent doesn’t delete children
Solutions:
# 1. Check foreign key definitionSELECT constraint_name, table_name, column_nameFROM information_schema.key_column_usageWHERE table_name = 'posts' AND column_name = 'user_id';
# 2. Check CASCADE ruleSELECT constraint_definition FROM information_schema.table_constraintsWHERE table_name = 'posts' AND constraint_type = 'FOREIGN KEY';
# 3. Recreate foreign key with CASCADEALTER TABLE comments DROP CONSTRAINT comments_post_id_fkey;ALTER TABLE commentsADD CONSTRAINT comments_post_id_fkeyFOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE;
# 4. Test CASCADE deleteBEGIN;DELETE FROM posts WHERE id = 123;-- Check if comments deleted tooSELECT * FROM comments WHERE post_id = 123;ROLLBACK;Foreign Key Constraint Violation
Section titled “Foreign Key Constraint Violation”Problem: Error: insert or update on table "comments" violates foreign key constraint
Solutions:
# 1. Check foreign key constraintSELECT * FROM information_schema.key_column_usageWHERE table_name = 'comments' AND column_name = 'post_id';
# 2. Verify referenced record existsSELECT * FROM posts WHERE id = 123;
# 3. Check for orphaned recordsSELECT c.* FROM comments cLEFT JOIN posts p ON c.post_id = p.idWHERE p.id IS NULL;
# 4. Clean orphaned recordsDELETE FROM commentsWHERE post_id NOT IN (SELECT id FROM posts);
# 5. Temporarily disable constraint (careful!)ALTER TABLE comments DISABLE TRIGGER ALL;-- Do your import/migrationALTER TABLE comments ENABLE TRIGGER ALL;Duplicate Key Error
Section titled “Duplicate Key Error”Problem: Error: duplicate key value violates unique constraint "idx_users_email"
Solutions:
# 1. Check unique constraintSELECT constraint_name, column_nameFROM information_schema.key_column_usageWHERE table_name = 'users' AND constraint_name LIKE 'idx_%';
# 2. Find duplicate valuesSELECT email, COUNT(*) FROM usersGROUP BY email HAVING COUNT(*) > 1;
# 3. Remove duplicatesDELETE FROM users WHERE id IN ( SELECT id FROM ( SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) as rn FROM users ) t WHERE rn > 1);
# 4. Ensure email is uniqueCREATE UNIQUE INDEX idx_users_email ON users(LOWER(email));NULL Value in NOT NULL Column
Section titled “NULL Value in NOT NULL Column”Problem: Error: null value in column "title" violates not-null constraint
Solutions:
# 1. Check NOT NULL constraintsSELECT column_name, is_nullableFROM information_schema.columnsWHERE table_name = 'posts';
# 2. Find NULL valuesSELECT * FROM posts WHERE title IS NULL;
# 3. Fix NULL valuesUPDATE posts SET title = 'Untitled' WHERE title IS NULL;
# 4. Add DEFAULT value for future insertsALTER TABLE posts ALTER COLUMN title SET DEFAULT 'Untitled';
# 5. Add NOT NULL if appropriateALTER TABLE posts ALTER COLUMN title SET NOT NULL;Transaction and Locking Issues
Section titled “Transaction and Locking Issues”Deadlock Detected
Section titled “Deadlock Detected”Problem: Error: deadlock detected
Solutions:
# 1. Check for deadlocks in logssudo grep "deadlock detected" /var/log/postgresql/postgresql.log
# 2. Identify deadlock victimsSELECT pid, usename, application_name, state, queryFROM pg_stat_activityWHERE state != 'idle'ORDER BY query_start;
# 3. Kill blocking querySELECT pg_terminate_backend(pid)FROM pg_stat_activityWHERE query ~ 'INSERT INTO posts' AND state = 'active';
# 4. Reduce transaction scope-- BAD: Long transactionBEGIN;SELECT * FROM posts WHERE user_id = 1;-- Long operationUPDATE posts SET title = 'New';COMMIT;
-- GOOD: Minimal transactionBEGIN;UPDATE posts SET title = 'New' WHERE user_id = 1;COMMIT;-- Long operation outside transaction
# 5. Use consistent lock orderingBEGIN;SELECT * FROM users WHERE id = 1 FOR UPDATE;SELECT * FROM posts WHERE user_id = 1 FOR UPDATE;COMMIT;Table Lock Timeout
Section titled “Table Lock Timeout”Problem: Error: canceling statement due to lock timeout
Solutions:
# 1. Check for long-running transactionsSELECT pid, usename, application_name, xact_start, queryFROM pg_stat_activityWHERE state != 'idle'ORDER BY xact_start;
# 2. Kill blocking transactionSELECT pg_terminate_backend(pid)FROM pg_stat_activityWHERE xact_start < NOW() - INTERVAL '1 hour';
# 3. Increase lock timeoutALTER SYSTEM SET lock_timeout = '30s';SELECT pg_reload_conf();
# 4. Use non-blocking modeSELECT * FROM posts WHERE user_id = 1FOR UPDATE SKIP LOCKED;Transaction Rollback
Section titled “Transaction Rollback”Problem: Changes unexpectedly rolled back
Solutions:
# 1. Check transaction logssudo tail -f /var/log/postgresql/postgresql.log | grep "ROLLBACK"
# 2. Verify all constraints before committingBEGIN;INSERT INTO posts (title, user_id) VALUES ('New', 999);-- Check foreign key existsSELECT * FROM users WHERE id = 999;COMMIT;
# 3. Use savepoints for recoveryBEGIN;UPDATE posts SET title = 'New' WHERE id = 1;SAVEPOINT s1;UPDATE posts SET title = 'Invalid' WHERE id = 2;ROLLBACK TO s1;COMMIT;
# 4. Check PostgreSQL log levelALTER SYSTEM SET log_statement = 'all';ALTER SYSTEM SET log_min_error_statement = 'error';SELECT pg_reload_conf();Replication Issues
Section titled “Replication Issues”Replica Lag
Section titled “Replica Lag”Problem: Replica is behind primary (old data)
Solutions:
# 1. Check replication status (on primary)SELECT slot_name, restart_lsn, confirmed_flush_lsnFROM pg_replication_slots;
# 2. Check replica lag (on primary)SELECT client_addr, state, sync_state, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) as bytes_behindFROM pg_stat_replication;
# 3. On replica, check WAL applicationSELECT now() - pg_last_xact_replay_time() as replication_lag;
# 4. Increase WAL settings to speed up replicationALTER SYSTEM SET max_wal_senders = 10;ALTER SYSTEM SET wal_keep_size = '1GB';SELECT pg_reload_conf();
# 5. Manually sync if very far behindpg_basebackup -h primary_host -D /var/lib/postgresql/16/main -U replicator -P -vReplication Slot Inactive
Section titled “Replication Slot Inactive”Problem: Replication slot marked as inactive
Solutions:
# 1. Check slot statusSELECT slot_name, slot_type, active, restart_lsnFROM pg_replication_slots;
# 2. Recreate if not usedSELECT pg_drop_replication_slot('slot_name');SELECT * FROM pg_create_physical_replication_slot('slot_name');
# 3. Ensure replica is trying to connectsudo cat /etc/postgresql/16/main/recovery.confIndex Issues
Section titled “Index Issues”Index Corruption
Section titled “Index Corruption”Problem: Queries return wrong results or crash
Solutions:
# 1. ReindexREINDEX INDEX idx_posts_user_id;
# 2. Or reindex allREINDEX DATABASE fraiseql;
# 3. Check index validitySELECT * FROM pg_indexes WHERE tablename = 'posts';EXPLAIN SELECT * FROM posts WHERE user_id = 1;
# 4. Recreate indexDROP INDEX idx_posts_user_id;CREATE INDEX idx_posts_user_id ON posts(user_id);Unused Indexes
Section titled “Unused Indexes”Problem: Taking space but not improving performance
Solutions:
# 1. Find unused indexesSELECT schemaname, tablename, indexname, idx_scanFROM pg_stat_user_indexesWHERE idx_scan = 0ORDER BY pg_relation_size(indexrelid) DESC;
# 2. Drop unused indexesDROP INDEX IF EXISTS idx_old_index;
# 3. Monitor index usageSELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetchFROM pg_stat_user_indexesORDER BY idx_scan DESC;
# 4. Check index sizeSELECT tablename, indexname, pg_size_pretty(pg_relation_size(indexrelid))FROM pg_stat_user_indexesORDER BY pg_relation_size(indexrelid) DESC;Backup and Recovery
Section titled “Backup and Recovery”Backup Failed
Section titled “Backup Failed”Problem: pg_dump fails or hangs
Solutions:
# 1. Create backup with proper settingspg_dump -h localhost -U fraiseql fraiseql > backup.sql \ --verbose \ --format=plain \ --compress=gzip \ --jobs=4
# 2. Check for long-running transactions blocking backupSELECT pid, usename, xact_start, queryFROM pg_stat_activityWHERE xact_start IS NOT NULL;
# 3. Increase timeout for backuppg_dump --timeout=300 fraiseql > backup.sql
# 4. Use custom format for parallelismpg_dump -Fc fraiseql > backup.dump
# 5. Restore from backuppg_restore -U fraiseql -d fraiseql backup.dumpPoint-in-Time Recovery
Section titled “Point-in-Time Recovery”Problem: Need to recover to specific timestamp
Solutions:
# 1. Enable WAL archiving (before failure)ALTER SYSTEM SET wal_level = 'replica';ALTER SYSTEM SET archive_mode = 'on';ALTER SYSTEM SET archive_command = 'cp %p /mnt/backups/%f';SELECT pg_reload_conf();
# 2. After failure, restore WAL segmentcp /mnt/backups/000000010000000000000001 /var/lib/postgresql/16/main/pg_wal/
# 3. Create recovery config signalsudo touch /var/lib/postgresql/16/main/recovery.signal
# 4. Set recovery target timeALTER SYSTEM SET recovery_target_timeline = 'latest';ALTER SYSTEM SET recovery_target_time = '2024-01-15 14:00:00';
# 5. Start PostgreSQL and monitorsudo systemctl start postgresqlsudo tail -f /var/log/postgresql/postgresql.logMaintenance
Section titled “Maintenance”Vacuum and Analyze
Section titled “Vacuum and Analyze”Problem: Tables bloated, queries slow
Solutions:
# 1. Run vacuum (cleanup dead rows)VACUUM ANALYZE posts;
# 2. Full vacuum (slowest but most thorough)VACUUM FULL posts;
# 3. Schedule regular maintenance-- In crontab0 2 * * * vacuumdb -U postgres fraiseql
# 4. Auto-vacuum settingsALTER TABLE posts SET (autovacuum_vacuum_scale_factor = 0.01);ALTER TABLE posts SET (autovacuum_analyze_scale_factor = 0.005);Database Size
Section titled “Database Size”Problem: Database growing too large
Solutions:
# 1. Check database sizeSELECT pg_size_pretty(pg_database_size('fraiseql'));
# 2. Find largest tablesSELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as sizeFROM pg_tablesORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
# 3. Archive old dataDELETE FROM audit_logs WHERE created_at < NOW() - INTERVAL '1 year';DELETE FROM activity_logs WHERE created_at < NOW() - INTERVAL '30 days';
# 4. Reclaim spaceVACUUM FULL;Diagnostic Queries
Section titled “Diagnostic Queries”-- Active connectionsSELECT pid, usename, application_name, state, query, query_startFROM pg_stat_activityWHERE state != 'idle'ORDER BY query_start;
-- Long transactionsSELECT pid, xact_start, queryFROM pg_stat_activityWHERE xact_start < NOW() - INTERVAL '5 minutes';
-- Index usageSELECT schemaname, tablename, indexname, idx_scanFROM pg_stat_user_indexesORDER BY idx_scan DESC;
-- Table sizesSELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))FROM pg_tablesORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- Slow queries (enable pg_stat_statements first)SELECT mean_exec_time, calls, query FROM pg_stat_statementsORDER BY mean_exec_time DESC LIMIT 20;
-- Cache hit ratio (should be > 99%)SELECT sum(heap_blks_read) as heap_read, sum(heap_blks_hit) as heap_hit, sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratioFROM pg_statio_user_tables;FraiseQL Configuration for PostgreSQL
Section titled “FraiseQL Configuration for PostgreSQL”In fraiseql.toml, tune PostgreSQL-specific settings:
[database]url = "${DATABASE_URL}"pool_size = 10pool_timeout = 30statement_timeout = "5000ms"ssl_mode = "require" # "disable", "allow", "prefer", "require", "verify-ca", "verify-full"
[database.postgresql]application_name = "fraiseql"schema_search_path = "public,fraiseql_internal"Performance Issues
PostgreSQL Documentation