Project

suo

0.15
Low commit activity in last 3 years
There's a lot of open issues
A long-lived project that still receives updates
Distributed locks (mutexes & semaphores) using Memcached or Redis.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
 Dependencies

Development

~> 5.5.0
~> 13.0
~> 0.49.0

Runtime

>= 0
>= 0
 Project Readme

Suo Build Status Code Climate Gem Version

đź”’ Distributed semaphores using Memcached or Redis in Ruby.

Suo provides a very performant distributed lock solution using Compare-And-Set (CAS) commands in Memcached, and WATCH/MULTI in Redis. It allows locking both single exclusion (like a mutex - sharing one resource), as well as multiple resources.

Installation

Add this line to your application’s Gemfile:

gem 'suo'

Usage

Basic

# Memcached
suo = Suo::Client::Memcached.new("foo_resource", connection: "127.0.0.1:11211")

# Redis
suo = Suo::Client::Redis.new("baz_resource", connection: {host: "10.0.1.1"})

# Pre-existing client
suo = Suo::Client::Memcached.new("bar_resource", client: some_dalli_client)

suo.lock do
  # critical code here
  @puppies.pet!
end

# The resources argument is the number of resources the semaphore will allow to lock (defaulting to one - a mutex)
suo = Suo::Client::Memcached.new("bar_resource", client: some_dalli_client, resources: 2)

Thread.new { suo.lock { puts "One"; sleep 2 } }
Thread.new { suo.lock { puts "Two"; sleep 2 } }
Thread.new { suo.lock { puts "Three" } }

# will print "One" "Two", but not "Three", as there are only 2 resources

# custom acquisition timeouts (time to acquire)
suo = Suo::Client::Memcached.new("protected_key", client: some_dalli_client, acquisition_timeout: 1) # in seconds

# manually locking/unlocking
# the return value from lock without a block is a unique token valid only for the current lock
# which must be unlocked manually
token = suo.lock
foo.baz!
suo.unlock(token)

# custom stale lock expiration (cleaning of dead locks)
suo = Suo::Client::Redis.new("other_key", client: some_redis_client, stale_lock_expiration: 60*5)

Stale locks

"Stale locks" - those acquired more than stale_lock_expiration (defaulting to 3600 or one hour) ago - are automatically cleared during any operation on the key (lock, unlock, refresh). The locked? method will not return true if only stale locks exist, but will not modify the key itself.

To re-acquire a lock in the middle of a block, you can use the refresh method on client.

suo = Suo::Client::Redis.new("foo")

# lock is the same token as seen in the manual example, above
suo.lock do |token|
  5.times do
    baz.bar!
    suo.refresh(token)
  end
end

Time To Live

Suo::Client::Redis.new("bar_resource", ttl: 60) #ttl in seconds

A key representing a set of lockable resources is removed once the last resource lock is released and the ttl time runs out. When another lock is acquired and the key has been removed the key has to be recreated.

TODO

  • more race condition tests

History

View the changelog.

Contributing

Everyone is encouraged to help improve this project. Here are a few ways you can help: