etaty / rediscala

Non-blocking, Reactive Redis driver for Scala (with Sentinel support)
Apache License 2.0
790 stars 142 forks source link

Transaction is not working as expected #82

Closed dawnbreaks closed 9 years ago

dawnbreaks commented 9 years ago

Th following Transaction will never commit, promises deadlock! That 's absolutely not acceptable.

object ExampleTransaction extends App {
  implicit val akkaSystem = akka.actor.ActorSystem()
  val redis = RedisClient("192.168.0.8", 6379)

  var keyX = "x"
  var keyY = "y"
  var keySum = "sum"
  //init values of x and y
  var setX = redis.set(keyX, 101)
  var setY = redis.set(keyY, 103)

  for {
     setXStatus <- setX
     setYStatus <- setY
  } yield {
    assert(setXStatus)
    assert(setYStatus)

    val redisTransaction = redis.transaction()
    redisTransaction.watch(keyX)
    redisTransaction.watch(keyY)
    var keyXFuture = redisTransaction.get(keyX);
    var keyYFuture = redisTransaction.get(keyY);
    var r = for {
        x <- keyXFuture
        y <- keyYFuture
    } yield {
        var sum = ParseNumber.parseInt(x.getOrElse(ByteString("-1"))) + ParseNumber.parseInt(y.getOrElse(ByteString("-1")))
        println("set(sum, x + y)|(x+y)=" + sum)
        redisTransaction.set(keySum, sum)
        redisTransaction.exec()
    }
     Await.result(r, 10 seconds)
  }

  Thread.sleep(1000*10)
  akkaSystem.shutdown()
}

Th following Transaction should not commit since watched keys has been changed. But it commit normally. That 's absolutely not acceptable.

object ExampleTransaction extends App {
  implicit val akkaSystem = akka.actor.ActorSystem()
  val redis = RedisClient("192.168.0.8", 6379)
  val anotherRedis = RedisClient("192.168.0.8", 6379)

  var keyX = "x"
  var keyY = "y"
  var keySum = "sum"
  //init value of x and y
  var setX = redis.set(keyX, 101)
  var setY = redis.set(keyY, 103)

  for {
     setXStatus <- setX
     setYStatus <- setY
  } yield {
    assert(setXStatus)
    assert(setYStatus)

    val redisTransaction = redis.transaction()
    redisTransaction.watch(keyX)
    redisTransaction.watch(keyY)
    var keyXFuture = redis.get(keyX);
    var keyYFuture = redis.get(keyY);

    //x and y modified by anotherRedis client
    anotherRedis.set(keyX, -101)
    anotherRedis.set(keyY, -103)
    Thread.sleep(1000)

    var r = for {
        x <- keyXFuture
        y <- keyYFuture
    } yield {
        var sum = ParseNumber.parseInt(x.getOrElse(ByteString("-1"))) + ParseNumber.parseInt(y.getOrElse(ByteString("-1")))
        println("set(sum, x + y)|(x+y)=" + sum)
        redisTransaction.set(keySum, sum)
        redisTransaction.exec()
    }

     Await.result(r, 10 seconds)
  }

  Thread.sleep(1000*10)
  akkaSystem.shutdown()
}

Hope that Transaction should be fully supported. I think Transaction should be implemented just like implemention of the blocking commands on the Lists.

Thanks & Best Regards!

etaty commented 9 years ago

I kind of cheated the implementation of transaction. It's more like a batch of commands. The rediscala watch command is kind of useless in 99% of case (because all the transaction commands are sent in one batch). The best way would be to add a blocking transaction, however the transaction is "deprecated" for redis server, so I did not care much. Lua script is the way to go.

dawnbreaks commented 9 years ago

Ok, i got it. Thanks very much!