Files
ai.allucanget.biz/docs/8.2-database.md
T

2.4 KiB

DuckDB Concurrency and Storage

Single Writer Per Process

DuckDB allows only one process to open the database file in read-write mode at a time. The FastAPI backend must be run with a single worker (uvicorn --workers 1). Running multiple workers against the same DuckDB file will cause startup errors.

asyncio.Lock for Writes

All database write operations (INSERT, UPDATE, DELETE) in the FastAPI async context are wrapped in a single asyncio.Lock (get_write_lock() from backend/app/db.py). This prevents concurrent coroutines from issuing overlapping writes within the single process, which would otherwise raise DuckDB optimistic concurrency errors.

Read operations (SELECT) do not require the lock — DuckDB's MVCC provides consistent read snapshots.

Schema

CREATE TABLE users (
    id            UUID      DEFAULT uuid() PRIMARY KEY,
    email         VARCHAR   NOT NULL UNIQUE,
    password_hash VARCHAR   NOT NULL,
    role          VARCHAR   DEFAULT 'user',
    created_at    TIMESTAMP DEFAULT now(),
    updated_at    TIMESTAMP DEFAULT now()
);

CREATE TABLE refresh_tokens (
    jti        UUID      DEFAULT uuid() PRIMARY KEY,
    user_id    UUID      NOT NULL,           -- soft FK to users.id
    issued_at  TIMESTAMP DEFAULT now(),
    expires_at TIMESTAMP NOT NULL,
    revoked    BOOLEAN   DEFAULT false
);

The REFERENCES users(id) foreign key is intentionally omitted from refresh_tokens. DuckDB fires FK checks on UPDATE of the parent table (including email changes), causing false constraint violations. Referential integrity is enforced manually: deleting a user also deletes their refresh tokens in the same write transaction.

Access Tokens

Access tokens are stateless JWTs — not stored in the database. They are validated by signature and expiry claim only. The short TTL (15 minutes) limits the blast radius if a token is leaked.

Refresh Tokens

Refresh tokens store a JTI (JWT ID) UUID in the refresh_tokens table. On each use the old JTI is revoked and a new one issued (rotation). On logout the JTI is immediately revoked. Expired and revoked tokens can be purged via POST /admin/tokens/purge.

Future: AI Generation History

AI generation metadata (model, prompt, cost, result URLs) can be stored as JSON columns in a future generation_history table in DuckDB, enabling per-user analytics and usage dashboards at zero extra infrastructure cost.