class Continuity::RedisBackend

Constants

LAST_SCHED_KEY
LOCK_KEY

Public Class Methods

new(redis, frequency = 10, lock_timeout = 30) click to toggle source
# File lib/continuity/redis_backend.rb, line 6
def initialize(redis, frequency = 10, lock_timeout = 30)
  @redis        = redis
  @lock_timeout = lock_timeout
  @frequency    = frequency
end

Public Instance Methods

lock_for_scheduling(now) { |now| ... } click to toggle source
# File lib/continuity/redis_backend.rb, line 12
def lock_for_scheduling(now)
  scheduled_up_to = @redis.get(LAST_SCHED_KEY).to_i

  # bootstrap
  if scheduled_up_to == 0
    lock(now) do

      # double check that someone else has bootstrapped
      # since we fetched the key
      scheduled_up_to = @redis.get(LAST_SCHED_KEY).to_i
      if scheduled_up_to == 0
        yield now - 1
        @redis.set(LAST_SCHED_KEY, now)

        return now
      else
        return scheduled_up_to
      end
    end

  end

  # this is tricky, we only want to attempt a lock
  # if there is a possibility we can schedule things.
  # BUT, once we attain a lock we need to make sure
  # someone else hasn't already scheduled that period
  if (now - scheduled_up_to) >= @frequency
    lock(now) do
      scheduled_up_to = @redis.get(LAST_SCHED_KEY).to_i
      if (now - scheduled_up_to) >= @frequency
        # good we should still schedule
        yield scheduled_up_to
        @redis.set(LAST_SCHED_KEY, now)
        scheduled_up_to = now
      end
    end
  end

  scheduled_up_to
end

Private Instance Methods

lock(now) { || ... } click to toggle source

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

# File lib/continuity/redis_backend.rb, line 55
def lock(now)
  lock_expiration = now + @lock_timeout + 1 
  res = @redis.setnx(LOCK_KEY, lock_expiration)

  acquired_lock = false
  if res
    acquired_lock = true
    yield
  else
    current_expiration  = @redis.get(LOCK_KEY).to_i
    if current_expiration < now
      new_expiration = now + @lock_timeout + 1 
      if current_expiration == @redis.getset(LOCK_KEY, new_expiration).to_i
        acquired_lock = true
        yield
      end
    end
  end
ensure
  if acquired_lock
    @redis.del(LOCK_KEY)
  end
end