Database
Rise uses PostgreSQL for data storage with SQLX for compile-time verified SQL queries and migrations.
Overview
Section titled “Overview”Schema: Projects, Teams, Deployments, Service Accounts, Users
Schema Management
Section titled “Schema Management”Rise uses SQLX migrations for database schema versioning.
Migrations Directory
Section titled “Migrations Directory”Migrations in ./migrations/ (project root) with timestamp-based names.
Creating Migrations
Section titled “Creating Migrations”sqlx migrate add <description>Creates migrations/<timestamp>_<description>.sql. Edit and add SQL.
Running Migrations
Section titled “Running Migrations”Development: mise db:migrate (auto-run by mise backend:run)
Migrations run automatically on container startup in production.
Separate-crate schemas
Section titled “Separate-crate schemas”Two backend crates own their own migrations against the same database, each
isolated in a dedicated Postgres schema with its own _sqlx_migrations tracking
table (sqlx 0.8 hard-codes that table name, so the crates switch search_path
when migrating):
rise-resource-store-postgres→ schemaresource_store(generic resource storage).rise-runtime-sync→ schemaruntime_sync(cross-replica synchronization primitives: leader leases and schedules backingLeaderElection,GlobalSchedule, andGlobalLock).
Both run via run_migrations(&pool) from AppState::new, immediately after the
root ./migrations/ are applied. Their cargo sqlx prepare caches are
crate-local (crates/<crate>/.sqlx), kept separate from the root cache; see the
runtime-sync:sqlx:* mise tasks.
Migration Best Practices
Section titled “Migration Best Practices”- Test on production copy first
- Use
CREATE INDEX CONCURRENTLYin PostgreSQL - Avoid blocking operations on large tables
- Test rollback procedures
Database Access
Section titled “Database Access”Development
Section titled “Development”Connect to the local PostgreSQL database:
# Using psqldocker-compose exec postgres psql -U rise -d rise
# Or with connection stringpsql postgres://rise:rise123@localhost:5432/riseCommon queries:
-- List all projectsSELECT * FROM projects;
-- Show deployment statusSELECT name, status, created_at FROM deployments ORDER BY created_at DESC LIMIT 10;
-- Count usersSELECT COUNT(*) FROM users;
-- Show team membershipSELECT t.name, u.emailFROM teams tJOIN team_members tm ON t.id = tm.team_idJOIN users u ON tm.user_id = u.id;Production
Section titled “Production”Use read-only access for debugging:
# Connect with read-only userpsql postgres://rise_readonly:password@rds-endpoint:5432/rise
# Limit query results\set LIMIT 100SELECT * FROM projects LIMIT :LIMIT;Never run write queries directly on production. Use migrations instead.
Resetting the Database
Section titled “Resetting the Database”Development
Section titled “Development”Completely reset the development database:
# Remove database volumedocker-compose down -v
# Start freshmise backend:runThis deletes all data and re-runs migrations.
Soft Reset (Keep Schema)
Section titled “Soft Reset (Keep Schema)”Delete data without removing the schema:
# Connect to databasepsql postgres://rise:rise123@localhost:5432/rise
# Truncate tables (preserves schema)TRUNCATE deployments, projects, teams, team_members, users, service_accounts RESTART IDENTITY CASCADE;Performance Considerations
Section titled “Performance Considerations”Indexes
Section titled “Indexes”Create indexes for frequently queried columns:
-- Lookups by ownerCREATE INDEX idx_projects_owner ON projects(owner_type, owner_id);
-- Deployment status queriesCREATE INDEX idx_deployments_status ON deployments(status) WHERE status != 'stopped';
-- Expiration cleanupCREATE INDEX idx_deployments_expires_at ON deployments(expires_at) WHERE expires_at IS NOT NULL;Connection Pooling
Section titled “Connection Pooling”Configure connection pool size in config/production.yaml based on load and database limits.
Query Optimization
Section titled “Query Optimization”Use EXPLAIN ANALYZE to optimize slow queries:
EXPLAIN ANALYZESELECT * FROM deploymentsWHERE project_id = 123 AND status = 'running'ORDER BY created_at DESC;Troubleshooting
Section titled “Troubleshooting””Migrations have not been run”
Section titled “”Migrations have not been run””Problem: Backend can’t start because migrations are pending.
Solution:
mise db:migrate“SQLX cache is out of date”
Section titled ““SQLX cache is out of date””Problem: Query metadata doesn’t match actual database schema.
Solution:
cargo sqlx prepare“Connection refused”
Section titled ““Connection refused””Problem: Can’t connect to PostgreSQL.
Solution:
# Check if PostgreSQL is runningdocker-compose ps postgres
# Check logsdocker-compose logs postgres
# Restartdocker-compose restart postgresDeadlocks
Section titled “Deadlocks”Problem: Transactions blocking each other.
Solution:
- Keep transactions short
- Always acquire locks in the same order
- Use
SELECT ... FOR UPDATE NOWAITto fail fast
Next Steps
Section titled “Next Steps”- Learn about local development: See Local Development
- Production database setup: See Production Setup