2012-07-02 13 views
7

MULTIトランザクションで複雑なRedisコマンドをカプセル化していますが、トランザクションのロジックはRedisの値に依存しています。しかし、すべてがnilRubyのMULTIブロック内でRedisからどのように読み込むことができますか?

を返すように見えるここで、問題を示す例だトランザクション内の読み込み:

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

私は、最後の戻り値が'baz'になりたい - どのように私はこれを達成することができますか?

+0

私はあなたがそれをすることができないのですか? –

答えて

1

セルジオのコメントによれば、オプションで、RedisのブロックのようにMULTIブロックを実行することはできません。 documentation on transactions

すべてのコマンドまたはいずれも処理されません。あなたは、しかし、チェック・アンド・セットを使用して楽観的ロック(擬似コード)を実装するためにWATCHを使用することができます

WATCHを使用して
SET foo bar 
WATCH foo 
$foo = GET foo 
MULTI 
if $foo == 'bar' 
    SET foo baz 
EXEC 
GET foo 

を見キー(sの場合は、トランザクションにのみ実行されます)は変更されていません。ウォッチキーが変更された場合、EXECは失敗し、もう一度やり直すことができます。

もう1つの可能性はスクリプティング機能を使用することですが、それは2.6リリース候補でのみ利用可能です。

16

すべてのコマンド(getを含む)は実際に実行時に実行されるため、実行できません。この場合、getコマンドは実際の値ではなく、将来のオブジェクトのみを返します。

このようなトランザクションを実装するには2通りの方法があります。

時計句が同時更新から保護するために使用されているWATCH句

を使用します。変数の値がwatchとmulti句の間で更新された場合、マルチブロック内のコマンドは適用されません。別の時間にトランザクションを試行するのはクライアントの責任です。

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 

ここでブロックの内容を空にすることができますので、スクリプトは少し複雑なので、トランザクションはそれがすべてで行われていなかったかどうかをキャンセル、またはされているかどうかを知るための簡単な方法はありません。一般に、マルチブロックがトランザクションがキャンセルされた場合を除いて、すべてのケースで結果を返す方が簡単です。 Redisの2.6またはより良いではLuaのサーバーサイドスクリプト

を使用して

、Luaのスクリプトはサーバー上で実行することができます。スクリプト全体の実行はアトミックです。それは簡単に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" 

これは、通常WATCH句を使用するよりもはるかに簡単です。

関連する問題