2012-08-17 9 views
10

Muszę napisać gwintowaną aplikację Rails, ponieważ uruchamiam ją na szczycie Neo4j.rb, która osadza bazę danych wykresu Neo4j wewnątrz procesu Railsowego, i dlatego muszę obsługiwać wiele żądań z tego samego procesu. Tak, byłoby fajnie, gdyby połączenie z bazą danych Neo4j działało jak bazy danych SQL, ale tak nie jest, więc przestanę narzekać i po prostu z niego korzystać.Działania kontrolerów wątków bezpieczne Railsy - ustawianie zmiennych instancji?

Jestem dość zaniepokojony konsekwencjami pisania współbieżnego kodu (tak jak powinienem być) i potrzebuję tylko porady, jak radzić sobie z typowym scenariuszem - kontroler ustawia zmienną instancji lub zmienną w haszy sesji , a potem coś się dzieje. Rozważ poniższy surowy kod, aby pokazać, co mam na myśli:

# 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 

Pytanie: Czy w tym kodzie są jakieś warunki wyścigu?

W SessionsController, czy mieszanie session i mieszanie params wątku są lokalne? Powiedzmy, że ta sama sesja przeglądarki wysyła wiele żądań do/sessions # create (aby wypożyczyć składnię trasy Railsów) z różnymi referencjami, zalogowany użytkownik powinien być ostatnim zapytaniem, które trafiło na linię session[:current_user_id] = user.id? Czy powinienem owinąć blokadę mutex wokół akcji kontrolera?

W kontrolerze CurrentUserController, jeśli akcja pokazu jest trafiona jednocześnie przez dwa żądania z różnymi sesjami, czy ta sama zmienna @current_user będzie ustawiona przez obie? To znaczy. czy pierwsze żądanie, przetwarzając plik .html.erb, wykryje, że zmienna instancji @ current_user została nagle zmieniona przez drugi wątek?

Dzięki

Odpowiedz

10

Każde żądanie dostaje new instance of your controller. W konsekwencji zmienne instancji kontrolera są bezpieczne dla wątków. params i session są również wspierane przez zmienne instancji kontrolera (lub sam obiekt żądania), dzięki czemu są również bezpieczne.

+0

To jest stara nitka, przepraszam. Ale mam śledzenie. To ma sens (i dlaczego ludzie nie martwią się o zmienne @ w metodach kontrolera, ale jeśli to prawda, jestem zdezorientowany tym, jak ten facet miał problemy z kontem http://tenderlovemaking.com/2012/ 06/18/removing-config-threadsafe.html –

+1

@OliverShaw w tym liczniku postów jest zmienną instancji klasy (ponieważ znajduje się w bloku 'class << self'), więc wszystkie żądania będą modyfikować ten sam licznik. –

+0

powrót do Ruby. Zapamiętałem zmienne klasy, ale nie zmienne instancji klasy.Dzięki! –

2

Ważne jest, aby wiedzieć, co jest dzielone między wątkami, a co nie.

Teraz wrócimy do konkretnego przykładu. Dwie prośby trafiają jednocześnie CurrentUserController#show, dlatego są obsługiwane przez dwa równoczesne wątki. Kluczem jest tutaj to, że każdy wątek ma swoje własne wystąpienie CurrentUserController, więc istnieją dwie zmienne @current_user, które nie zakłócają. Więc nie ma warunków wyścigowych wokół @current_user.

Przykładem wyścigu byłoby to:

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 

Na bardziej ogólnym notatki, z powodu GIL (Global Interpreter Lock) korzyści z korzystania wątki w MRI Ruby są dość ograniczone. Istnieją implementacje wolne od GIL (jruby).

+0

Dzięki za przykład, jednak już wiem, że zmienne klasy spowodowałyby wyścig. W kwestii GIL, myślę, że jesteś w błędzie - Ruby 1.9 wątków jest rodzimych, i używam JRuby w każdym razie –

+1

Cieszę się, że jesteś na właściwej ścieżce.Jak dla MRI ruby ​​1.9: ma natywne wątki i GIL, który jest globalnym muteksem używanym przez te natywne wątki. –

+0

Ah, rozumiem. Cóż, jak już powiedziałem, na szczęście używam JRuby :) –

Powiązane problemy