Em 08-01-2013 14:13, Jeremy Evans escreveu:
On Tuesday, January 8, 2013 6:18:41 AM UTC-8, Rodrigo Rosenfeld Rosas wrote:

    Hi, I'd like to get some advice on how to properly handle
    connections in my application.

    We're deploying a multi-tenant(-like) strategy using PG schemas.
    Basically we do that by setting the search_path properly on a
    before filter. Our application is composed by a Grails one and a
    Rails one (running Sequel) and I use the same approach in both.

    But "set search_path to xxx" will only take effect for that
    particular connection. For Grails this is a non-issue as long as I
    don't request a new Hibernate session (which I don't) since it
    will keep the same connection for all queries to the database.

    For the Rails app this is currently not an issue since I'm
    deploying it using Unicorn (multi-process approach). But I'd like
    to be able to change it to a threaded-based approach (like Puma or
    JRuby) if I want to and currently I can't because I wouldn't have
    any guarantees that the connection with the proper search_path set
    in the before filter will be the same next time I query the
    database from a controller for instance.

    So, I'd like to ask for any advices on how I could ensure I'll
    always use the same connection throughout the entire request cycle
    (using a pool of connections, of course)... Any ideas?

    Thanks in advance!


If you must change the search_path, you should use a rack middleware with Database#synchronize to ensure that the same connection is used for the entire request, setting the search path inside the Database#synchronize block, before calling the next middleware. Note that this limits concurrency, since you can then only serve as many concurrent requests as you have database listeners. That's the price you have to pay for transparent connection handling in a schema-per-tenant system.

The alternative is changing your app code so that tables are explicitly qualified instead of using the search path. This can probably be implemented by storing the schema in a thread local variable and making the model use it if available:

class Foo < Sequel::Model(DB.from(Sequel.delay{Thread.current[:pg_schema] ? Sequel.qualify(Thread.current[:pg_schema], :foos) : :foos}))
  end

With this approach, you'll probably have to make similar changes elsewhere in your codebase. This approach is better as it doesn't hurt concurrency, but it probably requires more changes to your app. If it works for you, it's the approach I would recommend.

Thanks for the prompt reply, Jeremy!

We've decided to support multiple templates recently and we thought that the fastest approach would be to just set up the search_path in a before filter to get it working. Then we currently only allow a single template usage at a time and we set up the current template (schema) in the users table current_template column. This didn't require much changes to the application and we could deploy it and get it running.

Of course a better approach is to just be explicit about the schema all the time but that requires more time to change and review the entire applications (Rails and Grails ones). We can do that later but for the time being if we can get it working with less changes it would be less error prone...

Your lazy tip (Sequel.delay) is quite interesting :) I wasn't aware of it, thanks for sharing. But there are also a few other places where I generate the SQL myself and I'd have to update it too among some other parts where some joins are used... So it surely is a good change but something that requires more time before getting it into production.

The Rack middleware trick is a good one, but there is a problem for my situation where I extract the schema from the current user table and the current user is retrieved by Devise (Warden) in a later middleware. I'd probably have to add the middleware right next Warden, somehow...

Grails implements this as a lazy operation in the sense that it won't checkout the connection from the pool until it is requested at some point of the request. For our case it doesn't help much since we get the schema from the database anyway, but maybe we may change that in the future to make the schema a request param.

I'm not too much worried about this currently since almost all (if not all) our requests involve database queries. But I understand that the possibility of returning the connection to the pool as soon as possible would be great when we're performing some Solr request for instance... Maybe we can review our entire app at some point to make the schema always explicit and avoid this shortcoming. On the other hand, it would become harder to ensure we didn't forget to set the correct schema in all queries all the time...

But thank you for your insight, I think middlewares are the way to go, I just need to figure out how to get the middleware to run after Warden's one and see if it works with Devise's authentication system.

--
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sequel-talk?hl=en.

Reply via email to