How can I lower Ecto repository connection costs for a low‑traffic workload?
0 reputation · 07 Jul 2020, 05:55 UTC
0 reputation · 07 Jul 2020, 05:55 UTC
For a low‑traffic application I want to keep the Ecto repository’s resource usage minimal while still answering requests promptly. The repository creates connection pools that can consume memory and database slots even when idle, and features such as query caching or detailed logging add overhead that may be unnecessary for infrequent workloads. I am considering adjusting pooling, caching, logging, and timeout settings to reduce cost, but I am unsure how these options interact under light load and whether lowering them could affect latency or reliability.
What is the impact of setting pool_size to 1 and pool_count to 1 on connection usage? Does disabling :query_cache save memory without hurting performance for rare queries? Is it safe to turn off :log to cut logging overhead in production?
35275 reputation · 07 Jul 2020, 09:44 UTC
Yes, you can meaningfully reduce Ecto repository connection costs for a low-traffic workload by tuning pool_size, disabling the query cache, and turning off SQL logging. However, each setting has trade-offs that depend on your expected concurrency and observability needs. The key is to match the pool to your actual request rate while keeping enough headroom for occasional bursts.
Setting pool_size to 1 limits the repository to a single database connection. For a truly low-traffic app where requests are serialized or infrequent, this is safe and minimizes idle connections. The main risk is that concurrent requests will queue on the single connection, increasing latency. If your workload occasionally spikes to 5–10 simultaneous requests, a pool of 1 will cause noticeable waits. A pool of 2–3 often strikes a better balance.
pool_count is typically left at 1—it only matters when you configure multiple pools (e.g., for read/write replicas). Lowering it below 1 is not valid, and keeping it at 1 has no cost impact.
Disabling :query_cache saves memory because Ecto no longer stores compiled query plans. For rare or one-off queries, the performance impact is negligible—the cache only helps when the same query runs repeatedly. If your workload is sparse and queries are diverse, disabling it is a reasonable memory saving. If you have a few hot queries that run often, you might see a small CPU increase from recompiling them, but it's rarely significant at low traffic.
Setting log: false eliminates per-query logging overhead, which includes formatting, I/O, and potential log volume. In production, this is safe as long as you have alternative observability (e.g., metrics, tracing). You lose the ability to see slow queries in logs, so consider enabling a slower-query threshold instead of full logging if you still want some visibility.
Beyond the settings you mentioned, consider queue_target and queue_interval. These control how long a request waits for a connection before timing out. For low traffic, a shorter queue_target (e.g., 50ms) can fail fast under unexpected bursts rather than piling up. Also, set a reasonable timeout for queries to reclaim stuck connections.
After adjusting settings, monitor the database server's active connection count (e.g., pg_stat_activity for PostgreSQL) and watch for DBConnection.ConnectionError or timeout errors under simulated load. The optimal pool size depends on your expected concurrency—if you can share that, I can give a more specific recommendation.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 07 Jul 2020, 16:59 UTC
While reducing pool_size to 1 minimizes idle resource usage, it introduces a risk of DBConnection.ConnectionError during rare concurrent bursts. To mitigate this without increasing the pool size, it is important to tune the pool_timeout setting.
By default, Ecto waits for a connection to become available for a specific duration. In a single-connection setup, if a long-running query occupies the pool, subsequent requests will queue. If the queue wait exceeds the pool_timeout, the request fails. For low-traffic apps, slightly increasing this timeout can prevent transient errors during overlapping requests, though it may increase perceived latency for the queued user.
To verify the actual impact of your pool settings, you can monitor the database's active session count. For PostgreSQL, use:
SELECT count(*) FROM pg_stat_activity;
This confirms whether the repository is maintaining the expected number of idle connections versus the configured pool_size.