README.md in redis-mutex-3.0.0 vs README.md in redis-mutex-4.0.0

- old
+ new

@@ -11,19 +11,19 @@ -------- In the following example, only one thread / process / server can enter the locked block at one time. ```ruby -Redis::Mutex.with_lock(:your_lock_name) do +RedisMutex.with_lock(:your_lock_name) do # do something exclusively end ``` or ```ruby -mutex = Redis::Mutex.new(:your_lock_name) +mutex = RedisMutex.new(:your_lock_name) if mutex.lock # do something exclusively mutex.unlock else puts "failed to acquire lock!" @@ -38,17 +38,25 @@ Or if you want to immediately receive `false` on an unsuccessful locking attempt, you can change the mutex mode to **non-blocking**. Changelog --------- +### v4.0 + +`redis-mutex` 4.0 has brought a few backward incompatible changes to follow the major upgrade of the underlying `redis-classy` gem. + +* The base class `Redis::Mutex` is now `RedisMutex`. +* `Redis::Classy.db = Redis.new` is now `RedisClassy.redis = Redis.new`. + ### v3.0 * Ruby 2.0 or later is required. +* `auto_mutex` now takes `:on` for additional key scoping. ### v2.0 -* **Exception-based control flow**: Added `lock!` and `unlock!`, which raises an exception when fails to acquire a lock. Raises `Redis::Mutex::LockError` and `Redis::Mutex::UnlockError` respectively. +* **Exception-based control flow**: Added `lock!` and `unlock!`, which raises an exception when fails to acquire a lock. Raises `RedisMutex::LockError` and `RedisMutex::UnlockError` respectively. * **INCOMPATIBLE CHANGE**: `#lock` no longer accepts a block. Use `#with_lock` instead, which uses `lock!` internally and returns the value of block. * `unlock` returns boolean values for success / failure, for consistency with `lock`. Install ------- @@ -65,41 +73,41 @@ ``` Register the Redis server: (e.g. in `config/initializers/redis_mutex.rb` for Rails) ```ruby -Redis::Classy.db = Redis.new(:host => 'localhost') +RedisClassy.redis = Redis.new ``` Note that Redis Mutex uses the `redis-classy` gem internally to organize keys in an isolated namespace. There are a number of methods: ```ruby -mutex = Redis::Mutex.new(key, options) # Configure a mutex lock +mutex = RedisMutex.new(key, options) # Configure a mutex lock mutex.lock # Try to acquire the lock, returns false when failed mutex.lock! # Try to acquire the lock, raises exception when failed mutex.unlock # Try to release the lock, returns false when failed mutex.unlock! # Try to release the lock, raises exception when failed mutex.locked? # Find out if resource already locked mutex.with_lock # Try to acquire the lock, execute the block, then return the value of the block. # Raises exception when failed to acquire the lock. -Redis::Mutex.sweep # Remove all expired locks -Redis::Mutex.with_lock(key, options) # Shortcut to new + with_lock +RedisMutex.sweep # Remove all expired locks +RedisMutex.with_lock(key, options) # Shortcut to new + with_lock ``` The key argument can be symbol, string, or any Ruby objects that respond to `id` method, where the key is automatically set as -`TheClass:id`. For any given key, `Redis::Mutex:` prefix will be automatically prepended. For instance, if you pass a `Room` -object with id of `123`, the actual key in Redis will be `Redis::Mutex:Room:123`. The automatic prefixing and instance binding -is the feature of `Redis::Classy` - for more internal details, refer to [Redis Classy](https://github.com/kenn/redis-classy). +`TheClass:id`. For any given key, `RedisMutex:` prefix will be automatically prepended. For instance, if you pass a `Room` +object with id of `123`, the actual key in Redis will be `RedisMutex:Room:123`. The automatic prefixing and instance binding +is the feature of `RedisClassy` - for more internal details, refer to [Redis Classy](https://github.com/kenn/redis-classy). The initialize method takes several options. ```ruby :block => 1 # Specify in seconds how long you want to wait for the lock to be released. - # Speficy 0 if you need non-blocking sematics and return false immediately. (default: 1) + # Specify 0 if you need non-blocking sematics and return false immediately. (default: 1) :sleep => 0.1 # Specify in seconds how long the polling interval should be when :block is given. # It is NOT recommended to go below 0.01. (default: 0.1) :expire => 10 # Specify in seconds when the lock should be considered stale when something went wrong # with the one who held the lock and failed to unlock. (default: 10) ``` @@ -112,26 +120,26 @@ ```ruby class RoomController < ApplicationController before_filter { @room = Room.find(params[:id]) } def enter - Redis::Mutex.with_lock(@room) do # key => "Room:123" + RedisMutex.with_lock(@room) do # key => "Room:123" # do something exclusively end render text: 'success!' - rescue Redis::Mutex::LockError + rescue RedisMutex::LockError render text: 'failed to acquire lock!' end end ``` Note that you need to explicitly call the `unlock` method when you don't use `with_lock` and its block syntax. Also it is recommended to put the `unlock` method in the `ensure` clause. ```ruby def enter - mutex = Redis::Mutex.new('non-blocking', block: 0, expire: 10.minutes) + mutex = RedisMutex.new('non-blocking', block: 0, expire: 10.minutes) if mutex.lock begin # do something exclusively ensure mutex.unlock @@ -151,14 +159,28 @@ If you give a proc object to the `after_failure` option, it will get called after locking attempt failed. ```ruby class JobController < ApplicationController - include Redis::Mutex::Macro + include RedisMutex::Macro auto_mutex :run, block: 0, after_failure: lambda { render text: 'failed to acquire lock!' } def run # do something exclusively render text: 'success!' + end +end +``` + +Also you can specify method arguments with the `on` option. The following creates a mutex key named `ItunesVerifier#perform:123456`, so that the same method can run in parallel as long as the `transaction_id` is different. + +```ruby +class ItunesVerifier + include Sidekiq::Worker + include RedisMutex::Macro + auto_mutex :perform, on: [:transaction_id] + + def perform(transaction_id) + ... end end ```