Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/platform/database/database.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 9bde0f9a74b89d01980fa658e5253acda7528d0c |
| Branch | per-tenant-routing |
| Path or archive member | internal/platform/database/database.go |
| Original SHA-256 | dece5a62296781581154503fec646b18044d5d3eb2f88f652ed59475065ace39 |
| Git blob SHA | 3aaacc0391744d7e3deeac36b4334244e70ebe5a |
| Reading-copy SHA-256 | dece5a62296781581154503fec646b18044d5d3eb2f88f652ed59475065ace39 |
Every recorded occurrence
These 3 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/platform/database/database.go | per-tenant-routing | 9bde0f9a74b89d01980fa658e5253acda7528d0c | Open original |
| Janady13/missionmatch / internal/platform/database/database.go | deploy-and-ops | bdacd9d0b4d5998d25fc74bf4007f4c3ec538c7a | Open original |
| Janady13/missionmatch / internal/platform/database/database.go | per-tenant-routing | 9bde0f9a74b89d01980fa658e5253acda7528d0c | Open original |
Recovered source transcript
Download complete source text ↓115 lines · 5.1 KB
// Package database constructs the application's Postgres connection pool.
//
// Two things are wired here that the rest of the app relies on (see docs/STACK_DECISIONS.md §2/§3/§5):
//
// 1. pgvector codecs are registered on EVERY physical connection via AfterConnect, so vector
// columns bind and scan as pgvector.Vector. Registering once on a single conn is a bug under
// pool churn, it must be AfterConnect.
// 2. The pool is expected to authenticate as the NOBYPASSRLS application role (never the migrator),
// so Postgres Row-Level Security is always in force. Which role is used is a property of the
// DSN, not of this code.
package database
import (
"context"
"fmt"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
pgxvec "github.com/pgvector/pgvector-go/pgx"
)
// NewPool parses dsn, registers the pgvector codecs per connection, and returns a ready pool.
// The caller owns the returned pool's Close(). dsn MUST point at the NOBYPASSRLS application role.
func NewPool(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
return newPool(ctx, dsn, nil)
}
// NewBoundedPool is NewPool with an explicit small connection ceiling, for a per tenant pool in the
// database per tenant model: there are many such pools, so each must stay small (MaxPools times MaxConns
// must stay well under the server max_connections, the pgx pool per tenant guidance). It also sets
// MinConns to zero (a quiet tenant reclaims its connections) and a nonzero MaxConnLifetimeJitter so many
// same age tenant pools do not expire and reconnect in lockstep and storm the shared server.
func NewBoundedPool(ctx context.Context, dsn string, maxConns int32) (*pgxpool.Pool, error) {
return newPool(ctx, dsn, func(cfg *pgxpool.Config) {
if maxConns > 0 {
cfg.MaxConns = maxConns
}
cfg.MinConns = 0
if cfg.MaxConnLifetime > 0 && cfg.MaxConnLifetimeJitter == 0 {
cfg.MaxConnLifetimeJitter = cfg.MaxConnLifetime / 10
} else if cfg.MaxConnLifetimeJitter == 0 {
cfg.MaxConnLifetimeJitter = 5 * time.Minute
}
})
}
// newPool is the shared builder: parse, register the pgvector codecs on EVERY physical connection, apply
// an optional tuning hook, and open. AfterConnect is the only correct place to register the vector codecs
// (registering once on a single conn is a bug under pool churn).
func newPool(ctx context.Context, dsn string, tune func(*pgxpool.Config)) (*pgxpool.Pool, error) {
cfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
return nil, fmt.Errorf("parse pool config: %w", err)
}
cfg.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
if err := pgxvec.RegisterTypes(ctx, conn); err != nil {
return fmt.Errorf("register pgvector types: %w", err)
}
return nil
}
if tune != nil {
tune(cfg)
}
pool, err := pgxpool.NewWithConfig(ctx, cfg)
if err != nil {
return nil, fmt.Errorf("create pool: %w", err)
}
return pool, nil
}
// AssertRole fails closed unless the pool authenticates as wantUser with rolbypassrls=false. It is
// the runtime guardrail behind the whole RLS and EEO isolation story: if an operator pastes a
// BYPASSRLS or superuser DSN (for example the migrator DSN) into the app or compliance variable, RLS
// would be silently bypassed and every tenant's rows, including protected demographic attributes,
// would leak cross org. This converts that misconfiguration into a hard boot failure.
func AssertRole(ctx context.Context, pool *pgxpool.Pool, wantUser string) error {
var user string
var bypass bool
err := pool.QueryRow(ctx,
`SELECT current_user, rolbypassrls FROM pg_roles WHERE rolname = current_user`).Scan(&user, &bypass)
if err != nil {
return fmt.Errorf("role assertion query: %w", err)
}
if user != wantUser {
return fmt.Errorf("pool authenticates as %q, expected %q", user, wantUser)
}
if bypass {
return fmt.Errorf("pool role %q has BYPASSRLS, which would bypass row level security", user)
}
return nil
}
// AssertMigratorRole is the mirror of AssertRole for the privileged batch jobs (the sync and import
// commands). Those jobs need the migrator role for one of two reasons: import-candidates writes a
// tenant scoped row with an explicit organization_id and no app.current_org_id, so a NOBYPASSRLS role
// would be rejected by the RLS WITH CHECK; the catalog syncs (SAM, awards, wages) write GLOBAL
// reference tables that the application role is granted read only, so a NOBYPASSRLS role would be
// denied INSERT. Both failures are caught here, at boot, by requiring a BYPASSRLS role. If an operator
// pastes the application DSN into a job's MM_MIGRATOR_DATABASE_URL this becomes a hard, legible boot
// failure instead of an RLS rejection or a permission denied error partway through the run.
func AssertMigratorRole(ctx context.Context, pool *pgxpool.Pool) error {
var user string
var bypass bool
err := pool.QueryRow(ctx,
`SELECT current_user, rolbypassrls FROM pg_roles WHERE rolname = current_user`).Scan(&user, &bypass)
if err != nil {
return fmt.Errorf("role assertion query: %w", err)
}
if !bypass {
return fmt.Errorf("pool role %q lacks BYPASSRLS; this job needs the migrator DSN (MM_MIGRATOR_DATABASE_URL)", user)
}
return nil
}