Class: Ribbon::Intercom::Service::Channel::Stores::RedisStore::Mutex

Inherits:
Object
  • Object
show all
Defined in:
lib/ribbon/intercom/service/channel/stores/redis_store.rb

Overview

A redis mutex inspired by <github.com/leandromoreira/redlock-rb>

Defined Under Namespace

Classes: LockUnobtainableError

Constant Summary collapse

UNLOCK_SCRIPT =

Lua script to send to Redis when releasing a lock. See: redis.io/commands/set

<<-LUA
  if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
  else
    return 0
  end
LUA
DEFAULT_RETRY_COUNT =
3
DEFAULT_RETRY_DELAY =
200
CLOCK_DRIFT_FACTOR =
0.01

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(redis, name, opts = {}) ⇒ Mutex

Returns a new instance of Mutex.



136
137
138
139
140
141
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 136

def initialize(redis, name, opts={})
  @redis = redis
  @name = name
  @retry_count = opts[:retry_count] || DEFAULT_RETRY_COUNT
  @retry_delay = opts[:retry_delay] || DEFAULT_RETRY_DELAY
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



132
133
134
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 132

def name
  @name
end

#redisObject (readonly)

Class Methods



131
132
133
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 131

def redis
  @redis
end

#retry_countObject (readonly)

Returns the value of attribute retry_count.



133
134
135
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 133

def retry_count
  @retry_count
end

#retry_delayObject (readonly)

Returns the value of attribute retry_delay.



134
135
136
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 134

def retry_delay
  @retry_delay
end

Class Method Details

.time_in_msObject



126
127
128
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 126

def time_in_ms
  (Time.now.to_f * 1000).to_i
end

Instance Method Details

#lock(ttl = 1000) ⇒ Object

Acquire a lock (non-blocking).



164
165
166
167
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 164

def lock(ttl=1000)
  handle = SecureRandom.uuid
  redis.set(name, handle, nx: true, px: ttl) && handle
end

#synchronize(ttl) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 143

def synchronize(ttl)
  retry_count.times {
    start_time = self.class.time_in_ms

    if (handle=lock(ttl))
      begin
        time_elapsed = (self.class.time_in_ms - start_time).to_i
        return yield(ttl - time_elapsed - _drift(ttl))
      ensure
        unlock(handle)
      end
    else
      sleep(rand(retry_delay).to_f / 1000)
    end
  }

  raise LockUnobtainableError, name
end

#unlock(handle) ⇒ Object

Release a lock.



171
172
173
174
175
# File 'lib/ribbon/intercom/service/channel/stores/redis_store.rb', line 171

def unlock(handle)
  redis.eval(UNLOCK_SCRIPT, [name], [handle])
rescue
  # Do nothing, unlocking is best effort.
end