2010-11-18 8 views
1

Ich habe folgendes in meiner Anwendung Controller:Warum sind meine Threadvariablen in Rails intermittierend?

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 

und dann im Anschluss an die in einigen meiner Modelle:

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

Nun, dies funktioniert - einen Teil der Zeit. Ich zum Beispiel laden eine Index-Methode und erhalten eine Liste von Datensätzen mit dem Gültigkeitsbereich angewendet, sondern manchmal auch eine leere Liste als Thread.current [: account_id] kommt als Null, obwohl Abfragen in der Anfrage früher funktionieren mit dem gleichen Wert.

Die Frage ist, warum funktioniert das nicht, und gibt es eine bessere Möglichkeit, eine globale Variable für die aktuelle Anforderung festzulegen?

Antwort

5

Die lokalen Variablen Thread zu manipulieren ist eine wirklich schlechte Idee und wird zu nichts als Traurigkeit, Herzschmerz und Schmerz führen. Es gibt keine Garantie, dass verschiedene Teile der Anforderungsverarbeitung von demselben Thread behandelt werden, und deshalb gehen Ihre Variablen möglicherweise verloren.

Die Rails-Konvention erstellt Instanzvariablen im Kontext von ApplicationController. dies in einfachen Worten ist alles, was Sie wirklich tun:

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 

Alle @... Typ Variablen Sie erstellen, wird auf die Instanz des mit der aktuellen Anforderung zugeordnet ApplicationController angebracht werden. Es ist wichtig zu beachten, dass für jede Anfrage eine brandneue Instanz der entsprechenden Controller-Klasse ausgestellt wird.

Sie können beliebige Instanzvariablen erstellen, sofern sie nicht in Konflikt mit denen stehen, die von Rails selbst verwendet werden, aber im Allgemeinen tritt dies nicht sehr häufig auf und Konflikte treten normalerweise bei Methodennamen auf.

Instanzvariablen auf Klassenebene bleiben zwischen Anforderungen in Umgebungen bestehen, in denen das Flag "Cache-Klassen" aktiviert ist. In der Entwicklungsumgebung wird Ihre Controller-Klasse bei jeder Anforderung erneut geladen, um sicherzustellen, dass sie den aktuellen Status Ihrer Quelldateien widerspiegelt.

+0

"Es gibt keine Garantie, dass verschiedene Teile der Anfrageverarbeitung von demselben Thread behandelt werden". Das ist irgendwie alarmierend. Sicher führt eine Controller-Aktion innerhalb eines einzelnen Threads durch? –

+0

Was ich damit meine ist, dass Sie von einer Anfrage zur nächsten sicherlich verschiedene Prozesse und Threads treffen werden. Sie werden nicht sehen, dass der Thread innerhalb einer einzigen Methode geschaltet wird, es sei denn, Sie tun dies absichtlich. In zukünftigen Versionen von Rails ist dies jedoch möglicherweise nicht der Fall, da die Verarbeitung von Multi-Threaded-Antworten wahrscheinlich implementiert wird, sobald die Global Interpreter-Sperre aufgehoben wird. – tadman

+0

Kühl. So kann eine Aktion ausgesetzt und letztere wieder aufgenommen werden. Aber ein Call-Stack kann nicht magisch von einem Thread zum anderen springen. Wenn Sie Thread-Variablen am Anfang Ihrer Controller-Aktion speichern, steht fest, dass sie immer noch da sind, wenn Sie das Ende dieser Methode erreicht haben. Da Datenbankabfragen synchron sind, erwarten Sie, dass sich Ihr Modellcode im selben Thread wie der Controller befindet. Nein? –

Verwandte Themen