2012-08-17 15 views
10

Devo scrivere un'app Rails filettata perché sto eseguendo su Neo4j.rb, che incorpora un database grafico Neo4j all'interno del processo Rails, e quindi devo servire più richieste dello stesso processo. Sì, sarebbe bello se la connessione a un database Neo4j funzionasse come i database SQL, ma non è così, quindi smetterò di lamentarmi e basta usarlo.Azioni controller thread-safe Rails - impostazione delle variabili di istanza?

Sono abbastanza preoccupato per le implicazioni della scrittura di codice concorrente (come dovrei essere) e ho solo bisogno di alcuni consigli su come gestire uno scenario comune comune: un controller imposta una variabile di istanza o una variabile nell'hash della sessione , poi succede qualcosa. Considera il seguente codice grezzo per dimostrare cosa intendo:

# THIS IS NOT REAL PRODUCTION CODE 
# I don't do this in real life, it is just to help me ask my question, I 
# know about one-way hashing, etc.! 

class SessionsController 
    def create 
    user = User.find_by_email_and_password(params[:email], params[:password]) 
    raise 'auth error' unless user 
    session[:current_user_id] = user.id 
    redirect_to :controller => 'current_user', :action => 'show' 
    end 
end 

class CurrentUserController 
    def show 
    @current_user = User.find(session[:current_user_id]) 
    render :action => :show # .html.erb file that uses @current_user 
    end 
end 

La domanda: ci sono condizioni di gara in questo codice?

In SessionsController, l'hash session e l'hash thread locale sono params? Supponiamo che la stessa sessione del browser faccia richieste multiple a/sessions # creare (per prendere in prestito la sintassi della rotta di Rails) con credenziali diverse, l'utente che ha effettuato l'accesso dovrebbe essere la richiesta che ha colpito la riga session[:current_user_id] = user.id per ultimo? O dovrei avvolgere un blocco mutex attorno all'azione del controller?

In CurrentUserController, se l'azione dello show viene raggiunta contemporaneamente da due richieste con sessioni diverse, la stessa variabile @current_user verrà impostata da entrambi? Cioè la prima richiesta, poiché sta elaborando il file .html.erb, rileva che è @current_user che la variabile d'istanza è stata improvvisamente cambiata dal secondo thread?

Grazie

risposta

10

Ogni richiesta ottiene un new instance of your controller. Di conseguenza le variabili di istanza del controllore sono thread-safe. params e session sono anch'essi supportati da variabili di istanza del controllore (o dall'oggetto di richiesta stesso) e quindi sono anche sicuri.

+0

Questa è una vecchia discussione, mi dispiace. Ma ho un seguito. Questo ha molto senso (e perché la gente non si preoccupi delle variabili @ nei metodi del controller, ma se è vero, sono confuso da come questo ragazzo abbia avuto problemi con il suo contatore http://tenderlovemaking.com/2012/ 06/18/remove-config-threadsafe.html –

+1

@OliverShaw in quel contatore di post è una variabile di istanza di classe (perché si trova nel blocco 'class << self'), quindi tutte le richieste modificheranno lo stesso contatore –

+0

Tornando a Ruby, mi sono ricordato delle variabili di classe, ma non delle variabili di istanza di classe Grazie! –

2

È importante sapere cosa è condiviso tra thread e cosa no.

Ora torna al tuo esempio specifico. Due richieste hanno colpito contemporaneamente lo CurrentUserController#show, quindi sono gestite da due thread simultanei. La chiave qui è che ogni thread ha la sua istanza di CurrentUserController, quindi ci sono due variabili @current_user che non interferiscono. Quindi non ci sono condizioni di gara intorno a @current_user.

Un esempio di condizione di competizione sarebbe questo:

class ApplicationController < ActionController::Base 
    before_each :set_current_user 
    cattr_accessor :current_user 

    def set_current_user 
    self.class.current_user = User.find_by_id(session[:current_user_id]) 
    end 
end 

# model 
class LogMessage < ActiveRecord::Base 
    belongs_to :user 

    def self.log_action(attrs) 
    log_message = new(attrs) 
    log_message.user = ApplicationController.current_user 
    log_message.save 
    end 
end 

Su Più in generale, a causa della GIL (Global Interpreter Lock) beneficia di utilizzare fili in risonanza magnetica rubino sono piuttosto limitati. Esistono implementazioni libere da GIL (jruby).

+0

Grazie per l'esempio, tuttavia so già che le variabili di classe causerebbero una gara. Sul problema GIL, penso che tu abbia torto - Ruby 1.9 thread sono nativi e sto usando JRuby in ogni caso –

+1

Sono contento che tu sia sulla strada giusta. Per quanto riguarda la risonanza magnetica 1.9: ha i thread nativi e GIL, che è il mutex globale usato da quei thread nativi. –

+0

Ah, capisco. Bene, come ho detto, per fortuna sto usando JRuby :) –

Problemi correlati