0.0
No commit activity in last 3 years
No release in over 3 years
redis locker gem allows to lock model methods to prevent concurent execution
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

Runtime

 Project Readme

RedisLocker

RedisLocker is a gem which provides locking system with redis backend.

Installation

Add this line to your application's Gemfile:

gem 'redis_locker'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install redis_locker

Usage

Main idea of RedisLocker is to provide simple interface for blocking models and its methods.

Configuration

RedisLocker needs redis obviously so you have to pass redis connection with configure block

RedisLocker.configure do |config|
  config.redis_connection = Redis.new
end

above snippet has to be called before you start using redis lock so eg. if you're using Ruby on Rails you can place it in application.rb

Model

A model in RedisLocker is any class that implements id method. To make model RedisLockable you have to include RedisLocker in your class

class MyModel
  include RedisLocker
end

Every lock is internally identified by model's id thus if you will lock model with id=10, then model with id=11 will be unlocked but every other instance with id=10 will be locked.

Locking

RedisLocker has two types of locks: model_lock and method_lock. Important thing is that every method_lock creates model_lock.

High Level Api

High Level Api is provided by instance method with_redis_lock and two class methods lock_every_method_call and lock_method.

with_redis_lock

with_redis_lock is an api to deal with model locks. It locks an object, then executes passed block and after that unlocks object

some_redis_lockable_object.with_redis_lock do
  some_task
end

If there will be another with_redis_lock called then second call will fail.

lock_method

lock_method internally wraps method to with_redis_lock call every time when it's called.

class Model
  include RedisLocker
  lock_method :some_method
  def id
    10
  end
  def some_method
    #sth
  end
end

Code above creates method_lock for :some_method and model_lock for Model with id=10 every time when some_method is called

lock_every_method_call

It effectively does same thing as adding lock_method for every method in Model class except method passed as excluded methods.

class Model
  include RedisLocker
  lock_every_method_call except: [:id, :initialize, :not_locked_method]
  def id
    10
  end
  def locked_method
    #sth
  end
  def not_locked_method
    #sth
  end
end

Default excluded methods are id and initialize

Additional options

Every high level api method accepts same options: :strategy, :retry_interval and :retry_count. Unless you're using :retry startegy, :retry_count and :retry_interval will be ignored

:strategy

:strategy tells RedisLocker what to do when locked action is tried to be performed, default strategy is :exception.
Exception ( strategy: :exception ) strategy raises RedisLocker::Errors::Locked when another lock on resource is present.
Retry ( strategy: :retry ) strategy tries :retry_count + 1 times to execute code with :retry_interval between tries.

class Model
  lock_method :some_method, strategy: :retry, retry_count: 2, retry_interval: 1
  # rest of class omitted
end

above snippet after some_method was called tries to execute some_method, when lock occurs it will try two times with 1 second interval. If lock will be still present then it will raise RedisLocker::Errors::Locked
Silently die ( strategy: :silently_die ) strategy returns false if lock occurs

Low level api

RedisLocker provides also low level api which allows to manualy locking and unlocking models, which can be helpful sometimes but shouldn't be used without good reason

lock

lock method locks model and returns true if model was locked successfuly or false if model is already locked

lock!

lock! method does same thing as lock but if model was locked already it raises RedisLocker::Errors::AlreadyLocked error

unlock

unlock unlocks object if there is lock and returns true, otherwise returns false

locked?

locked? returns if object is locked

You can mix low level and high level api

some_model.lock

then if you try in another place

some_model.with_redis_lock strategy: :exception do
  #sth
end

It will raise exception because some_model is locked but you will be able to call some locked method because there is no lock on any specific method.

releasing all locks

When you or someone else messed up with locks which are still present in redis you can use RedisLocker.release_locks! which removes all locks in redis.

Extending RedisLocker

You can write own locker by inheriting from RedisLocker::Locker, you have to implement lock, lock!, locked? and unlock methods. RedisLocker::Locker provides unique @instance_hash to sign locks and with_redis_lock method with implemented :exception, :retry and :silently_die strategies. You aren't forced to use Redis to store locks, if you want to you have to include RedisLocker::RedisConnection module which provides redis method to access redis connection. Otherwise you have to write own storing logic eg. using DB, own store engine or even files.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/rwegrzyniak/redis_locker. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the RedisLocker project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.