package store import ( "context" "database/sql" "fmt" "time" // The pure-Go PostgreSQL driver. Pure-Go is not a preference here: the // whole binary is built with CGO_ENABLED=0 so that it runs on a node whose // libc is older than the builder's, and a cgo driver would take that away // from every deployment, including the ones still on SQLite. _ "github.com/jackc/pgx/v5/stdlib" ) // OpenPostgres opens the store against PostgreSQL. // // The same queries run here as on SQLite; only the placeholders differ, and // the dialect handles that. What differs operationally is concurrency: SQLite // is held to one connection because more would only produce lock contention // that reads as a deadlock, while PostgreSQL is the reason someone moves to // it and wants a pool. // // The DSN carries a password, so it is taken as a string the caller read from // somewhere protected rather than as a path this package knows how to find — // and it is never logged, which is why errors below name the database and not // the DSN. func OpenPostgres(ctx context.Context, dsn string) (*DB, error) { if dsn == "" { return nil, fmt.Errorf("store: no PostgreSQL address configured") } db, err := sql.Open("pgx", dsn) if err != nil { // Deliberately not wrapped with the DSN: it holds a password, and an // error string ends up in logs, tickets and screenshots. return nil, fmt.Errorf("opening the PostgreSQL store: %w", err) } // Sized for a control plane rather than for a fleet of application // servers. The request path barely touches the store — usage is batched, // budgets are counted in memory, authentication is cached — so the pool // exists for conversation turns and reporting queries, not for inference. db.SetMaxOpenConns(16) db.SetMaxIdleConns(4) // Long enough that a quiet cluster keeps its connections, short enough // that a restarted database is not answered with dead ones for an hour. db.SetConnMaxLifetime(30 * time.Minute) db.SetConnMaxIdleTime(5 * time.Minute) if err := db.PingContext(ctx); err != nil { _ = db.Close() return nil, fmt.Errorf("reaching the PostgreSQL store: %w", err) } if err := migrate(ctx, db, string(PostgresDialect)); err != nil { _ = db.Close() return nil, err } s := &DB{ raw: db, dialect: PostgresDialect, db: bound{c: PostgresDialect, to: db}, } // Not fatal: a store without pgvector serves everything but knowledge // bases, and says why when somebody asks for one. s.kbErr = s.ensureKnowledge(ctx) return s, nil }