-
Notifications
You must be signed in to change notification settings - Fork 861
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
Multi-Tenant Databases Using pgx #288
Comments
@fluffybonkers we implemented something similar in one of our apps and that exact article was really helpful during the design phase. Basically we acquire a connection from a connection pool and set the tenant before returning a reference to our PGClient for the application to work with. We have a simple method like the following:
Where
|
@fluffybonkers Anything done with What you really want is to check out a single connection for the duration of the HTTP request and set tenant ID at the start as @ARolek suggests above. But it's a little trickier in your case because you are using the
|
Thank you both for your helpful advice. @jackc I don't really want to stop using SQLBoiler and I can't really take option 2 because I don't think I can give up transaction management. Thus, option 3 seems like the only one, though I am strongly considering switching to something like PostgraphQL (rather than a pgx, I feel a bit like I am in unchartered territory with Go, both because of my lack of experience with the language and because features in the language (like manual check out from a pool) are only coming now in a beta. Decisions, decisions... |
I have been using PostgraphQL and it is awesome for CRUD, however, I still want to use more traditional REST-based micro services for certain operations my application will need. To this end, I am eventually going to circle back to the problems I was having with multi-tenancy in Go. I have been looking at source code in both SQLBoiler and pgx and I think I have a slightly better grip on the problems, but I am hoping the wise ones here can shed some more light.
|
|
@jackc Can we get AfterAcquire functionality into the pool? Or some other solution, where a "dynamic" query could be run immediately after transaction start or after the connection is acquired from the pool? The solution purposed by @ARolek is a bit annoying and also the docs state that if you manually acquire you also have to release. |
What would |
Something that would get a connection in so one could run arbitrary queries. I don't know yet if is is feasible but IMO in the best way forward would probably be to copy the pool functionality into your own code and adapt it properly. The use case is really almost the same as above. Run a query for each transaction implicit or explicit setting some variable depending on the current request. |
Hm. It seems that's not possible to have a copy of Connection pooler in package that's under application control. The problem is that the original pooler uses unxeported variables.
|
This is what I've come up with, however this doesn't work because pgx pooler uses unexported fields. edit moved code to gist |
Can we make connPool on Rows Exported field or at least SetConnPool function so custom poolers are possible? And A NewBatch function for batches with |
I am very much in favor of decoupling ConnPool from the rest of the system such that 3rd party poolers could be used. However, when I tried to go that direction a year or two ago I ran into some issues. ConnPoll directly manipulates some of the internals of Conn, Rows, and Tx. I suppose the simplest way is to just export all the required fields, but I'm wary of leaking all that internal state to the public interace (and thereby making it more difficult to make non breaking changes in the future). I think the real solution is a significant refactor such that ConnPool doesn't need to be inside the pgx package. If that was done then that would ensure that a custom connection pool could do anything the built in one can ... but would be a substantial amount of work. |
In case someone is interested, we achieved multi-tenancy in The magic happens in https://gist.github.com/glerchundi/315be9ae9e4b72c467f4ef39d57ef004 The drawback is that per each query we do an extra roundtrip to the database just to @fluffybonkers we're using the |
Getting the same behaviour with |
@glerchundi I'm interested in achieving this with pgx only. |
The original functionality requested is now possible with |
@jackc that is awesome, do you have an end to end example of this? In particular, what cleanup, if any is necessary? |
I do not have an example of this type usage. But presumably in the In the |
Sorry to bump this issue but I dug into this a bit more and it appears to only be supported by |
I don't think it is possible. To my knowledge the |
@jackc awesome job! This new pgxpool works indeed! I have something like this to work:
This works to get each tenant's products by default. No need to use transaction anymore. Transaction works too, but it needs to set up transaction every time a query runs. There seems to be one bug though #1070. Overall, I think this solution works better than https://tip.golang.org/pkg/database/sql/#DB.Conn too. Both single transaction or single connection in a http request life cycle (DB.Conn) like what https://aws.amazon.com/blogs/database/multi-tenant-data-isolation-with-postgresql-row-level-security/ suggests is making the connection life time much longer than what it really needs. For example,
If Step c or Step e takes pretty long time like 1 second, then this request is holding one db connection useless for 1 second, which is pretty bad ~ |
Really nice writeup @smiletrl! |
I know this is quite old, but I've tried to get this working but for some reason, the BeforeAcquire and AfterRelease run immediately after each other, and then my query runs? Any ideas?
I use go-chi - not that it matters too much... I really can't figure this out. EDIT |
Hello all,
I hope this is not a question with an obvious answer, but I am a Go beginner interested in using Go and pgx to design a multi-tenant application with a Go REST API.
This obviously means I need a way of segregating tenant's data - all my tables will have a
tenant_id
. Now let's say that I include atenant_id
in each user's JWT being passed to the REST API. Thistenant_id
gets passed down the stack until I get to the methods accessing the Postgres database.However, I don't want to append
WHERE tenant_id = ?
for every query, as this is error prone and makes for longer queries. To ensure that data are seamlessly dealt out to the correct tenant, I want to use Postgres's Row Level Security with a session context variable as outlined here. This means I need a way to set the user on every database connection. I want to use thestdlib
to get back adatabase/sql
connection to use with my ORM (the excellent SQLBoiler).To do this, all I could think of was using pgx's
AfterConnect
hook on a connection pool and passing thetenant_id
contained in the JWT into it to set the session variable on the connection for that user. However, the parameters ofAfterConnect
won't allow that and I want to try and avoid usingAfterConnect
accessing some global variable to set it (with methods further up the chain setting the variable) as then all the logic is not in the method. The idea would be that thetenant_id
would have to be passed to theAfterConnect
method with every call toOpenFromConnPool
to ensure that programmers can't miss setting the Postgres session variable. Is it worth adding an empty interface to theAfterConnect
method for these sorts of use cases?I am sorry if this is a stupid question and I am more than open to other ideas that people have about solving this problem, as I am quite stuck.
The text was updated successfully, but these errors were encountered: