db

package
v0.6.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package db provides Postgres connection bootstrapping and schema migration for the Wardyn control plane. Postgres is the ONLY required dependency.

Index

Constants

View Source
const AuditChainLockKey int64 = 0x5741524459_434841 // ASCII "WARDYCHA"

AuditChainLockKey serializes appends to the audit_events hash chain (migration 0047). Unlike every key above it is taken with the TRANSACTION -scoped pg_advisory_xact_lock, never the session-scoped form: it is released by the commit that makes the new row visible, so the next writer's head read cannot miss it, and no code path can leak it by forgetting a release. It must be taken BEFORE the INSERT statement, on the inserting transaction — not inside 0047's BEFORE INSERT trigger, where the identity default has already assigned seq and two racing writers could invert chain order against seq order. Both in-tree insert paths do this: store.InsertAuditEvent and the broker's insertAuditEventTx. ponytail: ONE lock for the whole chain, so audit appends are globally serialized. That IS the feature (a chain has exactly one head), and audit write volume is nowhere near a contention regime. If it ever is, the upgrade is per-partition chains with a key per partition, not a finer lock over one.

View Source
const GroundTruthRotatorLockKey int64 = 0x5741524459_475452 // ASCII "WARDYGTR"

GroundTruthRotatorLockKey elects the ground-truth token rotator's leader across control planes (S2). Unlike ReaperAdvisoryLockKey (re-tried every tick via TryAdvisoryLock, release()d at the end of each one), this key is acquired ONCE before the rotator's loop starts and held for the process lifetime: only the holder mints/writes the shared token file, and every other replica parks on a backoff and retries, taking over automatically when the holder's Postgres session ends.

It buys AT MOST ONE STEADY-STATE leader, NOT mutual exclusion. An advisory lock dies with its SESSION, not with the process, and the holder never re-verifies it: a Postgres restart, a failover, pg_terminate_backend or an idle-session timeout releases it under a still-running leader, and a standby takes over within one backoff — two rotators, neither aware. Harmless for THIS workload only, because every write is an atomic rename of a stateless token (cmd/wardynd/gt_rotator.go). Work that needs genuine fencing must not reuse this key. Any stable value works, as long as it differs from every other key in this file.

View Source
const ReaperAdvisoryLockKey int64 = 0x5741524459_524541 // ASCII "WARDYREA"

ReaperAdvisoryLockKey makes the lifecycle reap tick single-flight across control planes. Unlike migrateAdvisoryLockKey (a BLOCKING lock — the second boot must still see the migrations applied), this one is only ever taken with TryAdvisoryLock: a replica that loses SKIPS the tick, because a queued second reap of the same runs is pure duplicate work and a duplicate run.autostop.

View Source
const SecretRekeyLockKey int64 = 0x5741524459_524B59 // ASCII "WARDYRKY"

SecretRekeyLockKey serializes the `wardynd -rotate-age-key` maintenance mode (cmd/wardynd's rotateAgeKeyMode): two concurrent rekeys of the same store would each re-encrypt from an old key the other has already replaced, so the second is refused rather than queued (TryAdvisoryLock, like ReaperAdvisoryLockKey).

HONEST CEILING — this does NOT detect a running wardynd. No wardynd holds a process-lifetime lock on this key or any other unconditional one (the reaper takes ReaperAdvisoryLockKey per tick and releases it; GroundTruthRotatorLockKey is only taken when the rotator is configured), so a serving daemon is invisible to this check. "Stop the daemon first" is an operator procedure documented in docs/OPERATIONS.md, not something this lock enforces — a live daemon holds the OLD identity in memory and would write ciphertext under a key the rekey has already retired.

Variables

This section is empty.

Functions

func AuditDDLProtected

func AuditDDLProtected(ctx context.Context, pool *pgxpool.Pool) (bool, error)

AuditDDLProtected reports whether the given (application) pool's role is UNABLE to bypass the audit_events append-only triggers via DDL — i.e. it is neither a superuser nor a MEMBER of the table's owner role (membership, not just direct ownership: a role GRANTed the owner role inherits DROP TRIGGER / ALTER ... DISABLE TRIGGER rights). The N4 role-separation only protects the append-only guarantee when this is true, so the two-DSN deploy must be VERIFIED here rather than assumed (honesty: never log a protection claim stronger than the enforcing role setup). Fails safe: any ambiguity (missing table, error) reports NOT protected.

func Connect

func Connect(ctx context.Context, dsn string) (*pgxpool.Pool, error)

Connect opens a pgxpool to dsn and performs a lightweight liveness check. Returns the pool; caller owns Close().

func Migrate

func Migrate(ctx context.Context, pool *pgxpool.Pool) error

Migrate applies all migrations in internal/db/migrations/*.sql in lexical order. Each migration runs inside its own transaction; already-applied filenames (tracked in schema_migrations) are skipped. Idempotent.

func TryAdvisoryLock added in v0.5.0

func TryAdvisoryLock(ctx context.Context, pool *pgxpool.Pool, key int64) (release func(), ok bool, err error)

TryAdvisoryLock takes session-level advisory lock key on a connection borrowed from pool WITHOUT waiting, reporting ok=false when another session already holds it. Call the returned release (deferred) to unlock and hand the connection back — skipping it strands a pooled conn for the life of the process. Only for work of BOUNDED duration: the borrowed conn is unavailable to everyone else until release — which also means the caller's own queries need a SECOND conn, so this requires pool_max_conns >= 2 (a 1-conn pool would self-deadlock: the lock holds the only conn while the guarded work blocks on Acquire; the reaper's per-tick deadline turns that into a failed tick, not a hang, but the lock is still wasted).

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL