2012-07-02 9 views
7

Estoy encapsulando un complicado conjunto de comandos Redis en una transacción MULTI, pero la lógica en la transacción depende de los valores que ya están en Redis. Sin embargo, todas las lecturas dentro de una transacción parecen volver nil¿Cómo puedo leer desde Redis dentro de un bloque MULTI en Ruby?

Aquí hay un ejemplo que muestra el problema:

[Dev]> $redis.set("foo", "bar") 
=> "OK" 
[Dev]> $redis.multi{ $redis.set("foo", "baz") if $redis.get("foo") == "bar" } 
=> ["bar"] 
[Dev]> $redis.get("foo") 
=> "bar" 

Obviamente quiero el último valor de retorno sea 'baz' - ¿cómo puedo lograr esto?

+0

Me temo que no puedes hacer eso. –

Respuesta

1

Como dice Sergio en su comentario, no puede ejecutar opcionalmente un bloque MULTI como ese en Redis. Consulte documentation on transactions:

Se procesan todos o ninguno de los comandos.

Puede, sin embargo, utilizar WATCH para implementar el bloqueo optimista de entrada automático y conjunto (pseudo código):

SET foo bar 
WATCH foo 
$foo = GET foo 
MULTI 
if $foo == 'bar' 
    SET foo baz 
EXEC 
GET foo 

Usando WATCH, sólo se ha realizado la operación si la clave observado (s) no ha sido cambiado. Si se cambia la llave del reloj, el EXEC fallará, y puede intentar nuevamente.

Otra posibilidad es utilizar la funcionalidad de secuencias de comandos, pero eso solo está disponible en la versión 2.6 del candidato.

16

No se puede, ya que todos los comandos (incluido get) se ejecutan realmente en tiempo de ejecución. En esta situación, el comando get solo devuelve un objeto futuro, no el valor real.

Hay dos formas de implementar dicha transacción.

El uso de una cláusula RELOJ

La cláusula de reloj se utiliza para proteger contra actualizaciones concurrentes. Si el valor de la variable se actualiza entre el reloj y la cláusula múltiple, entonces los comandos en el bloque múltiple no se aplican. Depende del cliente intentar la transacción en otro momento.

loop do 
    $redis.watch "foo" 
    val = $redis.get("foo") 
    if val == "bar" then 
     res = $redis.multi do |r| 
      r.set("foo", "baz") 
     end 
     break if res 
    else 
     $redis.unwatch "foo" 
     break 
    end 
end 

Aquí el guión es un poco compleja debido a que el contenido del bloque puede estar vacía, así que no hay manera fácil de saber si la operación ha sido cancelada, o si no tuvo lugar en absoluto. En general, es más fácil cuando el bloque múltiple devuelve resultados en todos los casos, excepto si la transacción se cancela.

Usando Lua script del lado del servidor

Con Redis 2.6 o mejor, los scripts Lua se puede ejecutar en el servidor. La ejecución de todo el guión es atómica. Se puede implementar fácilmente en Ruby:

cmd = <<EOF 
    if redis.call('get',KEYS[1]) == ARGV[1] then 
     redis.call('set',KEYS[1],ARGV[2]) 
    end 
EOF 
$redis.eval cmd, 1, "foo", "bar", "baz" 

Esto es típicamente mucho más simple que el uso de las cláusulas de reloj.

Cuestiones relacionadas