1from os import environ
2
3from plain.runtime.secret import Secret
4
5# DATABASE_URL is the widely-used convention for Postgres connection strings;
6# honor it as a fallback so most hosting setups work unmodified.
7POSTGRES_URL: Secret[str]
8if _env_url := environ.get("DATABASE_URL"):
9 POSTGRES_URL = _env_url
10
11# Optional second URL for management operations (migrations, convergence,
12# schema changes). When set, management commands connect via this URL
13# instead of POSTGRES_URL. Common uses: bypassing a transaction-mode
14# pgbouncer that can't handle DDL, or connecting as a DDL-capable role.
15# Falls back to POSTGRES_URL when empty.
16POSTGRES_MANAGEMENT_URL: Secret[str] = ""
17
18# Connection pool options forwarded to psycopg_pool.ConnectionPool.
19# Defaults match psycopg_pool's own defaults (max_size mirrors min_size when
20# left alone).
21POSTGRES_POOL_MIN_SIZE: int = 4
22POSTGRES_POOL_MAX_SIZE: int = 20
23POSTGRES_POOL_MAX_LIFETIME: float = 3600.0
24POSTGRES_POOL_TIMEOUT: float = 30.0
25
26# DDL timeouts. Applied per-statement via SET LOCAL before every framework-
27# issued DDL in migrations and convergence. Values are Postgres interval
28# strings ("3s", "500ms", "1min"). These do NOT affect application queries.
29#
30# lock_timeout: how long to wait for a lock before failing with
31# LockNotAvailable. Prevents the lock-queue cascade where a waiting
32# ACCESS EXCLUSIVE blocks every new query on the table.
33#
34# statement_timeout: how long a statement can run once it has its lock.
35# Only applied to statements that hold ACCESS EXCLUSIVE. Non-blocking
36# operations (CREATE INDEX CONCURRENTLY, VALIDATE CONSTRAINT) run
37# without a statement_timeout — the lock doesn't cascade, so letting
38# them run to completion on large tables is safe.
39POSTGRES_MIGRATION_LOCK_TIMEOUT: str = "3s"
40POSTGRES_MIGRATION_STATEMENT_TIMEOUT: str = "3s"
41POSTGRES_CONVERGENCE_LOCK_TIMEOUT: str = "3s"
42POSTGRES_CONVERGENCE_STATEMENT_TIMEOUT: str = "3s"