Ecto Repo prepare option and PgBouncer transaction pooling: which setting belongs in production config
0 reputation · 22 Jan 2020, 07:19 UTC
0 reputation · 22 Jan 2020, 07:19 UTC
An Ecto application connecting to PostgreSQL works in local development but is being deployed behind PgBouncer in transaction pooling mode (as is common with managed Postgres such as RDS Proxy, Supabase, or Cloud SQL). The relevant documented feature is the Postgrex prepare option on the Repo, which defaults to :named.
Named prepared statements are bound to a database session. Under transaction pooling, a client connection can be reassigned to a different server session between transactions, so a named statement prepared earlier may no longer exist on the session handling the next query. The documented alternative, prepare: :unnamed (available since Ecto 3.5 / Postgrex 0.15), avoids this but gives up server-side plan caching across transactions, which may add planning overhead for repeated complex queries. Assume Ecto 3.10+ and Postgrex 0.16+; behavior should be verified against the versions actually in use.
There is also uncertainty about scope: the setting is per Repo, and ecto.migrate runs on its own connection that honors the same option, so migrations in a transaction-pooled staging environment may behave differently from runtime queries.
Should prepare: :unnamed be set unconditionally for any deployment behind transaction pooling, or only after measuring plan-cache impact? Does the migration connection need separate treatment? Is there a supported way to detect pooling mode at boot rather than relying on manual config?
35775 reputation · 22 Jan 2020, 11:21 UTC
Set prepare: :unnamed in your Repo configuration for any production deployment that uses PgBouncer (or similar) in transaction‑pooling mode.
prepare: :unnamed tells Postgrex to use the unnamed statement portal, which lives only for the duration of a single query and is not pooled across sessions, eliminating the error.:namedIf you have measured that the loss of server‑side plan caching does not noticeably increase latency for your workload, you may keep the default :named. Without that measurement, the safest choice is :unnamed.
config/prod.exs or config/config.exs).config :my_app, MyApp.Repo,
username: System.get_env("DB_USERNAME"),
password: System.get_env("DB_PASSWORD"),
hostname: System.get_env("DB_HOSTNAME"),
database: System.get_env("DB_DATABASE"),
prepare: :unnamed # <-- add this line
# config/prod.exs
prepare: {:system, "ECTO_PREPARE", :unnamed}
:unnamed automatically. No separate configuration is needed.Ecto/Postgrex does not provide a built‑in way to query whether the connection is behind a transaction‑pooler. The setting must be supplied explicitly via config or an environment variable.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.