I recently used pgx library and it suggested to use connection pool over creating connection on demand during concurrency.
The *pgx.Conn returned by pgx.Connect() represents a single connection and is not concurrency safe
Are below reasons adequate to support the aforementioned statement?
- Pool initiates connections and is ready to be consumed when requested
- Ensure DB is not loaded with infinite connections at a time.
- Connections are thread safe.
Points 1 & 2 make a perfect usecase for using pool and not create a single connection but 3(thread safety) can be built in a regular connection as well.
Are there any other currency reasons why we should be using pool instead of creating objects on the fly?
2
Answers
pgx.Connect()
are just the same – if you give the same one for multiple threads to share, they can intefere.Thread safety mentioned in the "Getting started" section likely refers to what happens if multiple operators share the same, single connection. You can’t build in reliable safeties against these, without effectively ending up re-implementing session isolation that’s already offered. Examples include:
temp
object. Operating in the same connection, worker B shares the samepg_temp
namespace, so any attempt to perform a similar operation causes unintended interaction with A’s temp object on the db.Any session-level thing becomes a potential friction point between workers. That being said, you can come up with workflows that use none of the thread-unsafe facilities of a session and find the random latencies introduced by 4 above, acceptable. It’d be still safer and more sustainable to use regular, thread-safe setup for those.
Thread safety in this context is guaranteed by letting each worker use their own session/connection. You can make each worker always begin by setting up their own instead of requesting a fresh, off-the-shelf, stock connection, but that’s just slower – the pool does it independently ahead of time, saving everyone time establishing, configuring and later disposing of them.
dbpool.QueryRow()
passes your query to a clean connection fresh from the pool, runs it and immediately releases the connection back to it. Two concurrent threads can share a pool and their queries are guaranteed to end up in different connections, and not interfere. You can’t use it for anything relying on a transaction or something discardable. For that you need todbpool.Acquire()
, hold on to the acquired connection, re-use it to run the entire query chain on it, thenconn.Release()
back to the pool.The main reason is due to how this library implements
Conn
: sourceBecause the struct members and logic of
Conn
are inherently scoped to a single request, a pool is the higher-order abstraction to keep these objects fresh, reset, and reused without overlap. Otherwise things likewbuf
orpreparedStatements
would be written in expected ways.