Skip to content

Database Schema

OsuRender API uses PostgreSQL 16 with async SQLAlchemy ORM. Migrations are managed by Alembic.

Entity-Relationship Diagram

Jobs Table

The jobs table is the primary entity storing render job state.

Columns

ColumnTypeNullableDefaultDescription
idUUIDuuid4()Primary key
statusjob_statusqueuedCurrent job state
progressFLOAT0.0Render progress percentage
replay_storage_keyVARCHAR(512)S3 key of uploaded replay
configJSONB{}Rendering configuration + replay stats
beatmap_idINTEGERosu! beatmap ID (resolved during download)
map_titleVARCHAR(512)"Artist - Title" (resolved during download)
client_ipVARCHAR(45)Submitter's IP (for rate limiting)
modal_call_idVARCHAR(100)Modal function call ID (for polling fallback)
video_storage_keyVARCHAR(512)S3 key of rendered video
thumb_storage_keyVARCHAR(512)S3 key of thumbnail
analytics_storage_keyVARCHAR(512)S3 key of extracted analytics frames
error_messageTEXTError details on failure
retry_countINTEGER0Number of dispatch retries
created_atTIMESTAMP(tz)now()Job creation time
updated_atTIMESTAMP(tz)now()Last modification time (auto-updated)

Indexes

NameColumnsPurpose
idx_jobs_statusstatusFast filtering by status
idx_jobs_created_atcreated_atOrdering and pagination
(implicit)client_ipPer-IP job counting

Enum: job_status

sql
CREATE TYPE job_status AS ENUM (
    'queued', 'downloading', 'rendering', 'completed', 'failed'
);

Outbox Events Table

The outbox_events table implements the transactional outbox pattern.

Columns

ColumnTypeNullableDefaultDescription
idUUIDuuid4()Primary key
event_typeVARCHAR(100)Event name (e.g., render_job_created)
payloadJSONBEvent data (contains job_id)
statusoutbox_statusPENDINGCurrent event state
retry_countINTEGER0Dispatch retry count
created_atTIMESTAMP(tz)now()Event creation time
processing_started_atTIMESTAMP(tz)When dispatcher claimed the event
processed_atTIMESTAMP(tz)When the event was fully processed
last_errorTEXTError from last dispatch attempt

Indexes

NameColumnsPurpose
idx_outbox_status_createdstatus, created_atOrdered drain queries
idx_outbox_processingstatus, processing_started_atStuck event sweeper

Enum: outbox_status

sql
CREATE TYPE outbox_status AS ENUM (
    'PENDING', 'PROCESSING', 'DISPATCHED', 'PROCESSED', 'FAILED'
);

Migrations

Database migrations are managed by Alembic:

bash
# Create a new migration
alembic revision --autogenerate -m "description"

# Apply all migrations
alembic upgrade head

# Rollback one migration
alembic downgrade -1

The alembic/env.py reads DATABASE_URL_SYNC from the application settings for connection.

Connection Management

python
# Async engine with connection pooling
engine = create_async_engine(
    settings.database_url,
    pool_size=5,
    max_overflow=10,
    pool_pre_ping=True,   # Detect stale connections
)

# Session factory with expire_on_commit=False
# (allows reading attributes after commit without re-query)
session_factory = async_sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False,
)

Built with VitePress