Skip to main content

PostgreSQL

PostgreSQL is the production default for angzarr. ACID guarantees, mature tooling, and familiar operations make it the safest choice for most deployments.


Why PostgreSQL

StrengthBenefit
ACID transactionsEvents never partially commit
Strong durabilityfsync by default, no data loss
Mature toolingpg_dump, pg_restore, pgAdmin, extensions
Operational familiarityMost teams already know PostgreSQL
Vertical scalingSingle instance handles most workloads

Configuration

[storage]
backend = "postgres"

[storage.postgres]
url = "postgres://user:pass@localhost:5432/angzarr"
pool_size = 10
connect_timeout_seconds = 5

Environment Variables

export DATABASE_URL="postgres://user:pass@localhost:5432/angzarr"
export STORAGE_BACKEND="postgres"

Schema

Angzarr auto-migrates on startup. The schema includes:

-- Events table (append-only)
CREATE TABLE events (
id UUID PRIMARY KEY,
domain VARCHAR(255) NOT NULL,
edition VARCHAR(255) NOT NULL,
root UUID NOT NULL,
sequence BIGINT NOT NULL,
event_type VARCHAR(255) NOT NULL,
payload BYTEA NOT NULL,
correlation_id VARCHAR(255),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (domain, edition, root, sequence)
);

-- Positions table (handler checkpoints)
CREATE TABLE positions (
handler VARCHAR(255) NOT NULL,
domain VARCHAR(255) NOT NULL,
edition VARCHAR(255) NOT NULL,
root UUID NOT NULL,
sequence BIGINT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (handler, domain, edition, root)
);

-- Snapshots table (aggregate state cache)
CREATE TABLE snapshots (
domain VARCHAR(255) NOT NULL,
edition VARCHAR(255) NOT NULL,
root UUID NOT NULL,
sequence BIGINT NOT NULL,
state BYTEA NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (domain, edition, root)
);

Optimistic Concurrency

The unique constraint on (domain, edition, root, sequence) enforces optimistic concurrency:

-- Two concurrent writes to same aggregate with same sequence
INSERT INTO events (..., sequence) VALUES (..., 5); -- First succeeds
INSERT INTO events (..., sequence) VALUES (..., 5); -- Second fails: unique violation

The second writer must reload state and retry with the correct sequence.


Helm Deployment

# values.yaml
storage:
backend: postgres

postgres:
enabled: true
host: postgres.database.svc.cluster.local
port: 5432
database: angzarr
credentials:
secretName: postgres-credentials
usernameKey: username
passwordKey: password

Testing

# Run PostgreSQL tests (requires testcontainers)
cargo test --test storage_postgres --features postgres

# Requires podman socket
systemctl --user start podman.socket

Next Steps