Summary
DATABASE_DIRECT_URL is documented in CLAUDE.md and injected by our deployment (aca.yaml:38-39, from the database-direct-url secret), but no code reads it:
$ grep -rn "DATABASE_DIRECT_URL" src/ scripts/
(no matches)
runMigrations resolves its connection through loadTenantConfig() (src/api/server.ts), which reads only TENANT_DATABASES / DATABASE_URL (src/api/tenants.ts:32-41).
Impact
The deployment declares an intent — "run DDL off the pooler" — that the application silently ignores. Migrations connect through whatever DATABASE_URL points at. Combined with the session-scoped advisory lock (see the migrations issue), multi-replica boot has no working mutual exclusion, and nobody reading aca.yaml would know.
A secret is also being provisioned and mounted for no purpose.
Decision needed
We have settled on always connecting through the pooler (Azure Postgres built-in PgBouncer; no direct connection). Given that, the two options are not equal:
- Preferred — remove it. Delete the
CLAUDE.md row and the aca.yaml env + secret, and make migrations pooler-safe instead (single transaction + pg_advisory_xact_lock). This matches how we actually deploy and removes a misleading knob.
- Alternative — implement it. Have
runMigrations prefer DATABASE_DIRECT_URL when set. Only worth doing if we ever want a non-pooled path for DDL; it does not fix the advisory-lock bug on its own.
Either way the doc, the deployment, and the code should stop disagreeing.
Provenance
Found during pre-merge load testing of #162. Not introduced by that PR.
Summary
DATABASE_DIRECT_URLis documented inCLAUDE.mdand injected by our deployment (aca.yaml:38-39, from thedatabase-direct-urlsecret), but no code reads it:runMigrationsresolves its connection throughloadTenantConfig()(src/api/server.ts), which reads onlyTENANT_DATABASES/DATABASE_URL(src/api/tenants.ts:32-41).Impact
The deployment declares an intent — "run DDL off the pooler" — that the application silently ignores. Migrations connect through whatever
DATABASE_URLpoints at. Combined with the session-scoped advisory lock (see the migrations issue), multi-replica boot has no working mutual exclusion, and nobody readingaca.yamlwould know.A secret is also being provisioned and mounted for no purpose.
Decision needed
We have settled on always connecting through the pooler (Azure Postgres built-in PgBouncer; no direct connection). Given that, the two options are not equal:
CLAUDE.mdrow and theaca.yamlenv + secret, and make migrations pooler-safe instead (single transaction +pg_advisory_xact_lock). This matches how we actually deploy and removes a misleading knob.runMigrationspreferDATABASE_DIRECT_URLwhen set. Only worth doing if we ever want a non-pooled path for DDL; it does not fix the advisory-lock bug on its own.Either way the doc, the deployment, and the code should stop disagreeing.
Provenance
Found during pre-merge load testing of #162. Not introduced by that PR.