From 3687af90eee7033ac9c01742743b925af1e8a874 Mon Sep 17 00:00:00 2001 From: Andrew Coleman Date: Mon, 22 Dec 2014 15:38:46 -0600 Subject: [PATCH 1/4] Initial commit --- LICENSE | 22 ++++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 26 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..369ccac --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Andrew Coleman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..c63c694 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +uncomplicated_mutex +=================== + +A transactional mutex based in Redis for Ruby. From 2fa8c73710bbc12b911ecd52064bf03c284a9c7e Mon Sep 17 00:00:00 2001 From: Andrew Coleman Date: Mon, 22 Dec 2014 15:59:07 -0600 Subject: [PATCH 2/4] Update README.me --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c63c694..f7cbec9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,53 @@ -uncomplicated_mutex +Uncomplicated Mutex =================== -A transactional mutex based in Redis for Ruby. +A transactional mutex based in Redis for Ruby. It works across processes, threads, and machines provided you can all access the same Redis server. + +This is subtely different from two related projects here on Github: kenn/redis-mutex and dv/redis-semaphore + +RedisMutex uses a polling interval and raises an exception on lock timeout. RedisSemaphore uses `blpop` to wait until a value is pushed into a list. I have noticed several issues with the two approaches. I have had jobs continue to run for days with RedisSemaphore due to random and unknown failures. RedisMutex raises an exception, and in my situation, I do not need an exception, I would prefer to assume that the previous job has failed and that execution should continue. + +Major Features +============== + +* Uses your own Redis or Redis::Namespace instance +* Uses Redis `SET` using Lua transactions based on documentation from http://redis.io/commands/SET for a simple locking pattern. +* It can either _pessimistically_ or _optimistically_ fail. In other words, it will raise an exception if you want and ignore the timeout and continue to run otherwise. +* It uses a fixed number of ticks (or cycles) of wait that are calculated on initialization. +* A lock will not overwrite a value in Redis if the value was changed from the lock's "secret" token. +* It requires that objects to be locked respond to the method `id`. + +I take many feathers from the cap of Martin Fowler when I wrote this gem. Once initialized, variables contents never change. Methods are not longer than 10 lines. Method names are very specific yet not too long. Methods are alphabetized in the class definition (except the initializer). Tests are included. + +Usage +===== + +A number of options are available on initialization: + +|Option|Default value|Description| +|fail_on_timeout|`false`|Raise an exception if mutex lock is not acquired in the requested time| +|redis|`Redis.new` Redis connection to use| +|ticks|`100`|Number of times to wait during the timeout period| +|timeout|`300`|Time, in seconds, to wait until lock is considered stale| +|verbose|`false`|Prints debugging statements| + +``` +mutex = UncomplicatedMutex.new(my_obj, opts) + +mutex.lock do + my_obj.long_synchronized_process +end +``` + +This pattern works very well in Sidekiq or Resque. Also, if you need to access the name of the lock for your own value checking, you may ask the mutex `lock_name` to get the actual Redis key of the lock. + +Locking Algorithm +================= + +* Set value in Redis if it does not exist +* If it exists, wait up to 100 times until :timeout has been met +* Once waiting for lock has finished, either: +** Pessimistically throw an exception if not met +** Overwrite existing token with new value and assume ownership +* Run block of code +* Release lock if it contains the same value that it was set to From 5c644543d0ffac4d4aa63930fa2f0dbb183fc3dc Mon Sep 17 00:00:00 2001 From: Andrew Coleman Date: Mon, 22 Dec 2014 15:59:56 -0600 Subject: [PATCH 3/4] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f7cbec9..7f91371 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,7 @@ Locking Algorithm * Set value in Redis if it does not exist * If it exists, wait up to 100 times until :timeout has been met -* Once waiting for lock has finished, either: -** Pessimistically throw an exception if not met -** Overwrite existing token with new value and assume ownership +* Pessimistically throw an exception if lock was not released and configured to do so +* Overwrite existing token with new value and assume ownership if configured to do so * Run block of code * Release lock if it contains the same value that it was set to From 8530c24e8a1a5b83001ef1a851561bfd9d933880 Mon Sep 17 00:00:00 2001 From: Andrew Coleman Date: Mon, 22 Dec 2014 16:00:35 -0600 Subject: [PATCH 4/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f91371..3afe59f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Uncomplicated Mutex A transactional mutex based in Redis for Ruby. It works across processes, threads, and machines provided you can all access the same Redis server. -This is subtely different from two related projects here on Github: kenn/redis-mutex and dv/redis-semaphore +This is subtely different from two related projects here on Github: https://github.com/kenn/redis-mutex and https://github.com/dv/redis-semaphore RedisMutex uses a polling interval and raises an exception on lock timeout. RedisSemaphore uses `blpop` to wait until a value is pushed into a list. I have noticed several issues with the two approaches. I have had jobs continue to run for days with RedisSemaphore due to random and unknown failures. RedisMutex raises an exception, and in my situation, I do not need an exception, I would prefer to assume that the previous job has failed and that execution should continue.