brianc / node-pg-pool

A connection pool for node-postgres

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Race condition in pool.on('connect',...) ?

claytongulick opened this issue · comments

A (fairly) common question I see around is how to set options like search_path with node-pg. The answer I see most commonly given is to use something like

pool.on('connect', (client) => {client.query('set search_path...');});

Glancing through the code though, it doesn't look like doing this will actually guarantee that the search_path will be set before a client issues a query, given that it's an async call.

Are we running the risk that queries can be executed prior to setup? This could be a potential security issue as well if using RLS and something like 'SET my.user_id = 123' - a query could potentially be executed prior to the SET command completing?

Event listeners are called synchronously by emit, and the pool emits connect on a client before the client is used, so if the listener makes a query immediately (like here) there’s no race condition compared to queries after the client has been acquired. It’s not an elegant solution, however, and the query queue that allows this to work is a bit of a pain – which is why I’d prefer to expose brianc/node-postgres#1393 (comment).

@charmander I glanced through the source, and given that the db call is async, I didn't see any guarantee that a call like I have in my example would complete prior to a client issuing a query. Did I miss something?

@claytongulick A client can only run one query at a time, so pg keeps a queue of queries for each client. If the query is first to enter the queue, it will be the first to run.

Thanks for clarifying that @charmander -- I came here with the same concern as @claytongulick and found this issue, but what you're saying makes sense.

it's true that you can guarantee the query to be executed first, but, only if the connect callback is synchronous, if your callback uses await/promises before issuing the query, the order won't be guaranteed

I'm currently looking for a workaround for this

also, I think sometimes (eg, for SET my.user_id = 123) I think the correct event to listen to is acquire because the pool could potentially return an already connected client (despite the function being called connect). to be confirmed, I'm not 100% certain

well there is the undocumented options.verify that could be used for on connect, but it won't work for acquired: options.verify is only called for new clients and not when they are reused.

so I resorted to monkey patching the connect method for now

@mathroc What is it that you need to do every time a connection is acquired?

it's a websocket application, I call select set_config('session.id', $1::text, false) with the session id corresponding to socket I'm working with (it's used by row level security rules)