Module: RedisSupport::ClassMethods

Included in:
RedisSupport
Defined in:
lib/redis_support/locks.rb,
lib/redis_support/class_extensions.rb

Instance Method Summary collapse

Instance Method Details

#acquire_redis_lock(key_to_lock, expiration = 30, interval = 1) ⇒ Object

Acquire a lock on a key in our Redis database. This is a blocking call. It sleeps until the lock has been successfully acquired.

Basic usage:

acquire_redis_lock( key.my_key )
# do some stuff on my_key
release_redis_lock( key.my_key )

interval - sleep interval for checking the lock’s status.

Returns nothing.



53
54
55
56
57
# File 'lib/redis_support/locks.rb', line 53

def acquire_redis_lock( key_to_lock, expiration = 30, interval = 1 )
  until acquire_redis_lock_nonblock( key_to_lock, expiration )
    sleep interval
  end
end

#acquire_redis_lock_nonblock(key_to_lock, expiration = 30) ⇒ Object

Attempt to acquire a lock on a key in our Redis database.

Returns true on success and false on failure

Described in detail here:

http://code.google.com/p/redis/wiki/SetnxCommand

key_to_lock - the key to lock. the actual key for the lock in redis will

be this value with 'lock.' prepended, which lets this whole
acquire_lock business act like a standard ruby object or
synchronize lock. Also it ensures that all locks in the database
can be easily viewed using redis.keys("lock.*")

expiration - the expiration for the lock, expressed as an Integer. default is

30 seconds from when the lock is acquired. Note that this is the
amount of time others will wait for you, not the amount of time
you will wait to acquire the lock.


78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/redis_support/locks.rb', line 78

def acquire_redis_lock_nonblock( key_to_lock, expiration = 30 )
  key = lock_key( key_to_lock )
  if redis.setnx key, timeout_i( expiration )
    return true
  else
    if redis.get( key ).to_i < Time.now.to_i
      old_timeout = redis.getset( key, timeout_i( expiration ) ).to_i
      if old_timeout < Time.now.to_i
        return true
      end
    end
  end
  false
end

#has_redis_lock?(locked_key) ⇒ Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/redis_support/locks.rb', line 100

def has_redis_lock?( locked_key )
  redis.exists lock_key(locked_key) 
end

#is_redis_fulfilling_locked?(locked_key) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
# File 'lib/redis_support/locks.rb', line 108

def is_redis_fulfilling_locked?( locked_key )
  redis.get( locked_key ).to_i > Time.now.to_i
end

#is_redis_locked?(locked_key) ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/redis_support/locks.rb', line 104

def is_redis_locked?( locked_key )
  locked_until(locked_key) > Time.now.to_i
end

#locked_until(locked_key) ⇒ Object



112
113
114
# File 'lib/redis_support/locks.rb', line 112

def locked_until(locked_key)
  redis.get( lock_key(locked_key) ).to_i
end

#redefine_redis_key(name, keystruct) ⇒ Object



70
71
72
73
74
75
76
77
78
79
# File 'lib/redis_support/class_extensions.rb', line 70

def redefine_redis_key( name, keystruct )
  if Keys.methods.include? name.to_s
    Keys.class_eval <<-RUBY
      class << self
        undef #{name}
      end
    RUBY
  end
  redis_key(name, keystruct)
end

#redisObject



13
14
15
16
17
18
19
# File 'lib/redis_support/class_extensions.rb', line 13

def redis
  if(self == RedisSupport)
    @redis
  else
    @redis || RedisSupport.redis
  end
end

#redis=(connection) ⇒ Object



9
10
11
# File 'lib/redis_support/class_extensions.rb', line 9

def redis=(connection)
  @redis = RedisSupport.redis_connect(connection)
end

#redis_key(name, keystruct) ⇒ Object

Goal is to allow a class to declare a redis key/property The key is a colon delimited string where variables are listed are upper case (underscores inbetween) and non variables are completely lower case (underscores inbetween or appending/prepending)

variables cannot be repeated and must start with a letter the key must also start with a nonvariable

Examples

redis_key :workpools, "job:JOB_ID:workpools"

Returns the redis key.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/redis_support/class_extensions.rb', line 34

def redis_key( name, keystruct )
  if Keys.methods.include? name.to_s
    return
    # raise DuplicateRedisKeyDefinitionError
  end
  
  key = keystruct.split(":")
  
  unless (first = key.shift) =~ STR_PATTERN
    raise InvalidRedisKeyDefinitionError.new "keys must begin with lowercase letters"
  end

  vars, strs = key.inject([[],[]]) do |(vs, ss), token|
    case token
    when VAR_PATTERN
      var = token.downcase
      ss << "\#{#{var}}"
      vs << var
    when STR_PATTERN
      ss << token
    else
      raise InvalidRedisKeyDefinitionError.new "Internal error parsing #{keystruct} : last token : #{token}"
    end
    [vs, ss]
  end

  strs.unshift(first)

  RedisSupport::Keys.class_eval <<-RUBY, __FILE__, __LINE__ + 1
    def self.#{name.to_s}( #{vars.map {|x| x.to_s }.join(', ')} )
      "#{strs.join(":")}"
    end
  RUBY
  RedisSupport::Keys.keystructs << keystruct
end

#redis_lock(key_to_lock, expiration = 30, interval = 1) ⇒ Object

Lock a block of code so it can only be accessed by one thread in our system at a time.

See ‘acquire_redis_lock’ for details on parameters.

Returns nothing.



12
13
14
15
16
17
# File 'lib/redis_support/locks.rb', line 12

def redis_lock( key_to_lock, expiration = 30, interval = 1 )
  acquire_redis_lock( key_to_lock, expiration, interval )
  yield
ensure
  release_redis_lock( key_to_lock )
end

#redis_lock_nonblock(key_to_lock, expiration = 30) ⇒ Object

Attempt to lock a block of code so it can only be accessed by one thread in our system at a time. Skips the block if it cannot lock.

See ‘acquire_redis_lock_nonblock’ for details on parameters.

Returns nothing.



25
26
27
28
29
30
# File 'lib/redis_support/locks.rb', line 25

def redis_lock_nonblock( key_to_lock, expiration = 30 )
  lock_acquired = acquire_redis_lock_nonblock( key_to_lock, expiration )
  yield if lock_acquired
ensure
  release_redis_lock( key_to_lock ) if lock_acquired
end

#redis_throttle(key_to_lock, expiration = 30) ⇒ Object

Throttle a block of code so it is only executed at most every ‘expiration` seconds. The block is skipped if it has been run more recently.

Returns nothing



37
38
39
# File 'lib/redis_support/locks.rb', line 37

def redis_throttle( key_to_lock, expiration = 30 )
  yield if acquire_redis_lock_nonblock( key_to_lock, expiration )
end

#release_redis_lock(locked_key) ⇒ Object

See docs for acquire_redis_lock above

Returns nothing.



96
97
98
# File 'lib/redis_support/locks.rb', line 96

def release_redis_lock( locked_key )
  redis.del lock_key( locked_key )
end