Class: TrafficJam::Limit
- Inherits:
-
Object
- Object
- TrafficJam::Limit
- Defined in:
- lib/traffic_jam/limit.rb
Overview
This class represents a rate limit on an action, value pair. For example, if rate limiting the number of requests per IP address, the action could be :requests
and the value would be the IP address. The class exposes atomic increment operations and allows querying of the current amount used and amount remaining.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#action ⇒ Symbol
readonly
The name of the action being rate limited.
-
#max ⇒ Integer
readonly
The integral cap of the limit amount.
-
#period ⇒ Integer
readonly
The duration of the limit in seconds.
-
#value ⇒ Object
readonly
Returns the value of attribute value.
Instance Method Summary collapse
-
#decrement(amount = 1, time: Time.now) ⇒ true
Decrement the amount used by the given number.
-
#exceeded?(amount = 1) ⇒ Boolean
Return whether incrementing by the given amount would exceed limit.
- #flatten ⇒ Object
-
#increment(amount = 1, time: Time.now) ⇒ Boolean
Increment the amount used by the given number.
-
#increment!(amount = 1, time: Time.now) ⇒ nil
Increment the amount used by the given number.
-
#initialize(action, value, max: nil, period: nil) ⇒ Limit
constructor
Constructor takes an action name as a symbol, a maximum cap, and the period of limit.
-
#limit_exceeded(amount = 1) ⇒ TrafficJam::Limit?
Return itself if incrementing by the given amount would exceed limit, otherwise nil.
-
#remaining ⇒ Integer
Return amount of limit remaining, taking time drift into account.
-
#reset ⇒ nil
Reset amount used to 0.
-
#used ⇒ Integer
Return amount of limit used, taking time drift into account.
Constructor Details
#initialize(action, value, max: nil, period: nil) ⇒ Limit
Constructor takes an action name as a symbol, a maximum cap, and the period of limit. max
and period
are required keyword arguments.
35 36 37 38 39 |
# File 'lib/traffic_jam/limit.rb', line 35 def initialize(action, value, max: nil, period: nil) raise ArgumentError.new('Max is required') if max.nil? raise ArgumentError.new('Period is required') if period.nil? @action, @value, @max, @period = action, value, max, period end |
Instance Attribute Details
#action ⇒ Symbol (readonly)
Returns the name of the action being rate limited.
25 26 27 |
# File 'lib/traffic_jam/limit.rb', line 25 def action @action end |
#max ⇒ Integer (readonly)
Returns the integral cap of the limit amount.
25 |
# File 'lib/traffic_jam/limit.rb', line 25 attr_reader :action, :max, :period, :value |
#period ⇒ Integer (readonly)
Returns the duration of the limit in seconds. Regardless of the current amount used, after the period passes, the amount used will be 0.
25 |
# File 'lib/traffic_jam/limit.rb', line 25 attr_reader :action, :max, :period, :value |
#value ⇒ Object (readonly)
Returns the value of attribute value.
25 |
# File 'lib/traffic_jam/limit.rb', line 25 attr_reader :action, :max, :period, :value |
Instance Method Details
#decrement(amount = 1, time: Time.now) ⇒ true
Decrement the amount used by the given number. Time of decrement can be specified optionally with a keyword argument, which is useful for rolling back an increment operation at a certain time.
112 113 114 |
# File 'lib/traffic_jam/limit.rb', line 112 def decrement(amount = 1, time: Time.now) increment(-amount, time: time) end |
#exceeded?(amount = 1) ⇒ Boolean
Return whether incrementing by the given amount would exceed limit. Does not change amount used.
46 47 48 |
# File 'lib/traffic_jam/limit.rb', line 46 def exceeded?(amount = 1) used + amount > max end |
#flatten ⇒ Object
148 149 150 |
# File 'lib/traffic_jam/limit.rb', line 148 def flatten [self] end |
#increment(amount = 1, time: Time.now) ⇒ Boolean
Increment the amount used by the given number. Does not perform increment if the operation would exceed the limit. Returns whether the operation was successful. Time of increment can be specified optionally with a keyword argument, which is useful for rolling back with a decrement.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/traffic_jam/limit.rb', line 67 def increment(amount = 1, time: Time.now) return amount <= 0 if max.zero? if amount != amount.to_i raise ArgumentError.new("Amount must be an integer") end = (time.to_f * 1000).to_i argv = [, amount.to_i, max, period * 1000] result = begin redis.evalsha( Scripts::INCREMENT_SCRIPT_HASH, keys: [key], argv: argv) rescue Redis::CommandError redis.eval(Scripts::INCREMENT_SCRIPT, keys: [key], argv: argv) end !!result end |
#increment!(amount = 1, time: Time.now) ⇒ nil
Increment the amount used by the given number. Does not perform increment if the operation would exceed the limit. Raises an exception if the operation is unsuccessful. Time of# increment can be specified optionally with a keyword argument, which is useful for rolling back with a decrement.
99 100 101 102 103 |
# File 'lib/traffic_jam/limit.rb', line 99 def increment!(amount = 1, time: Time.now) if !increment(amount, time: time) raise TrafficJam::LimitExceededError.new(self) end end |
#limit_exceeded(amount = 1) ⇒ TrafficJam::Limit?
Return itself if incrementing by the given amount would exceed limit, otherwise nil. Does not change amount used.
54 55 56 |
# File 'lib/traffic_jam/limit.rb', line 54 def limit_exceeded(amount = 1) self if exceeded?(amount) end |
#remaining ⇒ Integer
Return amount of limit remaining, taking time drift into account.
144 145 146 |
# File 'lib/traffic_jam/limit.rb', line 144 def remaining max - used end |
#reset ⇒ nil
Reset amount used to 0.
119 120 121 122 |
# File 'lib/traffic_jam/limit.rb', line 119 def reset redis.del(key) nil end |
#used ⇒ Integer
Return amount of limit used, taking time drift into account.
127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/traffic_jam/limit.rb', line 127 def used return 0 if max.zero? , amount = redis.hmget(key, 'timestamp', 'amount') if && amount time_passed = Time.now.to_f - .to_i / 1000.0 drift = max * time_passed / period last_amount = [amount.to_f, max].min [(last_amount - drift).ceil, 0].max else 0 end end |