Skip to content

PostgreSQL

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


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

[storage]
backend = "postgres"
[storage.postgres]
url = "postgres://user:pass@localhost:5432/angzarr"
pool_size = 10
connect_timeout_seconds = 5
Terminal window
export DATABASE_URL="postgres://user:pass@localhost:5432/angzarr"
export STORAGE_BACKEND="postgres"

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)
);

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.


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

Terminal window
# Run PostgreSQL interface tests (requires testcontainers)
STORAGE_BACKEND=postgres cargo test --test interfaces --features "postgres test-utils"
# Requires podman socket
systemctl --user start podman.socket