Project

redis_lock

0.0
No commit activity in last 3 years
No release in over 3 years
Lock with redis
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.15
~> 0.17
~> 10.0
~> 3.6

Runtime

~> 3
 Project Readme

RedisLock

use cases:

  • Do not allow anyone to perform de same operation while this is running.
  • Do not perform this operation unless the previous was executed in more than 5 minutes ago.

Installation

Add this line to your application's Gemfile:

gem 'redis_lock'

And then execute:

$ bundle

Or install it yourself as:

$ gem install redis_lock

Setup

This setup it's optional in any instance of RedisLock you can provide an optional argument :redis. But if you do not want to provide it in all the instances is a good shortcut to set it here.

RedisLock.setup do |config|
  # redis
  # Accepts `block` (or something responding to `#call`) or `Hash`
  #
  # In test configuration like your `spec_helper`
  # recommend `mock_redis` gem
  # example:
  #    config.redis = -> { MockRedis.new }
  #
  # When using Sidekiq
  # example:    
  #    config.redis = -> { Sidekiq.redis{ |r| r } }
  #
  # In `Rails`
  # example:
  #     config.redis = -> do
  #       if Rails.env.test?
  #         MockRedis.new
  #       elsif Rails.env.development?
  #         { host: '127.0.0.1', port: 6379 }
  #       else
  #         Sidekiq.redis{ |r| r }
  #       end
  #     end
  config.redis = { host: '127.0.0.1'
                   port: 6379
                   db: 2 }
  # logger
  # default: Logger.new(STDOUT)
  config.logger = Rails.logger

  # Default ttl for all your locks
  # default: 60
  #
  # config.default_ttl = 120
end

Usage

lock = RedisLock.new('my_key')

lock.locked? #=> false
# Add 20 secs time to live (TTL)
lock.set(20) #=> true
lock.locked? #=> true
lock.remove #=> true
lock.locked? #=> false

semaphore: No one can perform the same operation while this is running the rest of the processes are waiting while the lock is in use, When lock is released another one takes the lock.

args:

  • key [string]
  • opts: {}
    • :redis
    • :ttl, time to live
    • :set_opts, check set documentation
    • :wait, time waiting for the next check if the lock is in use
out = RedisLock.semaphore('my_key') do |l|
        sleep 3 # Do something
        :hello
      end
out #=> :hello
RedisLock.new('my_key').locked? #=> false

multiple locks:

Very useful when you are changing multiple objects and want to protect them in a distributed system

lock_1 = RedisLock.new('my_key')
lock_2 = RedisLock.new('another_key')

out = RedisLock.semaphore('my_key', 'another_key') do |multi_lock|
        multi_lock.locked? #=> true
        lock_1.locked? #=> true
        lock_2.locked? #=> true
        sleep 3 # Do something
        :hello
      end
out #=> :hello
lock_1.locked? #=> false
lock_2.locked? #=> false

if_open:

Use case: Send email to user. The User should receive only 1 email per day

ttl = (24 * 3600) # one day
RedisLock.if_open("User:1-sales-products") do |l|
  # Send Email
  l.set(ttl)
end

Methods:

set

Will store the key to redis with a ttl (time to live). args:

  • ttl | default: 60
  • opts | default: {}
    • value (String) - default: time now
    • px - miliseconds instead of seconds | default: false
    • nk - Only set the key if it does not already exist. | default: false
    • xx - Only set the key if it already exist. | default: false
lock = RedisLock.new('my_key')

lock.set(60)
lock.ttl #=> 60
lock.open? # => false

with options:

lock = RedisLock.new('my_key')

lock.set(60, nx: true) # only if the key does not exists
# => true (key has been stored)
lock.ttl #=> 60
lock.open? # => false

Redis documentation: https://redis.io/commands/set

Set key to hold the string value. If key already holds a value, it is overwritten, regardless of its type. Any previous time to live associated with the key is discarded on successful SET operation.

EX seconds -- Set the specified expire time, in seconds.
PX milliseconds -- Set the specified expire time, in milliseconds.
NX -- Only set the key if it does not already exist.
XX -- Only set the key if it already exist.

locked?

Returns true if lock is set

lock = RedisLock.new('my_key')
lock.set(60) # => true (key has been stored)
lock.locked? # => true
lock.remove
lock.locked? # => false

alias method: exists?

open?

Returns true if NO lock is set

lock = RedisLock.new('my_key')
lock.open? # => true
lock.set(60) # => true (key has been stored)
lock.open? # => false

alias method: unlocked?

delete

Removes the key from the Redis store

lock = RedisLock.new('my_key')
lock.set(60) # => true (key has been stored)
lock.locked? # => true
lock.delete
lock.locked? # => false

alias methods: unlock!,open!

value

Returns the value stored in redis

lock = RedisLock.new('my_key')
lock.set(60, value: 'hi there!')
lock.value # => 'hi there!'

ttl

Returns the pending ttl (time to live)

lock = RedisLock.new('my_key')
lock.set(60)
lock.ttl # => 60
sleep 10
lock.ttl # => 50

having already a connection: example: Sidekiq

Sidekiq.redis do |connection|
  lock = RedisLock.new('my_key', redis: connection)
  # do something
end

Development

After checking out the repo, run bin/setup to install dependencies. 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.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/arturictus/redis_lock.