glutton_ratelimit 0.1.0 → 0.2.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/.document +5 -5
- data/LICENSE +23 -23
- data/README.rdoc +129 -124
- data/Rakefile +52 -52
- data/VERSION +1 -1
- data/examples/limit_instance_methods.rb +33 -33
- data/examples/simple_manual.rb +31 -29
- data/glutton_ratelimit.gemspec +64 -64
- data/lib/glutton_ratelimit.rb +38 -29
- data/lib/glutton_ratelimit/averaged_throttle.rb +44 -44
- data/lib/glutton_ratelimit/bursty_ring_buffer.rb +24 -24
- data/lib/glutton_ratelimit/bursty_token_bucket.rb +26 -26
- data/test/helper.rb +20 -21
- data/test/test_glutton_ratelimit_averaged_throttle.rb +15 -15
- data/test/test_glutton_ratelimit_bursty_ring_buffer.rb +15 -15
- data/test/test_glutton_ratelimit_bursty_token_bucket.rb +15 -15
- data/test/testing_module.rb +69 -67
- metadata +14 -18
- data/.gitignore +0 -21
data/examples/simple_manual.rb
CHANGED
@@ -1,30 +1,32 @@
|
|
1
|
-
|
2
|
-
require '
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
1
|
+
# $LOAD_PATH << File.dirname(__FILE__) +'/../lib'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'glutton_ratelimit'
|
4
|
+
|
5
|
+
puts "Maximum of 12 executions every 5 seconds (Bursty):"
|
6
|
+
rl = GluttonRatelimit::BurstyTokenBucket.new 12, 5
|
7
|
+
|
8
|
+
start = Time.now
|
9
|
+
n = 0
|
10
|
+
|
11
|
+
rl.times(25) do
|
12
|
+
puts "#{n += 1} - #{Time.now - start}"
|
13
|
+
# Simulating a constant-time task:
|
14
|
+
sleep 0.1
|
15
|
+
end
|
16
|
+
|
17
|
+
# The 25th execution should occur after 10 seconds has elapsed.
|
18
|
+
|
19
|
+
puts "Maximum of 3 executions every 3 seconds (Averaged):"
|
20
|
+
rl = GluttonRatelimit::AveragedThrottle.new 3, 3
|
21
|
+
# AverageThrottle will attempt to evenly space executions within the allowed period.
|
22
|
+
|
23
|
+
start = Time.now
|
24
|
+
n = 0
|
25
|
+
|
26
|
+
rl.times(7) do
|
27
|
+
puts "#{n += 1} - #{Time.now - start}"
|
28
|
+
# Simulating a 0 to 1 second random-time task:
|
29
|
+
sleep rand
|
30
|
+
end
|
31
|
+
|
30
32
|
# The 7th execution should occur after 6 seconds has elapsed.
|
data/glutton_ratelimit.gemspec
CHANGED
@@ -1,64 +1,64 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{glutton_ratelimit}
|
8
|
-
s.version = "0.1.0"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Wally Glutton"]
|
12
|
-
s.date = %q{2010-06-10}
|
13
|
-
s.description = %q{A Ruby library for limiting the number of times a method can be invoked within a specified time period.}
|
14
|
-
s.email = %q{stungeye@gmail.com}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
".gitignore",
|
22
|
-
"LICENSE",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION",
|
26
|
-
"examples/limit_instance_methods.rb",
|
27
|
-
"examples/simple_manual.rb",
|
28
|
-
"glutton_ratelimit.gemspec",
|
29
|
-
"lib/glutton_ratelimit.rb",
|
30
|
-
"lib/glutton_ratelimit/averaged_throttle.rb",
|
31
|
-
"lib/glutton_ratelimit/bursty_ring_buffer.rb",
|
32
|
-
"lib/glutton_ratelimit/bursty_token_bucket.rb",
|
33
|
-
"test/helper.rb",
|
34
|
-
"test/test_glutton_ratelimit_averaged_throttle.rb",
|
35
|
-
"test/test_glutton_ratelimit_bursty_ring_buffer.rb",
|
36
|
-
"test/test_glutton_ratelimit_bursty_token_bucket.rb",
|
37
|
-
"test/testing_module.rb"
|
38
|
-
]
|
39
|
-
s.homepage = %q{http://github.com/stungeye/glutton_ratelimit}
|
40
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
41
|
-
s.require_paths = ["lib"]
|
42
|
-
s.rubygems_version = %q{1.3.6}
|
43
|
-
s.summary = %q{Simple Ruby library for self-imposed rater-limiting.}
|
44
|
-
s.test_files = [
|
45
|
-
"test/test_glutton_ratelimit_averaged_throttle.rb",
|
46
|
-
"test/helper.rb",
|
47
|
-
"test/test_glutton_ratelimit_bursty_ring_buffer.rb",
|
48
|
-
"test/testing_module.rb",
|
49
|
-
"test/test_glutton_ratelimit_bursty_token_bucket.rb",
|
50
|
-
"examples/limit_instance_methods.rb",
|
51
|
-
"examples/simple_manual.rb"
|
52
|
-
]
|
53
|
-
|
54
|
-
if s.respond_to? :specification_version then
|
55
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
56
|
-
s.specification_version = 3
|
57
|
-
|
58
|
-
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
59
|
-
else
|
60
|
-
end
|
61
|
-
else
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{glutton_ratelimit}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Wally Glutton"]
|
12
|
+
s.date = %q{2010-06-10}
|
13
|
+
s.description = %q{A Ruby library for limiting the number of times a method can be invoked within a specified time period.}
|
14
|
+
s.email = %q{stungeye@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"examples/limit_instance_methods.rb",
|
27
|
+
"examples/simple_manual.rb",
|
28
|
+
"glutton_ratelimit.gemspec",
|
29
|
+
"lib/glutton_ratelimit.rb",
|
30
|
+
"lib/glutton_ratelimit/averaged_throttle.rb",
|
31
|
+
"lib/glutton_ratelimit/bursty_ring_buffer.rb",
|
32
|
+
"lib/glutton_ratelimit/bursty_token_bucket.rb",
|
33
|
+
"test/helper.rb",
|
34
|
+
"test/test_glutton_ratelimit_averaged_throttle.rb",
|
35
|
+
"test/test_glutton_ratelimit_bursty_ring_buffer.rb",
|
36
|
+
"test/test_glutton_ratelimit_bursty_token_bucket.rb",
|
37
|
+
"test/testing_module.rb"
|
38
|
+
]
|
39
|
+
s.homepage = %q{http://github.com/stungeye/glutton_ratelimit}
|
40
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = %q{1.3.6}
|
43
|
+
s.summary = %q{Simple Ruby library for self-imposed rater-limiting.}
|
44
|
+
s.test_files = [
|
45
|
+
"test/test_glutton_ratelimit_averaged_throttle.rb",
|
46
|
+
"test/helper.rb",
|
47
|
+
"test/test_glutton_ratelimit_bursty_ring_buffer.rb",
|
48
|
+
"test/testing_module.rb",
|
49
|
+
"test/test_glutton_ratelimit_bursty_token_bucket.rb",
|
50
|
+
"examples/limit_instance_methods.rb",
|
51
|
+
"examples/simple_manual.rb"
|
52
|
+
]
|
53
|
+
|
54
|
+
if s.respond_to? :specification_version then
|
55
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
56
|
+
s.specification_version = 3
|
57
|
+
|
58
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
59
|
+
else
|
60
|
+
end
|
61
|
+
else
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/lib/glutton_ratelimit.rb
CHANGED
@@ -1,29 +1,38 @@
|
|
1
|
-
module GluttonRatelimit
|
2
|
-
|
3
|
-
def rate_limit symbol, executions, time_period, rl_class = AveragedThrottle
|
4
|
-
rl = rl_class.new executions, time_period
|
5
|
-
old_symbol = "#{symbol}_old".to_sym
|
6
|
-
alias_method old_symbol, symbol
|
7
|
-
define_method symbol do |*args|
|
8
|
-
rl.wait
|
9
|
-
self.send old_symbol, *args
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
# All the other classes extend this parent and are therefore
|
15
|
-
# constructed in the same manner.
|
16
|
-
class ParentLimiter
|
17
|
-
attr_reader :executions
|
18
|
-
|
19
|
-
def initialize executions, time_period
|
20
|
-
@executions = executions
|
21
|
-
@time_period = time_period
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
1
|
+
module GluttonRatelimit
|
2
|
+
|
3
|
+
def rate_limit symbol, executions, time_period, rl_class = AveragedThrottle
|
4
|
+
rl = rl_class.new executions, time_period
|
5
|
+
old_symbol = "#{symbol}_old".to_sym
|
6
|
+
alias_method old_symbol, symbol
|
7
|
+
define_method symbol do |*args|
|
8
|
+
rl.wait
|
9
|
+
self.send old_symbol, *args
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
# All the other classes extend this parent and are therefore
|
15
|
+
# constructed in the same manner.
|
16
|
+
class ParentLimiter
|
17
|
+
attr_reader :executions
|
18
|
+
|
19
|
+
def initialize executions, time_period
|
20
|
+
@executions = executions
|
21
|
+
@time_period = time_period
|
22
|
+
end
|
23
|
+
|
24
|
+
def times(num, &block)
|
25
|
+
raise ArgumentError, "Code block expected" if not block
|
26
|
+
raise ArgumentError, "Parameter expected to be Fixnum but found a #{num.class}." unless num.class.equal?(Fixnum)
|
27
|
+
num.times do
|
28
|
+
wait
|
29
|
+
yield
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
36
|
+
require File.join(dir, "glutton_ratelimit", "bursty_ring_buffer")
|
37
|
+
require File.join(dir, "glutton_ratelimit", "bursty_token_bucket")
|
38
|
+
require File.join(dir, "glutton_ratelimit", "averaged_throttle")
|
@@ -1,44 +1,44 @@
|
|
1
|
-
module GluttonRatelimit
|
2
|
-
|
3
|
-
class AveragedThrottle < ParentLimiter
|
4
|
-
|
5
|
-
def reset_bucket
|
6
|
-
@before_previous_execution = Time.now if @before_previous_execution.nil?
|
7
|
-
@oldest_timestamp = Time.now
|
8
|
-
@tokens = @executions
|
9
|
-
@total_task_time = 0
|
10
|
-
end
|
11
|
-
|
12
|
-
def executed_this_period
|
13
|
-
@executions - @tokens
|
14
|
-
end
|
15
|
-
|
16
|
-
def average_task_time
|
17
|
-
@total_task_time.to_f / executed_this_period
|
18
|
-
end
|
19
|
-
|
20
|
-
def wait
|
21
|
-
reset_bucket if @tokens.nil?
|
22
|
-
|
23
|
-
now = Time.now
|
24
|
-
delta_since_previous = now - @before_previous_execution
|
25
|
-
@total_task_time += delta_since_previous
|
26
|
-
remaining_time = @time_period - (now - @oldest_timestamp)
|
27
|
-
|
28
|
-
if @tokens.zero?
|
29
|
-
sleep(remaining_time) if remaining_time > 0
|
30
|
-
reset_bucket
|
31
|
-
elsif executed_this_period != 0
|
32
|
-
throttle = (remaining_time.to_f + delta_since_previous) / (@tokens+1) - average_task_time
|
33
|
-
sleep throttle if throttle > 0
|
34
|
-
end
|
35
|
-
|
36
|
-
@tokens -= 1
|
37
|
-
@before_previous_execution = Time.now
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
|
1
|
+
module GluttonRatelimit
|
2
|
+
|
3
|
+
class AveragedThrottle < ParentLimiter
|
4
|
+
|
5
|
+
def reset_bucket
|
6
|
+
@before_previous_execution = Time.now if @before_previous_execution.nil?
|
7
|
+
@oldest_timestamp = Time.now
|
8
|
+
@tokens = @executions
|
9
|
+
@total_task_time = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def executed_this_period
|
13
|
+
@executions - @tokens
|
14
|
+
end
|
15
|
+
|
16
|
+
def average_task_time
|
17
|
+
@total_task_time.to_f / executed_this_period
|
18
|
+
end
|
19
|
+
|
20
|
+
def wait
|
21
|
+
reset_bucket if @tokens.nil?
|
22
|
+
|
23
|
+
now = Time.now
|
24
|
+
delta_since_previous = now - @before_previous_execution
|
25
|
+
@total_task_time += delta_since_previous
|
26
|
+
remaining_time = @time_period - (now - @oldest_timestamp)
|
27
|
+
|
28
|
+
if @tokens.zero?
|
29
|
+
sleep(remaining_time) if remaining_time > 0
|
30
|
+
reset_bucket
|
31
|
+
elsif executed_this_period != 0
|
32
|
+
throttle = (remaining_time.to_f + delta_since_previous) / (@tokens+1) - average_task_time
|
33
|
+
sleep throttle if throttle > 0
|
34
|
+
end
|
35
|
+
|
36
|
+
@tokens -= 1
|
37
|
+
@before_previous_execution = Time.now
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
|
@@ -1,24 +1,24 @@
|
|
1
|
-
module GluttonRatelimit
|
2
|
-
|
3
|
-
class BurstyRingBuffer < ParentLimiter
|
4
|
-
|
5
|
-
def oldest_timestamp
|
6
|
-
@timestamps = Array.new executions, (Time.now - @time_period) if @timestamps.nil?
|
7
|
-
@timestamps[0]
|
8
|
-
end
|
9
|
-
|
10
|
-
def current_timestamp= new_stamp
|
11
|
-
@timestamps.push(new_stamp).shift
|
12
|
-
end
|
13
|
-
|
14
|
-
def wait
|
15
|
-
delta = Time.now - oldest_timestamp
|
16
|
-
sleep(@time_period - delta) if delta < @time_period
|
17
|
-
self.current_timestamp = Time.now
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
|
1
|
+
module GluttonRatelimit
|
2
|
+
|
3
|
+
class BurstyRingBuffer < ParentLimiter
|
4
|
+
|
5
|
+
def oldest_timestamp
|
6
|
+
@timestamps = Array.new executions, (Time.now - @time_period) if @timestamps.nil?
|
7
|
+
@timestamps[0]
|
8
|
+
end
|
9
|
+
|
10
|
+
def current_timestamp= new_stamp
|
11
|
+
@timestamps.push(new_stamp).shift
|
12
|
+
end
|
13
|
+
|
14
|
+
def wait
|
15
|
+
delta = Time.now - oldest_timestamp
|
16
|
+
sleep(@time_period - delta) if delta < @time_period
|
17
|
+
self.current_timestamp = Time.now
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
@@ -1,26 +1,26 @@
|
|
1
|
-
module GluttonRatelimit
|
2
|
-
|
3
|
-
class BurstyTokenBucket < ParentLimiter
|
4
|
-
|
5
|
-
def reset_bucket
|
6
|
-
@oldest_timestamp = Time.now
|
7
|
-
@tokens = @executions
|
8
|
-
end
|
9
|
-
|
10
|
-
def wait
|
11
|
-
reset_bucket if @tokens.nil?
|
12
|
-
|
13
|
-
if @tokens.zero?
|
14
|
-
delta = Time.now - @oldest_timestamp
|
15
|
-
sleep(@time_period - delta) if delta < @time_period
|
16
|
-
reset_bucket
|
17
|
-
end
|
18
|
-
|
19
|
-
@tokens -= 1
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
|
1
|
+
module GluttonRatelimit
|
2
|
+
|
3
|
+
class BurstyTokenBucket < ParentLimiter
|
4
|
+
|
5
|
+
def reset_bucket
|
6
|
+
@oldest_timestamp = Time.now
|
7
|
+
@tokens = @executions
|
8
|
+
end
|
9
|
+
|
10
|
+
def wait
|
11
|
+
reset_bucket if @tokens.nil?
|
12
|
+
|
13
|
+
if @tokens.zero?
|
14
|
+
delta = Time.now - @oldest_timestamp
|
15
|
+
sleep(@time_period - delta) if delta < @time_period
|
16
|
+
reset_bucket
|
17
|
+
end
|
18
|
+
|
19
|
+
@tokens -= 1
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|