I’m working on a nodejs server using Knex.js to connect to a postgres DB.
On some of the tables I’ve set some RLS policies based on the current session values (app.user_id and app.tenant_id) to isolate data correctly.
I would like to be able to pass the userId
and tenantId
from my model/service methods to a db util method that grabs a pool connection, sets the session variables and returns the connection to be used (all in Knex ideally).
Currently I have the following:
const buildSetSessionQuery = ({userId, tenantId}) => {
let queryString = "";
if(userId) {
queryString += format(`SET SESSION app.user_id=%L;`, userId);
}
if(tenantId) {
queryString += format(`SET SESSION app.tenant_id=%L;`, tenantId);
}
return queryString;
};
exports.getUserConnection = ({userId, tenantId}={}) => {
const conn = knex({
client: 'pg',
connection: getConfig(), // Assume this returns the correct connection config
pool: {
min: 0,
max: 40,
afterCreate: function (conn, done) {
const setSessionQuery = buildSetSessionQuery({userId, tenantId});
conn.query(setSessionQuery);
done (null, conn);
}
}
});
return conn;
};
But this code creates a whole Knex pool for each query that’s executed (as opposed to grabbing an available pool connection from a single pool), which quickly exhausts the max_connections limit on my pg server.
Is there a way to make it so getUserConnection()
returns a knex pool connection with the session variables set, so that I can use it easily like so:
exports.getData = async ({tenantId, userId}) => {
const conn = db.getUserConnection({ tenantId, userId });
const rows = await conn
.select('*')
.from('data')
.orderBy([{
column: 'timestamp',
order: 'desc',
nulls: 'last'
}]);
return rows;
};