简体   繁体   中英

Rails + Postgres: Multi-tenancy done right?

I am going to build an app using Rails. It uses multi-tenancy using the Apartment gem and PostgreSQL. The app will have users, each of which have an account. This implies that each user has it's own PostgresSQL schema; the users table is in the default schema.

Each user has his own list of customers in his own schema. A customer with the same email (essentially the same customer) can appear in multiple schemas. I want a customer to be able to log in and see all the users he's associated with. I can't put the customers table in the default/public schema because it's related to other tables that are not in the default schema.

What I thought I would do is create a link table between customers and users in the public schema. This table would contain the email of the customer and the id of the user. My issue with that is that I don't understand how well this would work with Rails. What I would like to achieve is something like customer.users .

So the question is: How should I approach this problem?

I suggest to differ between your users (who log in, not part of a tenant), and the customers (which are kept separately, and located in each tenant). The users table (possibly accompanied by other tables) can hold the information for the assignment from user to schema/customer etc. I would not even use foreign keys to link the user table with the tables in the tenant, just to keep them really separate.

In short, the user table serves to authenticate and to authorize only.

Update : The question describes a multi-tenancy approach using separate database schemas for the individual tenants. In this setup up, I would not link the users with the customers by database foreign keys , and I would not query them together. Just authenticate against the users, and get the assigned tenant(s). After that switch to the tenant.

If you really want to query both items (users and customers) in one run, I would not use separate schemas: One schema, create a tenant table, and put a foreign key into all other tables (customers etc.). In this scenario you could even do without a separate user table, and you could query the (single) customer table.

Update 2 : To answer your query question:

You can query for schemas in PostgreSql's meta data:

select schemaname from pg_tables where tablename = 'Customer'

Which gives you all schemas with a customer table. Using that information you can dynamically build a union select :

select name from schema1.customer
union all
select name from schema2.customer
union all

[...repeat for all schemas...]

to query all tables across schemas. You could use group by to eliminate duplicates.

I created this lib to help us solve this issue

https://github.com/Dandush03/pg_rls

Currently the most famous implementation are Apartment Gem (from Influitive), ActiveRecordMultiTenant (from Citus) and the rails 6.1 way, DB sharding

there are many issues with the Apartment and rails 6.1 approach when dealing with a huge amount of schemas/databases mainly when you most run a scale migration or when you have to change default values on a table, this is because you would need to run this migration on each tenant, and that very cost-efficient. and Citus's approach gets expensive in the long run.

thankfully PostgreSQL came with a great solution on pg V-9 but it had some performance issue that was solved on pg V-10. This approach allows you to keep specific tables behind a 'special id' which can be later on partitioned with pg new tools

my approach is mainly focused on PG and how they recommend you to implement RLS, and most of my queries are executed with SQL statements which help us a bit when dealing with performance issues when running the migration. I tried to mix the best of rails with the best of pg functions.

what is even better on my approach is that when you start running your special pg search function, there will be no downfall because all data is secure and under the same DB. also you will gain the ability to log as a superuser and get your statistics.

I can keep going but I think I make my point clear, if you'd like I encourage you to check out the gem (prob still some bugs to handle like right now it only handle setting the Tenant from subdomain) but it does make my life easier on my ongoing projects. and if I get some more supports (like) I would keep on maintaining it and upgrading it to be a more generic tool

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM