-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Equivalent of Pool.query for Client #1938
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Something like this should be on the top level import, not on the Client. Maybe something like const pg = require('pg');
const { withClient } = pg;
await withClient(opts, async (client) => {
await client.query('....');
await client.query('....');
await client.query('....');
return 'some result';
}) Then the single query version could use that generic one to run one query and return it's result: const runOneQuery = (opts, sql, params) => {
return pg.withClient(opts, (client) => client.query(sql, params))
} |
Why shouldn't it be on Client? Client would let you set all the connection parameters, then anywhere you need to call it you just run something like |
Ah, you're referring to having it on a I read the original comment to imply it was a static function on the class, Rather than adding top level functions, how about adding a new top level sibling to Pool that does not do any pooling but maintains a similar API. Could be a drop-in replacement for user's that do not want any app level pooling. Something like this (NOTE: not tested!): class ZeroPool {
constructor(opts) {
// Save connection settings
this.opts = opts;
}
connect = async () => {
const client = new Client(this.opts);
await client.connect();
// Mimic pool's release() but actually end connection:
client.release = () => {
return client.end();
}
return client;
}
query = async (sql, params) => {
// Outside the try block as we don't need to end a failed connect
const client = await this.connect();
try {
// Must await result so that finally block executes after this
const result = await client.query(sql, params);
// If we get here then query was successful
// ... otherwise the query error have been thrown
return result;
} finally {
try {
await client.end();
} catch (ignore) {
}
}
}
end = () => {
// no-op
}
} |
That looks great! Could it be added to an upcoming version? I'm assuming |
@rightaway Its |
@charmander If you needed to use |
@rightaway It's mean to be a drop-in replacement for environments where you do not want to using app level pooling. If you have existing code that uses |
Will |
@sehrope Could this feature be added? Will be very useful for people who are using pgbouncer and have to write their own, potentially buggy implementations of managing transactions. |
@rightaway I guess it's common enough in Lambda style settings that it'd be useful. @charmander What do you think? Should I open a PR for it? |
Well, I’d make a new package – but it’s worth asking @brianc! |
When I import |
@charmander When you make a single Or does it batch them #1190 (comment) to avoid round trips that aren't needed? |
Neither. There's no need to send |
yep. And that's how I'd prefer it to be. It's too custom to your use case to be something we roll into the core of export async function query(connectionArgs, queryText, queryParams) {
await client.connect(connectionArgs)
try {
return client.query(queryText, queryParams)
}
finally {
client.end()
}
} This is p small & you should really create a layer between your application and the database anyway and not sprinkle direct calls to postgres everywhere as it makes it harder to add error handling, logging, other custom decorator stuff. You can add something like this in that layer. Ultimately I think this would be better served in a separate module as it solves a particular use case & niche and could have tests written against pg-bouncer for that module's CI and better documentation and explanation and all that. |
@charmander So if I do Are |
Yes.
They're run in two separate transactions, yes. (Or I'm pretty sure, at least. You can double-check with |
@charmander Actually I tried it with Pool and plain Client and all statements passed in 1 string are run in the same transaction. Will be good if it can be added to documentation to prevent confusion. Do client.connect() and client.end() below make round trips to the database? Can I remove client.connect() from here and do it just once on application startup so that I don't need to make that round trip on every query? Just like how Pool.query doesn't need that extra round trip on every query because Pool.connect is only run once on application startup. export async function query(connectionArgs, queryText, queryParams) {
await client.connect(connectionArgs)
try {
return client.query(queryText, queryParams)
}
finally {
client.end()
}
} |
Yes.
Yes. You can't connect the same |
@charmander If I stick with Pool instead of Client is there a way to use LISTEN/NOTIFY? How can you create one listener when the application starts and keep it running the whole time the application is running? The docs only show how to do it with Client. Can you give an example of how it should look if you're using Pool? |
@rightaway Ah, no, if you're using |
People who are using external connection pooling like
pgbouncer
use theClient
api rather thanPool
. ButClient
has no equivalent to the convenientPool.query
. Documentation saysInstead we have to do
Client.connect
thenClient.query
thenClient.end
. There's a risk of leaking a client withClient
that doesn't exist when usingPool.query
. If an equivalent were provided it would be much better than everyone writing their own helper function that does this.Since the name
Client.query
is already taken, it could have a different name likeClient.single
.The text was updated successfully, but these errors were encountered: