Database Issues
Troubleshooting database connection problems, query issues, and migration failures.
Database Issues
Comprehensive guide to resolving database-related problems in your application.
Connection Problems
Database Connection Timeout
Symptoms:
- Connection timeout errors
- Slow database responses
- Network connectivity issues
Solutions:
// Increase timeout in Supabase client
const supabase = createClient(url, key, {
db: {
schema: 'public',
},
global: {
headers: {
'X-Client-Info': 'smart-shelf',
},
},
// Increase timeout
realtime: {
params: {
timeout: 30000,
},
},
})
// Add retry logic
async function withRetry<T>(
operation: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation()
} catch (error) {
if (i === maxRetries - 1) throw error
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)))
}
}
throw new Error('Max retries exceeded')
}
Connection Pool Exhaustion
Symptoms:
- "Too many connections" errors
- Application hangs waiting for database connections
Solutions:
// Configure connection pooling
const supabase = createClient(url, key, {
db: {
schema: 'public',
},
// Limit concurrent connections
realtime: {
params: {
eventsPerSecond: 10,
},
},
})
// Use connection pooling middleware
export const poolConfig = {
max: 20, // Maximum number of connections
min: 2, // Minimum number of connections
acquire: 30000, // Maximum time to wait for connection
idle: 10000, // Maximum time connection can be idle
}
Query Issues
Slow Database Queries
Symptoms:
- Long response times
- Timeout errors
- Poor application performance
Solutions:
-- Add indexes for frequently queried columns
CREATE INDEX CONCURRENTLY idx_products_name ON products(name);
CREATE INDEX CONCURRENTLY idx_inventory_product_warehouse ON inventory(product_id, warehouse_id);
CREATE INDEX CONCURRENTLY idx_orders_created_at ON orders(created_at);
-- Optimize queries with EXPLAIN ANALYZE
EXPLAIN ANALYZE SELECT * FROM products WHERE name ILIKE '%search%';
-- Use proper LIMIT and OFFSET for pagination
SELECT * FROM products
ORDER BY created_at DESC
LIMIT 20 OFFSET 0;
-- Avoid N+1 queries with proper joins
SELECT
p.*,
i.quantity_on_hand
FROM products p
LEFT JOIN inventory i ON p.id = i.product_id
WHERE p.is_active = true;
Query Optimization Best Practices
// Use specific column selection
const { data } = await supabase
.from('products')
.select(`
id,
name,
sku,
inventory!inner(quantity_on_hand)
`)
.eq('is_active', true)
.limit(20)
// Batch operations instead of individual queries
const batchInsert = await supabase
.from('products')
.insert([
{ name: 'Product 1', sku: 'SKU1' },
{ name: 'Product 2', sku: 'SKU2' },
{ name: 'Product 3', sku: 'SKU3' },
])
// Use proper filtering
const { data } = await supabase
.from('products')
.select('*')
.gte('created_at', '2024-01-01')
.lt('created_at', '2024-12-31')
.order('created_at', { ascending: false })
Row Level Security (RLS) Issues
Symptoms:
- No data returned from queries
- Permission denied errors
- Unauthorized access attempts
Solutions:
-- Check existing RLS policies
SELECT
schemaname,
tablename,
policyname,
permissive,
roles,
cmd,
qual,
with_check
FROM pg_policies
WHERE tablename = 'products';
-- Create proper RLS policies
CREATE POLICY "Users can view products" ON products
FOR SELECT USING (true);
CREATE POLICY "Users can insert products" ON products
FOR INSERT WITH CHECK (auth.role() = 'authenticated');
CREATE POLICY "Users can update own products" ON products
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "Admins can delete products" ON products
FOR DELETE USING (
auth.jwt() ->> 'role' = 'admin'
);
-- Enable RLS on table
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
-- Temporarily disable RLS for debugging (not recommended for production)
ALTER TABLE products DISABLE ROW LEVEL SECURITY;
Migration Issues
Migration Failures
Symptoms:
- Database schema out of sync
- Migration rollback errors
- Data consistency issues
Solutions:
# Check migration status
supabase migration list
# Reset database migrations (development only)
supabase db reset
# Apply specific migration
supabase db migrate up --file 20240101000000_initial_schema.sql
# Generate new migration
supabase migration new add_products_table
# Rollback migration
supabase migration down --file 20240101000000_initial_schema.sql
Data Migration Best Practices
-- Always backup before migrations
pg_dump -h your-host -U your-user your-database > backup.sql
-- Use transactions for complex migrations
BEGIN;
-- Add new column with default value
ALTER TABLE products ADD COLUMN category_id INTEGER DEFAULT 1;
-- Update existing data
UPDATE products SET category_id = 2 WHERE type = 'electronics';
-- Add constraints
ALTER TABLE products ADD CONSTRAINT fk_category
FOREIGN KEY (category_id) REFERENCES categories(id);
COMMIT;
-- Handle large table migrations
-- Create new table
CREATE TABLE products_new (LIKE products INCLUDING ALL);
-- Add new columns
ALTER TABLE products_new ADD COLUMN new_field VARCHAR(255);
-- Copy data in batches
INSERT INTO products_new SELECT *, 'default_value'
FROM products WHERE id BETWEEN 1 AND 10000;
-- Swap tables
BEGIN;
DROP TABLE products;
ALTER TABLE products_new RENAME TO products;
COMMIT;
Performance Optimization
Database Monitoring
-- Monitor active queries
SELECT
pid,
now() - pg_stat_activity.query_start AS duration,
query,
state
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY duration DESC;
-- Check table sizes
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- Analyze query performance
SELECT
query,
calls,
total_time,
mean_time,
rows
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;
Index Optimization
-- Find missing indexes
SELECT
schemaname,
tablename,
attname,
n_distinct,
correlation
FROM pg_stats
WHERE schemaname = 'public'
AND n_distinct > 100;
-- Monitor index usage
SELECT
indexrelname,
idx_tup_read,
idx_tup_fetch,
idx_scan
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;
-- Remove unused indexes
SELECT
schemaname,
tablename,
indexname,
idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0;
Troubleshooting Tools
Database Debugging Commands
# Connect to database
psql -h your-host -U your-user -d your-database
# Check database status
\conninfo
\l # List databases
\dt # List tables
\di # List indexes
# Analyze table
ANALYZE products;
# Vacuum table
VACUUM ANALYZE products;
# Check for locks
SELECT * FROM pg_locks WHERE NOT granted;
Monitoring Queries
// Add query logging
const supabase = createClient(url, key, {
global: {
headers: {
'X-Client-Info': 'your-app',
},
},
})
// Monitor Supabase queries
supabase.realtime.setAuth(token)
const subscription = supabase
.channel('db-changes')
.on('postgres_changes',
{ event: '*', schema: 'public' },
(payload) => {
console.log('Database change:', payload)
}
)
.subscribe()
Common Error Messages
"relation does not exist"
- Cause: Table or view not found
- Solution: Check table name, schema, and migrations
"column does not exist"
- Cause: Column name typo or missing migration
- Solution: Verify column names and run migrations
"permission denied for table"
- Cause: RLS policy blocking access
- Solution: Check and update RLS policies
"deadlock detected"
- Cause: Concurrent transactions waiting for each other
- Solution: Implement proper transaction ordering and retry logic
For additional database troubleshooting, refer to the Supabase Documentation or contact support.