Replies: 12 comments 4 replies
-
What do you mean by "centralised processing"? How are you modelling the multi-tenancy in your database in the first place? One database per tenant? One schema per tenant? Via Row Level Security? |
Beta Was this translation helpful? Give feedback.
-
What I mean by centralized processing is not adding the tenant id filter condition to every statement. It can be processed uniformly, and our logic does not care about the existence of tenant. We are currently adding the tenent_id field to all databases in a schema, hoping that we can add the tenant_id condition to the common consent (instead of adding tenant_id to every statement).Can you explain separately how to implement schema per tenant and RLS through Postgres. |
Beta Was this translation helpful? Give feedback.
-
Please be more precise with the terminologies. It causes more confusion than explanation. There is no database in a schema. Schemas are in a database. Do you actually mean adding a tenant_id column for all tables in a schema ?
This is not the place for this kind of conversation. There are plenty of articles online for that, you should do your own research. This is not really a issue nor a question directly about the postgres.js rather a generic multi-tenacy data modelling question. |
Beta Was this translation helpful? Give feedback.
-
@Louis-Tian Hi,I would like to confirm whether postgres.js provides public methods that can be used to support the addition of all table public fields? |
Beta Was this translation helpful? Give feedback.
-
Hi,@Louis-Tian We use the shared tables to implement multi-tenancy. The RLS strategy I am using now can theoretically achieve this goal, but the difficulties encountered in practical development. I want to add RLS in batches to the existing concatenated sql, but using.begin() will be executed immediately. The concatenated SQL will lose the PendingQuery state, do you have any good suggestions? My code looks something like this //select function -- concatenated SQL
export const getRooms = async (
params: QueryFilter,
): Promise<RoomInfo[]> => {
const roomQuery =
params.type !== undefined
? sql`AND type = ${params.type}`
: sql``;
const result = await sql<Room[]>`
SELECT room_id, type
FROM room
WHERE room_id IN ${sql(params.roomIds)}
${roomQuery}
`;
return result;
}
export async function sql<T extends readonly object[]>(strings: any, ...args: any[]){
const tenantId = '1';
return sqlOriginal.begin(async (pg_sql) => {
pg_sql`select set_config('myapp.current_tenant', ${tenantId},false);`;
return pg_sql<T>(strings as TemplateStringsArray, ...args)
});
}
export const sqlOriginal = postgres({
username: getRequired('POSTGRES_USER'),
password: getRequired('POSTGRES_PASSWORD'),
database: getRequired('POSTGRES_DB'),
host: getRequired('POSTGRES_HOST'),
port: Number(getRequired('POSTGRES_PORT')),
ssl: getEnv().POSTGRES_ENABLE_UNAUTHORIZED ? false : true,
types: {
bigint: {
to: 20,
from: [20],
serialize: (v: number) => v.toString(),
parse: (v: string) => parseInt(v),
},
},
onnotice: () => {
// ignore notices
},
}); |
Beta Was this translation helpful? Give feedback.
-
No, there is no magic way of inserting a where condition for every query. I don't know why are you setting the current_tenant as GUC. Is it just for passing tenant id number to the getRoom query? How about using higher order functions like: import postgres from 'postgres'
const pg = postgres({
user: 'postgres',
pass: 'password',
});
function tenant(tenantId){
return (query) => sql`${query} and tenant = ${tenantId}`
}
function getRooms(roomIds) {
return sql`select * from room where room_id in ${roomIds}`;
}
await tenant(1)(getRooms(2)) |
Beta Was this translation helpful? Give feedback.
-
@xiujuan-li there is no exposed handler for this, but your way of doing it in a transaction is the most straight forward. If you ensure returning an array without await in the begin callback you can ensure Postgres.js pipelines the transaction. I've played around with CTE's to see if there was a way to avoid the transaction, but unfortunately nested CTEs don't allow data modifications. If they did you could do some clever wrapping there 😋 |
Beta Was this translation helpful? Give feedback.
-
Hi,I appreciate your advice, but I'm still a little confused.@Louis-Tian There are some SQL in our code using 'LEFT JOIN', 'INNER JOIN', some SQL does not set Where conditions, these need to be unified on the tenantId where conditions, similar to this there are many, not easy to enumerate, this kind of higher-order function how to deal with? The code looks like this: return await sql<Info[]>`
SELECT
a.file,
a.version,
h.type
FROM
a
INNER JOIN
(select * from b where b.name is not null) h
ON
h.version = a.version
WHERE
h.code = ${params.code}
`; We have about hundreds of tenants. Based on RLS policy, each tenant uses its own database link, which will require a lot of links to be used at the same time. If these tenant links are cached in Map, is this solution feasible? Or do you have any good suggestions? export const sqlOriginal = postgres({
...,
connection: {
'myapp.current_value': '1',
}
},
}); our RLS policy like this: CREATE POLICY product_policy ON userlist
USING (current_setting('myapp.current_value') = id ); |
Beta Was this translation helpful? Give feedback.
-
Hi,@porsager Can you tell us more about the way postgresjs pipes transactions? I'm very interested. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
it should be great if we can find a common way to wrap each SQL statement with a WHERE tenant_id = ${tenantId} clause, like the my-habits in java |
Beta Was this translation helpful? Give feedback.
-
@porsager would you help to share your suggestions? |
Beta Was this translation helpful? Give feedback.
-
Dear expert,
Our project has migrated from typeorm to postgres, and now we plan to support multi-tenant. The primary issue we face is to support tenant Id in all sql statements. We hope to have a common method for centralized processing, but unfortunately, we have not found a good method.
Do experts have any good suggestions? very appreciated for your response
Thanks
Beta Was this translation helpful? Give feedback.
All reactions