1

I have the following in my application controller:

before_filter :set_current_subdomain

  protected
    def set_current_subdomain
      Thread.current[:current_subdomain] = current_subdomain
      @account = Account.find_by_subdomain(current_subdomain)
    end

    def current_subdomain
      request.subdomain
    end

and then the following in some of my models:

default_scope :conditions => { :account_id => (Thread.current[:account].id unless Thread.current[:account].nil?) }  

Now, this works - some of the time. I for instance load up an index method and get back a list of records with the scope applied, but also sometimes get an empty list as Thread.current[:account_id] is coming out as nil, even though queries earlier in the request are working using the same value.

Question is, why is this not working, and is there a better way to set a variable that's global to the current request?

Neil Middleton
  • 22,105
  • 18
  • 80
  • 134

1 Answers1

6

Manipulating the Thread local variables is a really bad idea and is going to lead to nothing but sadness, heartache, and pain. There's no guarantee that different parts of the request processing will be handled by the same thread, and because of this, your variables might end up getting lost.

The Rails convention is to create instance variables in the context of ApplicationController. In simple terms, all you really do is this:

class ApplicationController < ActionController::Base
  before_filter :set_current_subdomain

  attr_reader :current_subdomain
  helper_method :current_subdomain

protected
  def set_current_subdomain
    @current_subdomain = request.subdomain

    @account = Account.find_by_subdomain(@current_subdomain)
  end
end

Any @... type variables you create will be attached to the instance of the ApplicationController associated with the current request. It's important to note that each request will be issued a brand-new instance of the appropriate controller class.

You're free to create whatever instance variables you want provided they don't somehow conflict with those used by Rails itself but in general terms this doesn't happen very often and conflicts typically occur on method names instead.

Class-level instance variables will persist between requests in environments where the "cache classes" flag is enabled. In the development environment your controller class is re-loaded each time a request is made to ensure it reflects the current state of your source files.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • "There's no guarantee that different parts of the request processing will be handled by the same thread". That's kind of alarming. Surely a controller action executes within a single thread through? – Lachlan Cotter Aug 12 '11 at 12:55
  • What I mean by that is that from one request to the next you will most certainly hit different processes and threads. You won't see the thread being switched within a single method unless you do that deliberately. In future versions of Rails, though, this may not be the case as multi-threaded response handling will probably be implemented as soon as the Global Interpreter Lock is eliminated. – tadman Aug 12 '11 at 14:51
  • Cool. So an action may be suspended and resumed latter. But a call stack can't magically jump from one thread to another. If you store thread variables at the beginning of your controller action, stands to reason they'll still be there when you reach the end of that method. Database queries are synchronous, so you would expect your model code to be on the same thread as the controller, no? – Lachlan Cotter Aug 13 '11 at 12:28
  • 1
    So—actually I'm just wanting to confirm that thread variables are reliable for the request in which they were set? – Lachlan Cotter Aug 13 '11 at 12:40
  • 1
    Don't use Thread variables unless you have a very, very good reason. It's extremely bad form. Future versions of Rails may make use of multiple threads, so you should never assume anything you don't have to. – tadman Aug 14 '11 at 23:15