throttler 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +18 -9
- data/lib/throttler/throttle.rb +31 -0
- data/lib/throttler.rb +17 -17
- data/spec/fixtures/foo.rb +3 -3
- data/spec/{throttler_spec.rb → integration/scenarios_spec.rb} +28 -15
- data/spec/unit/throttler/throttle_spec.rb +66 -0
- data/spec/unit/throttler_spec.rb +36 -0
- metadata +11 -8
data/README.md
CHANGED
@@ -5,22 +5,31 @@ Throttler
|
|
5
5
|
|
6
6
|
Throttle concurrency.
|
7
7
|
|
8
|
-
|
8
|
+
class Foo
|
9
|
+
include Throttler
|
10
|
+
|
11
|
+
def request
|
12
|
+
throttle("foo#{interface}", 1.0) do
|
13
|
+
#noop
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def interface; "eth0"; end
|
18
|
+
end
|
19
|
+
|
20
|
+
foo, count = Foo.new, 0
|
9
21
|
|
10
22
|
100.times do
|
11
23
|
Thread.new do
|
12
|
-
|
13
|
-
|
14
|
-
count += 1
|
15
|
-
end
|
16
|
-
end
|
24
|
+
foo.request
|
25
|
+
count += 1
|
17
26
|
end
|
18
27
|
end
|
19
|
-
sleep 2.2
|
20
28
|
|
21
|
-
|
29
|
+
sleep 1.2
|
30
|
+
count.should eql 2 # 98 threads pending
|
22
31
|
|
23
32
|
Use case
|
24
33
|
--------
|
25
34
|
|
26
|
-
Imagine multiple workers hitting the Amazon API on the same IP address.
|
35
|
+
Imagine multiple workers hitting the Amazon API on the same IP address.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Throttler
|
2
|
+
|
3
|
+
# The Throttle file
|
4
|
+
class Throttle
|
5
|
+
def initialize(name)
|
6
|
+
path = "/tmp/.#{name}"
|
7
|
+
@file = File.open(path, File::RDWR|File::CREAT)
|
8
|
+
end
|
9
|
+
|
10
|
+
def delay(interval)
|
11
|
+
last = @file.gets.to_f
|
12
|
+
last = (Time.now.to_f - interval) if last == 0.0
|
13
|
+
|
14
|
+
sleep [last + interval - Time.now.to_f, 0.0].max
|
15
|
+
end
|
16
|
+
|
17
|
+
def lock
|
18
|
+
@file.flock(File::LOCK_EX)
|
19
|
+
end
|
20
|
+
|
21
|
+
def timestamp
|
22
|
+
@file.rewind
|
23
|
+
@file.write(Time.now.to_f)
|
24
|
+
end
|
25
|
+
|
26
|
+
def unlock
|
27
|
+
@file.flock(File::LOCK_UN)
|
28
|
+
@file.close
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/throttler.rb
CHANGED
@@ -1,26 +1,26 @@
|
|
1
|
-
|
1
|
+
require File.dirname(__FILE__) + "/throttler/throttle"
|
2
|
+
|
3
|
+
# The Throttler module.
|
4
|
+
#
|
5
|
+
# Simply include this in the class where you wish to use the throttler.
|
2
6
|
module Throttler
|
3
|
-
require "fileutils"
|
4
7
|
|
8
|
+
# Throttles execution of a block of code.
|
9
|
+
#
|
10
|
+
# Pass name of throttler and optionally, the interval between each
|
11
|
+
# moment of execution. Latte defaults to one second.
|
12
|
+
#
|
13
|
+
# throttle("foo") { some_code }
|
14
|
+
#
|
5
15
|
def throttle(name, interval=1.0)
|
6
16
|
begin
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
file = File.open(path, "r+")
|
12
|
-
file.flock(File::LOCK_EX)
|
13
|
-
|
14
|
-
last = file.gets.to_f || Time.now.to_f - interval
|
15
|
-
sleep [last + interval - Time.now.to_f, 0.0].max
|
16
|
-
|
17
|
+
file = Throttle.new(name)
|
18
|
+
file.lock
|
19
|
+
file.delay interval
|
17
20
|
yield if block_given?
|
18
|
-
|
19
|
-
file.rewind
|
20
|
-
file.write(Time.now.to_f)
|
21
|
+
file.timestamp
|
21
22
|
ensure
|
22
|
-
file.
|
23
|
-
file.close
|
23
|
+
file.unlock
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/spec/fixtures/foo.rb
CHANGED
@@ -2,7 +2,7 @@ require "benchmark"
|
|
2
2
|
require "fileutils"
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
class
|
5
|
+
class Bar
|
6
6
|
include Throttler
|
7
7
|
|
8
8
|
def bar
|
@@ -20,7 +20,7 @@ class Foo
|
|
20
20
|
def noop; end
|
21
21
|
end
|
22
22
|
|
23
|
-
describe
|
23
|
+
describe "Simple integration tests" do
|
24
24
|
before do
|
25
25
|
FileUtils.rm "/tmp/.bar", :force => true
|
26
26
|
FileUtils.rm "/tmp/.baz", :force => true
|
@@ -29,7 +29,7 @@ describe Throttler do
|
|
29
29
|
it "throttles a loop" do
|
30
30
|
time = Benchmark.realtime do
|
31
31
|
3.times do
|
32
|
-
|
32
|
+
Bar.new.bar
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -40,9 +40,9 @@ describe Throttler do
|
|
40
40
|
count = 0
|
41
41
|
threads = 10.times.collect do
|
42
42
|
Thread.new do
|
43
|
-
|
43
|
+
bar = Bar.new
|
44
44
|
loop do
|
45
|
-
|
45
|
+
bar.bar
|
46
46
|
count += 1
|
47
47
|
end
|
48
48
|
end
|
@@ -53,23 +53,41 @@ describe Throttler do
|
|
53
53
|
count.should eql 3
|
54
54
|
end
|
55
55
|
|
56
|
+
it "throttles fibers" do
|
57
|
+
require "fiber"
|
58
|
+
|
59
|
+
time = Benchmark.realtime do
|
60
|
+
fib = Fiber.new do
|
61
|
+
bar = Bar.new
|
62
|
+
loop do
|
63
|
+
bar.bar
|
64
|
+
Fiber.yield
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
3.times { fib.resume }
|
69
|
+
end
|
70
|
+
|
71
|
+
time.should be_close 2, 0.01
|
72
|
+
end
|
73
|
+
|
56
74
|
it "throttles concurrently-running scripts" do
|
57
75
|
time = Benchmark.realtime do
|
58
76
|
3.times do
|
59
|
-
`ruby #{File.dirname(__FILE__) + "
|
77
|
+
`ruby #{File.dirname(__FILE__) + "/../fixtures/foo.rb"}`
|
60
78
|
end
|
61
79
|
end
|
62
80
|
|
63
|
-
time.should be >
|
81
|
+
time.should be > 4.0
|
64
82
|
end
|
65
83
|
|
66
|
-
it "
|
84
|
+
it "supports multiple throttles" do
|
67
85
|
count = 0
|
68
86
|
threads = 10.times.collect do
|
69
87
|
Thread.new do
|
70
|
-
|
88
|
+
bar = Bar.new
|
71
89
|
loop do
|
72
|
-
count % 2 == 0 ?
|
90
|
+
count % 2 == 0 ? bar.bar : bar.baz
|
73
91
|
count += 1
|
74
92
|
end
|
75
93
|
end
|
@@ -79,9 +97,4 @@ describe Throttler do
|
|
79
97
|
|
80
98
|
count.should eql 6
|
81
99
|
end
|
82
|
-
|
83
|
-
it "removes lock if block raises exception" do
|
84
|
-
lambda{ Foo.new.exceptional }.should raise_error
|
85
|
-
File.open("/tmp/.bar") { |f| f.flock(File::LOCK_EX | File::LOCK_NB).should_not be_false }
|
86
|
-
end
|
87
100
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "benchmark"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module Throttler
|
6
|
+
describe Throttle do
|
7
|
+
before do
|
8
|
+
FileUtils.rm "/tmp/.foo", :force => true
|
9
|
+
@foo = Throttle.new("foo")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "creates a file" do
|
13
|
+
File.exists?("/tmp/.foo").should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
context "#lock" do
|
17
|
+
it "locks the file" do
|
18
|
+
@foo.lock
|
19
|
+
File.open("/tmp/.foo") { |f| f.flock(File::LOCK_EX | File::LOCK_NB).should be_false }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "#delay" do
|
24
|
+
it "throttles if file contains a timestamp" do
|
25
|
+
file = @foo.instance_variable_get(:@file)
|
26
|
+
file.stub!(:gets).and_return(Time.now.to_f)
|
27
|
+
|
28
|
+
time = Benchmark.realtime do
|
29
|
+
@foo.delay(1.0)
|
30
|
+
end
|
31
|
+
|
32
|
+
time.should be_close 1, 0.1
|
33
|
+
end
|
34
|
+
|
35
|
+
it "does not throttle if file is blank" do
|
36
|
+
file = @foo.instance_variable_get(:@file)
|
37
|
+
file.stub!(:gets).and_return("")
|
38
|
+
|
39
|
+
time = Benchmark.realtime do
|
40
|
+
@foo.delay(1.0)
|
41
|
+
end
|
42
|
+
|
43
|
+
time.should be_close 0, 0.1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "#timestamp" do
|
48
|
+
it "timestamps file" do
|
49
|
+
@foo.timestamp
|
50
|
+
file = @foo.instance_variable_get(:@file)
|
51
|
+
file.close # We need to close the file first
|
52
|
+
|
53
|
+
File.open("/tmp/.foo") { |f| f.gets.to_f.should be_close Time.now.to_f, 0.1 }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "#unlock" do
|
58
|
+
it "unlocks the file" do
|
59
|
+
@foo.lock
|
60
|
+
@foo.unlock
|
61
|
+
|
62
|
+
File.open("/tmp/.foo") { |f| f.flock(File::LOCK_EX | File::LOCK_NB).should_not be_false }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
require "fileutils"
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
class Foo
|
6
|
+
include Throttler
|
7
|
+
|
8
|
+
def bar
|
9
|
+
throttle("foo"){ raise }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Throttler do
|
14
|
+
before do
|
15
|
+
FileUtils.rm "/tmp/.foo", :force => true
|
16
|
+
|
17
|
+
class Foo; include Throttler; end
|
18
|
+
@foo = Foo.new
|
19
|
+
end
|
20
|
+
|
21
|
+
context "#throttle" do
|
22
|
+
it "removes lock after block raises an exception" do
|
23
|
+
lambda{ @foo.throttle("foo"){ raise } }.should raise_error
|
24
|
+
|
25
|
+
File.open("/tmp/.foo"){ |f| f.flock(File::LOCK_EX | File::LOCK_NB).should_not be_false }
|
26
|
+
end
|
27
|
+
|
28
|
+
it "throttles for one second by default" do
|
29
|
+
time = Benchmark.realtime do
|
30
|
+
2.times{ @foo.throttle("foo"){ } }
|
31
|
+
end
|
32
|
+
|
33
|
+
time.should be_close 1, 0.1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: throttler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 29
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
7
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
8
|
+
- 4
|
9
|
+
version: 0.1.4
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Hakan Ensari
|
@@ -16,7 +15,7 @@ autorequire:
|
|
16
15
|
bindir: bin
|
17
16
|
cert_chain: []
|
18
17
|
|
19
|
-
date: 2010-07-
|
18
|
+
date: 2010-07-24 00:00:00 +01:00
|
20
19
|
default_executable:
|
21
20
|
dependencies: []
|
22
21
|
|
@@ -32,10 +31,13 @@ extra_rdoc_files:
|
|
32
31
|
files:
|
33
32
|
- LICENSE
|
34
33
|
- lib/throttler.rb
|
34
|
+
- lib/throttler/throttle.rb
|
35
35
|
- README.md
|
36
36
|
- spec/fixtures/foo.rb
|
37
|
+
- spec/integration/scenarios_spec.rb
|
37
38
|
- spec/spec_helper.rb
|
38
|
-
- spec/
|
39
|
+
- spec/unit/throttler/throttle_spec.rb
|
40
|
+
- spec/unit/throttler_spec.rb
|
39
41
|
has_rdoc: true
|
40
42
|
homepage: http://github.com/papercavalier/throttler
|
41
43
|
licenses: []
|
@@ -50,7 +52,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
52
|
requirements:
|
51
53
|
- - ">="
|
52
54
|
- !ruby/object:Gem::Version
|
53
|
-
hash:
|
55
|
+
hash: -2072448878210444692
|
54
56
|
segments:
|
55
57
|
- 0
|
56
58
|
version: "0"
|
@@ -59,7 +61,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
61
|
requirements:
|
60
62
|
- - ">="
|
61
63
|
- !ruby/object:Gem::Version
|
62
|
-
hash: 3
|
63
64
|
segments:
|
64
65
|
- 0
|
65
66
|
version: "0"
|
@@ -72,5 +73,7 @@ specification_version: 3
|
|
72
73
|
summary: Throttles the frequency at which concurrently-running Ruby blocks are executed.
|
73
74
|
test_files:
|
74
75
|
- spec/fixtures/foo.rb
|
76
|
+
- spec/integration/scenarios_spec.rb
|
75
77
|
- spec/spec_helper.rb
|
76
|
-
- spec/
|
78
|
+
- spec/unit/throttler/throttle_spec.rb
|
79
|
+
- spec/unit/throttler_spec.rb
|