nstojiljkovic / akka-coordination-redis

Akka Coordination Lease Implementation using Redis

GitHub

akka-coordination-redis

Build Status Codacy Badge Maven Central

Akka Coordination Lease implementation using Redis and Redlock algorithm.

Compatibility

The library is currently compatible with Akka 2.5.22 and 2.5.23. It is tested with 2 use cases built-in into Akka:

Installation

Add akka-coordination-redis to sbt dependencies:

libraryDependencies ++= Seq(
  "com.github.nstojiljkovic" %% "akka-coordination-redis" % "0.3.1"
)

Configuration

Sample configuration using 5 Redis servers, with minimum amount of locks set to 4:

redisson-red-lock-lease {
  # reference to the lease implementation class
  lease-class = "com.nikolastojiljkovic.akka.coordination.lease.RedissonRedLockLease"

  # if the node that acquired the leases crashes, how long should the lease be held before another owner can get it
  heartbeat-timeout = 120s

  # interval for communicating with the third party to confirm the lease is still held
  heartbeat-interval = 12s

  # interval to time out after acquire and release calls or document
  lease-operation-timeout = 5s

  # minimum number of (single) locks required, defaults to N / 2 + 1 if not defined
  min-locks-amount = 4

  # custom dispatcher to use, defaults to actor system default dispatcher if not specified
  dispatcher = custom-dispatcher

  # common configuration option, not used directly by RedissonRedLockLease
  default-server {
    singleServerConfig {
      address = "redis://10.10.11.40:6379"
      connectionMinimumIdleSize = 8
      connectionPoolSize = 16
    }
    threads = 64
    nettyThreads = 64
    transportMode = NIO
    transportMode = NIO
  }

  # list of servers
  servers = [
    ${redisson-red-lock-lease.default-server} {singleServerConfig.address = "redis://10.10.11.40:6379"},
    ${redisson-red-lock-lease.default-server} {singleServerConfig.address = "redis://10.10.11.41:6379"},
    ${redisson-red-lock-lease.default-server} {singleServerConfig.address = "redis://10.10.11.42:6379"},
    ${redisson-red-lock-lease.default-server} {singleServerConfig.address = "redis://10.10.11.43:6379"},
    ${redisson-red-lock-lease.default-server} {singleServerConfig.address = "redis://10.10.11.44:6379"}
  ]
}

If min-locks-amount is not configured it will be calculated as N / 2 + 1 where N is the number of configured Redis servers. The default-server from the above sample is just used to copy the common configuration to each of the configured servers. Each of the servers in the servers array accepts any of the configuration options as per JSON/YAML based Redisson configuration. You should also probably take special care when configuring the Redisson thread and connection pools.

There's also an option to specify custom execution context used by RedissonRedLockLease (which is completely separate from the Redisson thread pool) using the dispatcher configuration option. It should point to the name of a custom Akka dispatcher, configured as per Akka official documentation.

The lease-class, heartbeat-timeout, heartbeat-interval and lease-operation-timeout configuration options all come from Akka Coordination Lease API.

Technical details

Redisson is used as a Redis client library. However, Redisson's RedLock implementation does not map 1:1 to Akka Lease API. Notable changes and tweaks are listed bellow:

  • RedissonRedLock is Redis based distributed reentrant Lock object for Java and implements java.util.concurrent.locks.Lock interface. It means that only lock owner thread can unlock it otherwise IllegalMonitorStateException would be thrown. On the other hand, Akka Coordination Lease API has configurable owner (without locking the leases to threads) so akka-coordination-redis implementation uses custom internal RedissonLockWithCustomOwnerAndFixedLeaseTime. It does not comply with the Java lock specification and is therefore marked private. That also means that you should, as per Akka Coordination specification, take special care when picking up a lease name that will be unique for your use case.

  • RedissonRedLock has bunch of functions which throw UnsupportedOperationException (and which would otherwise be useful for implementing Akka Coordination Lease). The akka-coordination-redis library has custom logic implemented in RedissonRedLockLease to overcome the missing functionality from Redisson RedLock implementation.

    • Akka allows to specify both heartbeat-timeout and heartbeat-interval. Redisson allows to specify lockWatchdogTimeout (which maps to Akka's heartbeat-timeout for locks with leastTime set to -1), but the heartbeat-interval is then locked to 1/3 of lockWatchdogTimeout (hardcoded in a private method in RedissonLock). In order to fully allow Akka's heartbeat-interval setting, RedissonRedLockLease does not use Redisson's watchdog, but instead renews the lease itself.

    • It is assumed that every server configured for each of the (single) locks constituting a distributed RedLock will be actually a single Redis server (though that is not a requirement). RedissonRedLockLease listens for disconnects from each of the configured servers and opts to mark lease as lost on any server disconnect. So if you have anything besides singleServerConfig as a single lock server (for example Redis cluster), note that acquired leases will be marked as lost if any of the cluster servers goes down.

Contributing

Please feel free to create a new pull request for each change or bug fix. Make sure that all of the changes are covered using tests. You can run the full suite of tests using sbt:

> testOnly com.nikolastojiljkovic.akka.coordination.lease.*

License

See the LICENSE file for details.