fine_tune 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.ruby-version +1 -0
- data/.travis.yml +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +86 -18
- data/fine_tune.gemspec +5 -4
- data/lib/fine_tune/base.rb +87 -15
- data/lib/fine_tune/max_rate_error.rb +1 -0
- data/lib/fine_tune/strategies/base.rb +20 -2
- data/lib/fine_tune/strategies/leaky_bucket.rb +126 -0
- data/lib/fine_tune/version.rb +1 -1
- data/lib/fine_tune.rb +14 -2
- metadata +56 -27
- checksums.yaml +0 -7
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.9.3-p194
|
data/.travis.yml
CHANGED
data/LICENSE.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2016
|
|
3
|
+
Copyright (c) 2016 Vesess Inc. (laknath [at] vesess [dot] com)
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
|
@@ -1,41 +1,109 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://travis-ci.org/laknath/fine_tune)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# FineTune - a flexible rate limiting/monitoring library
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
FineTune can be used to limit the rate of any given activity/event
|
|
6
|
+
stream such as outgoing emails for a user. It's not fixed on a
|
|
7
|
+
particular implementation of throttling, rather it provides a framework
|
|
8
|
+
to add custom implementations/algorithms.
|
|
9
|
+
|
|
10
|
+
The main difference with other rate limiting libraries is the
|
|
11
|
+
flexibility of using custom throttling algorithms and external
|
|
12
|
+
data/cache stores. Also it doesn't need to be configured first, all
|
|
13
|
+
options can be passed in each throttling request.
|
|
6
14
|
|
|
7
15
|
## Installation
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
$ gem install fine_tune
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
gem 'fine_tune'
|
|
13
|
-
```
|
|
19
|
+
## Usage
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
Throttle represents an occurrence of a defined event. It will return true
|
|
22
|
+
if the threshold has reached. If not the count will be increased by one
|
|
23
|
+
and will return false.
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
```
|
|
26
|
+
$ FineTune.throttle(:transactions_per_hour, user_id, options)
|
|
27
|
+
```
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
* :transactions_per_hour - the name for the event
|
|
30
|
+
* user_id - some unique identifier for a resource (ie: user)
|
|
31
|
+
* options - configurations for the throttling implementation
|
|
20
32
|
|
|
21
|
-
|
|
33
|
+
An additional block can be passed, which will be called with event
|
|
34
|
+
details. ie:
|
|
22
35
|
|
|
23
|
-
|
|
36
|
+
```
|
|
37
|
+
$ FineTune.throttle(:transactions_per_hour, user_id, options) do
|
|
38
|
+
| count, comparison, id, strategy, options |
|
|
39
|
+
#something...
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
In addition, there's a more charged version - throttle! which will raise
|
|
44
|
+
a MaxRateError when reached the threshold. Same as throttle, it takes a
|
|
45
|
+
block.
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
```
|
|
48
|
+
$ FineTune.throttle!(:transactions_per_hour, user_id, options)
|
|
49
|
+
```
|
|
26
50
|
|
|
27
|
-
|
|
51
|
+
To get the current count of events:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
$ FineTune.count(:emails_per_day, user_id, options)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
To check whether threshold has reached:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
$ FineTune.rate_exceeded?(:emails_per_day, user_id, options)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
To reset the count of events for a resource:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
$ FineTune.reset(:emails_per_day, user_id, options)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
###(Optional)
|
|
28
70
|
|
|
29
|
-
|
|
71
|
+
To configure default options:
|
|
30
72
|
|
|
31
|
-
|
|
73
|
+
```
|
|
74
|
+
$ FineTune.add_limit(:hourly_emails, window: 3600, limit: 25)
|
|
75
|
+
```
|
|
76
|
+
Options passed here will be overridden by any options passed in
|
|
77
|
+
the individual calls.
|
|
78
|
+
|
|
79
|
+
To remove a pre-defined rule:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
$ FineTune.remove_limit(:hourly_emails, window: 3600, limit: 25)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
###Supported implementations:
|
|
86
|
+
|
|
87
|
+
* [Leaky Bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
After checking out the repo, run `bundle` to install dependencies. Then, run `rake test` to run the tests.
|
|
32
93
|
|
|
33
94
|
## Contributing
|
|
34
95
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
96
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/laknath/fine_tune. This project
|
|
97
|
+
is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to
|
|
98
|
+
the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
99
|
+
|
|
100
|
+
## Credits
|
|
101
|
+
|
|
102
|
+
Some of the behaviours were inspired by [Props](https://github.com/zendesk/prop).
|
|
103
|
+
|
|
104
|
+
Thanks you!
|
|
36
105
|
|
|
37
106
|
|
|
38
107
|
## License
|
|
39
108
|
|
|
40
109
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
41
|
-
|
data/fine_tune.gemspec
CHANGED
|
@@ -17,9 +17,10 @@ Gem::Specification.new do |spec|
|
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
18
18
|
spec.require_paths = ["lib"]
|
|
19
19
|
|
|
20
|
-
spec.add_development_dependency "bundler"
|
|
21
|
-
spec.add_development_dependency "rake"
|
|
20
|
+
spec.add_development_dependency "bundler"
|
|
21
|
+
spec.add_development_dependency "rake"
|
|
22
22
|
spec.add_development_dependency "minitest", "~> 5.0"
|
|
23
|
-
spec.add_development_dependency "minitest-reporters", "~> 1.1
|
|
24
|
-
spec.add_development_dependency "mocha", "~> 0.14
|
|
23
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.1"
|
|
24
|
+
spec.add_development_dependency "mocha", "~> 0.14"
|
|
25
|
+
spec.add_development_dependency "activesupport", "~> 4.2"
|
|
25
26
|
end
|
data/lib/fine_tune/base.rb
CHANGED
|
@@ -1,15 +1,69 @@
|
|
|
1
|
+
#
|
|
2
|
+
# FineTune::Base provides a set of public methods and helpers to throttle
|
|
3
|
+
# and fetch the event count through a singleton object. The mechanism used to
|
|
4
|
+
# throttle and event counting is delegated to the defined strategy.
|
|
5
|
+
#
|
|
6
|
+
# Strategies can be defined for the entire library through
|
|
7
|
+
# FineTune.default_strategy=. Strategy can also be defined for each
|
|
8
|
+
# throttling call by passing <tt>:strategy</tt> option.
|
|
9
|
+
#
|
|
10
|
+
# Additionally, the datastore used to maintain event counts can be
|
|
11
|
+
# also customized by setting FineTune.adapter=. It takes an
|
|
12
|
+
# ActiveSupport::Cache::Store[http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html]
|
|
13
|
+
# as an adapter.
|
|
14
|
+
|
|
1
15
|
module FineTune
|
|
2
16
|
class Base
|
|
3
17
|
include Singleton
|
|
4
18
|
|
|
5
|
-
class << self;
|
|
19
|
+
class << self;
|
|
20
|
+
# Used to set a default strategy for all calls.
|
|
21
|
+
attr_accessor :default_strategy
|
|
22
|
+
|
|
23
|
+
# Used to set the external data store.
|
|
24
|
+
attr_accessor :adapter
|
|
25
|
+
|
|
26
|
+
def registry #:nodoc:
|
|
27
|
+
@@registry
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def find_strategy(strategy) #:nodoc:
|
|
31
|
+
registry.fetch(strategy, default_strategy).instance
|
|
32
|
+
end
|
|
33
|
+
end
|
|
6
34
|
|
|
35
|
+
# Currently supported strategies:
|
|
36
|
+
# * Leaky bucket algorithm
|
|
7
37
|
@@registry = {
|
|
8
38
|
:leaky_bucket => ::FineTune::Strategies::LeakyBucket,
|
|
9
39
|
:sliding_window => ::FineTune::Strategies::SlidingWindow
|
|
10
40
|
}
|
|
11
41
|
@default_strategy = ::FineTune::Strategies::LeakyBucket
|
|
12
42
|
|
|
43
|
+
# Calling throttle represents an occurance of an event.
|
|
44
|
+
# Depends on the strategy used for the actual calculations.
|
|
45
|
+
# Returns true if the event should be throttled.
|
|
46
|
+
#
|
|
47
|
+
# ==== Attributes
|
|
48
|
+
#
|
|
49
|
+
# * +name+ - Name of the rule ie: emails_sent
|
|
50
|
+
# * +id+ - Identifier for a specific resource ie: abc@example.com
|
|
51
|
+
# * +options+ - Options for the strategy. Depends on the strategy choosen
|
|
52
|
+
#
|
|
53
|
+
# ==== Options
|
|
54
|
+
#
|
|
55
|
+
# * +:strategy+ - The strategy chosen. Valid options are:
|
|
56
|
+
# - leaky_bucket
|
|
57
|
+
#
|
|
58
|
+
# Optionally, a block can be passed and it will be called with
|
|
59
|
+
# calculated values
|
|
60
|
+
#
|
|
61
|
+
# ==== Examples
|
|
62
|
+
# FineTune.throttle(:emails_sent, "abc@example.com",
|
|
63
|
+
# {strategy: :leaky_bucket}) do |count, comparison, key, strategy, options|
|
|
64
|
+
# # some calculation ...
|
|
65
|
+
# end
|
|
66
|
+
|
|
13
67
|
def throttle(name, id, options)
|
|
14
68
|
strategy, key, options = current_strategy(name, id, options)
|
|
15
69
|
count = strategy.increment(key, options)
|
|
@@ -20,46 +74,72 @@ module FineTune
|
|
|
20
74
|
comp >= 0
|
|
21
75
|
end
|
|
22
76
|
|
|
23
|
-
|
|
24
|
-
|
|
77
|
+
# The more forceful variant of throttle. Will raise a MaxRateError if the
|
|
78
|
+
# defined threshold is exceeds.
|
|
79
|
+
#
|
|
80
|
+
# Attributes and options are same as for +throttle+.
|
|
25
81
|
|
|
26
|
-
|
|
27
|
-
|
|
82
|
+
def throttle!(name, id, options)
|
|
83
|
+
throttle(name, id, options) do |count, comp, key, strategy, opts|
|
|
84
|
+
yield count, comp, key, strategy, opts if block_given?
|
|
28
85
|
|
|
29
|
-
raise MaxRateError.new(key, count, comp, strategy,
|
|
86
|
+
raise MaxRateError.new(key, count, comp, strategy, opts) if comp >= 0
|
|
30
87
|
end
|
|
31
88
|
end
|
|
32
89
|
|
|
90
|
+
# Returns true if current count exceeds the given limits.
|
|
91
|
+
#
|
|
92
|
+
# Attributes and options are same as for +throttle+.
|
|
33
93
|
def rate_exceeded?(name, id, options)
|
|
34
94
|
strategy, key, options = current_strategy(name, id, options)
|
|
35
95
|
strategy.compare?(strategy.count(key, options), options) >= 0
|
|
36
96
|
end
|
|
37
97
|
|
|
98
|
+
# Returns the current event count.
|
|
99
|
+
#
|
|
100
|
+
# Attributes and options are same as for +throttle+.
|
|
38
101
|
def count(name, id, options)
|
|
39
102
|
strategy, key, options = current_strategy(name, id, options)
|
|
40
103
|
strategy.count(key, options)
|
|
41
104
|
end
|
|
42
105
|
|
|
106
|
+
# Resets the counter for the given resource identifer to 0.
|
|
107
|
+
#
|
|
108
|
+
# Attributes and options are same as for +throttle+.
|
|
43
109
|
def reset(name, id, options)
|
|
44
110
|
strategy, key, options = current_strategy(name, id, options)
|
|
45
111
|
strategy.reset(key, options)
|
|
46
112
|
end
|
|
47
113
|
|
|
114
|
+
# Adds a preconfigured rule and options to the rule. These configs
|
|
115
|
+
# can be overridden by passing different option values later when
|
|
116
|
+
# calling +throttle+.
|
|
117
|
+
#
|
|
118
|
+
# ==== Attributes
|
|
119
|
+
#
|
|
120
|
+
# * +name+ - Name of the rule ie: emails_sent
|
|
121
|
+
# * +options+ - Default options for the rule
|
|
122
|
+
#
|
|
123
|
+
# ==== Examples
|
|
124
|
+
# FineTune.add_limit(:emails_sent_per_hour, {window: 3600})
|
|
125
|
+
#
|
|
48
126
|
def add_limit(name, options = {})
|
|
49
127
|
limits[name] ||= {}
|
|
50
128
|
limits[name].merge!(options)
|
|
51
129
|
end
|
|
52
130
|
|
|
131
|
+
# Removes a preconfigured rule and options.
|
|
53
132
|
def remove_limit(name)
|
|
54
133
|
limits.delete(name)
|
|
55
134
|
end
|
|
56
135
|
|
|
136
|
+
# Returns all default limits defined
|
|
57
137
|
def limits
|
|
58
138
|
@limits ||= {}
|
|
59
139
|
end
|
|
60
140
|
|
|
61
141
|
private
|
|
62
|
-
def current_strategy(name, id, options)
|
|
142
|
+
def current_strategy(name, id, options) #:nodoc:
|
|
63
143
|
options = (limits[name] || {}).merge(options)
|
|
64
144
|
strategy = self.class.find_strategy(options[:strategy])
|
|
65
145
|
|
|
@@ -71,13 +151,5 @@ module FineTune
|
|
|
71
151
|
|
|
72
152
|
[strategy, key, options]
|
|
73
153
|
end
|
|
74
|
-
|
|
75
|
-
def self.find_strategy(strategy)
|
|
76
|
-
registry.fetch(strategy, default_strategy).instance
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def self.registry
|
|
80
|
-
@@registry
|
|
81
|
-
end
|
|
82
154
|
end
|
|
83
155
|
end
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
module FineTune
|
|
2
2
|
module Strategies
|
|
3
|
+
# This class is only for the purpose of extending and implementing
|
|
4
|
+
# own strategies. Any subclass should implement:
|
|
5
|
+
# * +identifier+
|
|
6
|
+
# * +increment+
|
|
7
|
+
# * +count+
|
|
8
|
+
# * +reset+
|
|
3
9
|
class Base
|
|
4
10
|
include Singleton
|
|
5
11
|
|
|
@@ -8,7 +14,15 @@ module FineTune
|
|
|
8
14
|
end
|
|
9
15
|
|
|
10
16
|
def build_key(name, id, options)
|
|
11
|
-
[name, id].flatten.join('
|
|
17
|
+
[:fine_tune, name, identifier, id].flatten.join('/')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def adapter
|
|
21
|
+
FineTune.adapter
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def identifier
|
|
25
|
+
raise "not defined"
|
|
12
26
|
end
|
|
13
27
|
|
|
14
28
|
def increment(key, options)
|
|
@@ -20,7 +34,11 @@ module FineTune
|
|
|
20
34
|
end
|
|
21
35
|
|
|
22
36
|
def validate?(options)
|
|
23
|
-
|
|
37
|
+
if adapter.nil?
|
|
38
|
+
raise "no adapter given"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
true
|
|
24
42
|
end
|
|
25
43
|
|
|
26
44
|
def reset(key, options)
|
|
@@ -1,6 +1,132 @@
|
|
|
1
1
|
module FineTune
|
|
2
2
|
module Strategies
|
|
3
|
+
|
|
4
|
+
# Implements {LeakyBucket algorithm}[https://en.wikipedia.org/wiki/Leaky_bucket]
|
|
5
|
+
# as a FineTune::Strategy.
|
|
3
6
|
class LeakyBucket < Base
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
# Set default values for window, average and maximum values so
|
|
10
|
+
# they won't need to be passed in every call
|
|
11
|
+
attr_accessor :default_window, :default_average, :default_maximum
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# compares the count given and the burst value.
|
|
15
|
+
# Returns 0 if equal, -1 less and 1 greater
|
|
16
|
+
#
|
|
17
|
+
# ====== Attributes
|
|
18
|
+
#
|
|
19
|
+
# * +count+ - the current count
|
|
20
|
+
# * +options+ - Optional if default_maximum is set
|
|
21
|
+
#
|
|
22
|
+
# ====== Options
|
|
23
|
+
# * +:maximum - can be used to override default_maximum
|
|
24
|
+
def compare?(count, options)
|
|
25
|
+
count <=> maximum(options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns the current count incremented by one if it does not exceed the
|
|
29
|
+
# maximum value given. If it exceeds the maximum value, then it returns
|
|
30
|
+
# the maximum value.
|
|
31
|
+
#
|
|
32
|
+
# ====== Attributes
|
|
33
|
+
#
|
|
34
|
+
# * +key+ - the identifier for the resource in cache store
|
|
35
|
+
# * +options+ - Optional if default_maximum, default_window and default_average set
|
|
36
|
+
#
|
|
37
|
+
# ====== Options
|
|
38
|
+
# * +:maximum+ - can be used to override default_maximum ie: 20
|
|
39
|
+
# * +:average+ - can be used to override default_average ie: 10
|
|
40
|
+
# * +:window+ - can be used to override default_window ie: 3600 (one hour)
|
|
41
|
+
def increment(key, options)
|
|
42
|
+
count = count(key, options)
|
|
43
|
+
count = [maximum(options), count + 1].min
|
|
44
|
+
adapter.write(key, {count: count, timestamp: Time.now.to_i})
|
|
45
|
+
count
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns the current count after discounting for the time since
|
|
49
|
+
# last access.
|
|
50
|
+
#
|
|
51
|
+
# Attributes and options are same as +increment+
|
|
52
|
+
def count(key, options)
|
|
53
|
+
resource = adapter.fetch(key, options) do
|
|
54
|
+
zero_counts
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
loss = loss(Time.now.to_i, resource[:timestamp], average(options),
|
|
58
|
+
window(options))
|
|
59
|
+
count = resource[:count] - loss
|
|
60
|
+
count > 0 ? count : 0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Validates the options given. Window, average and maximum values must be set
|
|
64
|
+
# either through default values or the options
|
|
65
|
+
#
|
|
66
|
+
# Options are same as +increment+
|
|
67
|
+
def validate?(options)
|
|
68
|
+
super(options)
|
|
69
|
+
error = nil
|
|
70
|
+
window, average, maximum = window(options), average(options), maximum(options)
|
|
71
|
+
|
|
72
|
+
error = if !window || !average || !maximum
|
|
73
|
+
"window, average and maximum options are required"
|
|
74
|
+
elsif !positive_integer?(window)
|
|
75
|
+
"time window must be a positive integer"
|
|
76
|
+
elsif !non_negative_numeric?(average)
|
|
77
|
+
"average should be non negative numbers"
|
|
78
|
+
elsif maximum < average
|
|
79
|
+
"maximum should not be less than the average"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
raise ArgumentError.new(error) if error
|
|
83
|
+
|
|
84
|
+
true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Reset the count of the resource identified by the key to 0.
|
|
88
|
+
#
|
|
89
|
+
# ====== Attributes
|
|
90
|
+
#
|
|
91
|
+
# * +key+ - the identifier for the resource in cache store
|
|
92
|
+
def reset(key, options)
|
|
93
|
+
adapter.write(key, zero_counts)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def identifier #:nodoc:
|
|
97
|
+
:leaky_bucket
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# calculates the loss given an average, window and a time interval
|
|
101
|
+
def loss(now, last_accessed, average, window)
|
|
102
|
+
loss = ((now - last_accessed) * average).to_f/window
|
|
103
|
+
loss > 0 ? loss : 0
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
def zero_counts #:nodoc:
|
|
108
|
+
{count: 0, timestamp: Time.now.to_i}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def window(options) #:nodoc:
|
|
112
|
+
options[:window] || self.class.default_window
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def average(options) #:nodoc:
|
|
116
|
+
options[:average] || self.class.default_average
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def maximum(options) #:nodoc:
|
|
120
|
+
options[:maximum] || options[:limit] || self.class.default_maximum
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def positive_integer?(e) #:nodoc:
|
|
124
|
+
e.is_a?(Integer) && e > 0
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def non_negative_numeric?(e) #:nodoc:
|
|
128
|
+
e.is_a?(Numeric) && e >= 0
|
|
129
|
+
end
|
|
4
130
|
end
|
|
5
131
|
end
|
|
6
132
|
end
|
data/lib/fine_tune/version.rb
CHANGED
data/lib/fine_tune.rb
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Author:: Laknath Semage (blaknath at gmail dot com)
|
|
3
|
+
# Copyright:: Copyright (c) 2016 Vesess Inc.
|
|
4
|
+
# License:: MIT License
|
|
5
|
+
|
|
1
6
|
require "fine_tune/version"
|
|
2
7
|
require 'singleton'
|
|
3
8
|
require "forwardable"
|
|
@@ -8,11 +13,18 @@ end
|
|
|
8
13
|
require "fine_tune/base"
|
|
9
14
|
require "fine_tune/max_rate_error"
|
|
10
15
|
|
|
16
|
+
##
|
|
17
|
+
# FineTune helps throttle any kind of event sequence. It also supports
|
|
18
|
+
# custom throttling strategies using a common API. By default
|
|
19
|
+
# it supports LeakyBucket[https://en.wikipedia.org/wiki/Leaky_bucket] algorithm.
|
|
20
|
+
|
|
11
21
|
module FineTune
|
|
12
22
|
class << self
|
|
13
23
|
extend Forwardable
|
|
14
|
-
def_delegators :"FineTune::Base.instance", :
|
|
15
|
-
:
|
|
24
|
+
def_delegators :"FineTune::Base.instance", :count, :reset, :throttle,
|
|
25
|
+
:throttle!, :add_limit, :remove_limit, :rate_exceeded?
|
|
26
|
+
def_delegators :"FineTune::Base", :adapter, :adapter=, :default_strategy,
|
|
27
|
+
:default_strategy=
|
|
16
28
|
end
|
|
17
29
|
end
|
|
18
30
|
|
metadata
CHANGED
|
@@ -1,85 +1,112 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fine_tune
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
prerelease:
|
|
5
6
|
platform: ruby
|
|
6
7
|
authors:
|
|
7
8
|
- Laknath Semage
|
|
8
9
|
autorequire:
|
|
9
10
|
bindir: bin
|
|
10
11
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
12
|
+
date: 2016-07-04 00:00:00.000000000 Z
|
|
12
13
|
dependencies:
|
|
13
14
|
- !ruby/object:Gem::Dependency
|
|
14
15
|
name: bundler
|
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
16
18
|
requirements:
|
|
17
|
-
- -
|
|
19
|
+
- - ! '>='
|
|
18
20
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
21
|
+
version: '0'
|
|
20
22
|
type: :development
|
|
21
23
|
prerelease: false
|
|
22
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
23
26
|
requirements:
|
|
24
|
-
- -
|
|
27
|
+
- - ! '>='
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
29
|
+
version: '0'
|
|
27
30
|
- !ruby/object:Gem::Dependency
|
|
28
31
|
name: rake
|
|
29
32
|
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
30
34
|
requirements:
|
|
31
|
-
- -
|
|
35
|
+
- - ! '>='
|
|
32
36
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
37
|
+
version: '0'
|
|
34
38
|
type: :development
|
|
35
39
|
prerelease: false
|
|
36
40
|
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
37
42
|
requirements:
|
|
38
|
-
- -
|
|
43
|
+
- - ! '>='
|
|
39
44
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
45
|
+
version: '0'
|
|
41
46
|
- !ruby/object:Gem::Dependency
|
|
42
47
|
name: minitest
|
|
43
48
|
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
44
50
|
requirements:
|
|
45
|
-
- -
|
|
51
|
+
- - ~>
|
|
46
52
|
- !ruby/object:Gem::Version
|
|
47
53
|
version: '5.0'
|
|
48
54
|
type: :development
|
|
49
55
|
prerelease: false
|
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
51
58
|
requirements:
|
|
52
|
-
- -
|
|
59
|
+
- - ~>
|
|
53
60
|
- !ruby/object:Gem::Version
|
|
54
61
|
version: '5.0'
|
|
55
62
|
- !ruby/object:Gem::Dependency
|
|
56
63
|
name: minitest-reporters
|
|
57
64
|
requirement: !ruby/object:Gem::Requirement
|
|
65
|
+
none: false
|
|
58
66
|
requirements:
|
|
59
|
-
- -
|
|
67
|
+
- - ~>
|
|
60
68
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: 1.1
|
|
69
|
+
version: '1.1'
|
|
62
70
|
type: :development
|
|
63
71
|
prerelease: false
|
|
64
72
|
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
65
74
|
requirements:
|
|
66
|
-
- -
|
|
75
|
+
- - ~>
|
|
67
76
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: 1.1
|
|
77
|
+
version: '1.1'
|
|
69
78
|
- !ruby/object:Gem::Dependency
|
|
70
79
|
name: mocha
|
|
71
80
|
requirement: !ruby/object:Gem::Requirement
|
|
81
|
+
none: false
|
|
72
82
|
requirements:
|
|
73
|
-
- -
|
|
83
|
+
- - ~>
|
|
74
84
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 0.14
|
|
85
|
+
version: '0.14'
|
|
76
86
|
type: :development
|
|
77
87
|
prerelease: false
|
|
78
88
|
version_requirements: !ruby/object:Gem::Requirement
|
|
89
|
+
none: false
|
|
79
90
|
requirements:
|
|
80
|
-
- -
|
|
91
|
+
- - ~>
|
|
81
92
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 0.14
|
|
93
|
+
version: '0.14'
|
|
94
|
+
- !ruby/object:Gem::Dependency
|
|
95
|
+
name: activesupport
|
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
|
97
|
+
none: false
|
|
98
|
+
requirements:
|
|
99
|
+
- - ~>
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '4.2'
|
|
102
|
+
type: :development
|
|
103
|
+
prerelease: false
|
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
+
none: false
|
|
106
|
+
requirements:
|
|
107
|
+
- - ~>
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '4.2'
|
|
83
110
|
description: A rate limiter without the worst case throttling of two times the defined
|
|
84
111
|
threshold.
|
|
85
112
|
email:
|
|
@@ -88,8 +115,9 @@ executables: []
|
|
|
88
115
|
extensions: []
|
|
89
116
|
extra_rdoc_files: []
|
|
90
117
|
files:
|
|
91
|
-
-
|
|
92
|
-
-
|
|
118
|
+
- .gitignore
|
|
119
|
+
- .ruby-version
|
|
120
|
+
- .travis.yml
|
|
93
121
|
- Gemfile
|
|
94
122
|
- LICENSE.txt
|
|
95
123
|
- README.md
|
|
@@ -105,25 +133,26 @@ files:
|
|
|
105
133
|
homepage: https://github.com/vesess/fine_tune
|
|
106
134
|
licenses:
|
|
107
135
|
- MIT
|
|
108
|
-
metadata: {}
|
|
109
136
|
post_install_message:
|
|
110
137
|
rdoc_options: []
|
|
111
138
|
require_paths:
|
|
112
139
|
- lib
|
|
113
140
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
141
|
+
none: false
|
|
114
142
|
requirements:
|
|
115
|
-
- -
|
|
143
|
+
- - ! '>='
|
|
116
144
|
- !ruby/object:Gem::Version
|
|
117
145
|
version: '0'
|
|
118
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
|
+
none: false
|
|
119
148
|
requirements:
|
|
120
|
-
- -
|
|
149
|
+
- - ! '>='
|
|
121
150
|
- !ruby/object:Gem::Version
|
|
122
151
|
version: '0'
|
|
123
152
|
requirements: []
|
|
124
153
|
rubyforge_project:
|
|
125
|
-
rubygems_version:
|
|
154
|
+
rubygems_version: 1.8.23
|
|
126
155
|
signing_key:
|
|
127
|
-
specification_version:
|
|
156
|
+
specification_version: 3
|
|
128
157
|
summary: Limits rate of a given event sequence with defined strategies.
|
|
129
158
|
test_files: []
|
checksums.yaml
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
SHA1:
|
|
3
|
-
metadata.gz: 084b2f0a33fb235fae26629c06c4d3ea49832456
|
|
4
|
-
data.tar.gz: 36428f60b499e91fca15c2208edbb93b2ae21ec2
|
|
5
|
-
SHA512:
|
|
6
|
-
metadata.gz: ef657f44b8ccc3516998568101b3192103f0591a81171875bf626718137d88ae2a79c804ac47893fa0d42abad4f34cebf3ee0dcad12db9ebfc52c2ed9d57d130
|
|
7
|
-
data.tar.gz: e41b1821a9e08b4d3323e7fc52a66ce353a3669946444c8316997a2988e5bca99dc0f93ef807f9a3c5e1131e7a98d4367f1555a6d2d2f6fb21829f304c9f3367
|