Class: Logster::RedisRateLimiter

Inherits:
Object
  • Object
show all
Defined in:
lib/logster/redis_store.rb

Constant Summary collapse

BUCKETS =
6
PREFIX =
"__LOGSTER__RATE_LIMIT".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(redis, severities, limit, duration, redis_prefix = nil, callback = nil) ⇒ RedisRateLimiter

Returns a new instance of RedisRateLimiter.



22
23
24
25
26
27
28
29
30
31
# File 'lib/logster/redis_store.rb', line 22

def initialize(redis, severities, limit, duration, redis_prefix = nil, callback = nil)
  @severities = severities
  @limit = limit
  @duration = duration
  @callback = callback
  @redis_prefix = redis_prefix
  @redis = redis
  @bucket_range = @duration / BUCKETS
  @mget_keys = (0..(BUCKETS - 1)).map { |i| "#{key}:#{i}" }
end

Instance Attribute Details

#callbackObject (readonly)

Returns the value of attribute callback.



9
10
11
# File 'lib/logster/redis_store.rb', line 9

def callback
  @callback
end

#durationObject (readonly)

Returns the value of attribute duration.



9
10
11
# File 'lib/logster/redis_store.rb', line 9

def duration
  @duration
end

Class Method Details

.clear_all(redis, redis_prefix = nil) ⇒ Object



11
12
13
14
15
16
17
18
19
20
# File 'lib/logster/redis_store.rb', line 11

def self.clear_all(redis, redis_prefix=nil)
  prefix = key_prefix(redis_prefix)

  redis.eval "
  local keys = redis.call('keys', '*#{prefix}*')
  if (table.getn(keys) > 0) then
    redis.call('del', unpack(keys))
  end
  "
end

Instance Method Details

#callback_keyObject



78
79
80
# File 'lib/logster/redis_store.rb', line 78

def callback_key
  "#{key}:callback_triggered"
end

#check(severity) ⇒ Object



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
69
70
# File 'lib/logster/redis_store.rb', line 37

def check(severity)
  return unless @severities.include?(severity)
  time = Time.now.to_i
  num = bucket_number(time)
  redis_key = "#{key}:#{num}"

  current_rate = @redis.eval <<-LUA
    local bucket_number = #{num}
    local bucket_count = redis.call("INCR", "#{redis_key}")

    if bucket_count == 1 then
      redis.call("EXPIRE", "#{redis_key}", "#{bucket_expiry(time)}")
      redis.call("DEL", "#{callback_key}")
    end

    local function retrieve_rate ()
      local sum = 0
      local values = redis.call("MGET", #{mget_keys(num)})
      for index, value in ipairs(values) do
        if value ~= false then sum = sum + value end
      end
      return sum
    end

    return (retrieve_rate() + bucket_count)
  LUA

  if !@redis.get(callback_key) && (current_rate >= @limit)
    @callback.call(current_rate) if @callback
    @redis.set(callback_key, 1)
  end

  current_rate
end

#keyObject



72
73
74
75
76
# File 'lib/logster/redis_store.rb', line 72

def key
  # "_LOGSTER_RATE_LIMIT:012:20:30"
  # Triggers callback when log levels of :debug, :info and :warn occurs 20 times within 30 secs
  "#{key_prefix}:#{@severities.join("")}:#{@limit}:#{@duration}"
end

#retrieve_rateObject



33
34
35
# File 'lib/logster/redis_store.rb', line 33

def retrieve_rate
  @redis.mget(@mget_keys).reduce(0) { |sum, value| sum + value.to_i }
end