2012-08-17 11 views
10

Tengo que escribir una aplicación de Rails con hilos porque la estoy ejecutando encima de Neo4j.rb, que incorpora una base de datos de gráficos Neo4j dentro del proceso de Rails, y por lo tanto tengo que atender varias solicitudes del mismo proceso. Sí, sería fantástico si conectarse a una base de datos Neo4j funcionaba como bases de datos SQL, pero no es así, así que dejaré de quejarme y simplemente lo usaré.Acciones de controlador de rieles seguros para rosca: ¿establecer variables de instancia?

Estoy bastante preocupado por las implicaciones de escribir código concurrente (como debería ser), y solo necesito algunos consejos sobre cómo manejar un escenario común: un controlador establece una variable de instancia o una variable en el hash de sesión , entonces algunas cosas pasan. Considere el siguiente código crudo para demostrar lo que quiero decir:

# 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 pregunta: ¿Hay alguna condición de carrera en este código?

En SessionsController, ¿el hash session y el params hash thread-local? Supongamos que la misma sesión del navegador realiza múltiples solicitudes/sesiones # create (para tomar prestada la sintaxis de la ruta de Rails) con credenciales diferentes, el usuario que está conectado debe ser la solicitud que golpeó la línea session[:current_user_id] = user.id al final? ¿O debería envolver un bloqueo mutex alrededor de la acción del controlador?

En CurrentUserController, si la acción mostrar se ve afectada simultáneamente por dos solicitudes con sesiones diferentes, ¿se establecerá la misma @current_user variable por ambos? Es decir. ¿La primera solicitud, cuando está procesando el archivo .html.erb, descubre que su variable de instancia @current_user ha sido cambiada de repente por el segundo hilo?

Gracias

Respuesta

10

Cada solicitud consigue un new instance of your controller. Como consecuencia, las variables de instancia del controlador son seguras para hilos. params y session también están respaldados por variables de instancia del controlador (o el objeto de solicitud en sí mismo) y por lo tanto también son seguros.

+0

Este es un hilo viejo, lo siento. Pero tengo un seguimiento. Eso tiene mucho sentido (y por qué la gente no se preocupa por las @variables en los métodos de control. Sin embargo, si eso es cierto, estoy confundido por cómo este tipo tenía problemas con su contador http://tenderlovemaking.com/2012/ 06/18/removing-config-threadsafe.html –

+1

@OliverShaw en ese contador de correos es una variable de instancia de clase (porque está en el bloque 'class << self'), por lo que todas las solicitudes estarían modificando el mismo contador –

+0

Coming volviendo a Ruby. Recordé las variables de clase, pero no las variables de instancia de clase. Gracias! –

2

Es importante saber qué se comparte entre los hilos y qué no.

Ahora volviendo a su ejemplo específico. Dos solicitudes golpean CurrentUserController#show simultáneamente, por lo tanto, se manejan mediante dos subprocesos concurrentes. La clave aquí es que cada subproceso tiene su propia instancia de CurrentUserController, por lo que hay dos variables @current_user que no interfieren. Entonces no hay una condición de carrera alrededor del @current_user.

Un ejemplo de condición de carrera sería la siguiente:

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 

En términos más generales, a causa de GIL (Global Interpreter Lock) beneficios del uso de hilos en la RM rubí son bastante limitadas. Hay implementaciones que están libres de GIL (jruby).

+0

Gracias por el ejemplo, sin embargo, ya sé que las variables de clase causarían una carrera. Sobre el problema de GIL, creo que estás equivocado - Ruby 1.9 hilos son nativos, y estoy usando JRuby de todos modos. –

+1

Me alegra que estés en el camino correcto. En cuanto a MRI ruby ​​1.9: tiene subprocesos nativos y GIL, que es mutex global utilizado por esos subprocesos nativos. –

+0

Ah, ya veo. Bueno, como dije, afortunadamente estoy usando JRuby :) –

Cuestiones relacionadas