throttler 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,35 +1,47 @@
1
1
  Throttler
2
2
  =========
3
3
 
4
+ Put a throttle on concurrency.
5
+
4
6
  ![Mapplethorpe](http://github.com/papercavalier/throttler/raw/master/mapplethorpe_chains.jpg)
5
7
 
6
- Throttle concurrency.
8
+ Example
9
+ --------
7
10
 
8
- class Foo
9
- include Throttler
11
+ Let's assume we have multiple workers hitting the Amazon API on three IP addresses.
10
12
 
13
+ class Worker
14
+ include Throttler
15
+
16
+ attr_accessor :interface
17
+
11
18
  def request
12
- throttle("foo#{interface}", 1.0) do
13
- # do something
14
- end
19
+
20
+ # Scope the throttle by network interface:
21
+ name = "foo#{interface}"
22
+
23
+ # Amazon asks you to limit requests to one per second per IP, so:
24
+ interval = 0.9
25
+
26
+ # Say a request downloads in two seconds on average
27
+ throttle(name, interval) { sleep(2) }
15
28
  end
16
-
17
- def interface; "eth0"; end
18
29
  end
19
30
 
20
- foo, count = Foo.new, 0
31
+ worker, count = Worker.new, 0
21
32
 
22
- 100.times do
23
- Thread.new do
24
- foo.request
25
- count += 1
33
+ (0..3).each do |port|
34
+ 10.times do
35
+ Thread.new do
36
+ count += 1
37
+ worker.interface = "eth#{port}"
38
+ loop{ worker.request }
39
+ end
26
40
  end
27
41
  end
28
42
 
29
- sleep 1.2
30
- count.should eql(2)
31
-
32
- Use case
33
- --------
34
-
35
- Imagine multiple workers hitting the Amazon API on the same IP address.
43
+ sleep 10.25
44
+
45
+ # We expect workers to have sent 11 requests within 10 seconds on
46
+ # each of the three available network interfaces.
47
+ count.should eql(33)
@@ -1,7 +1,7 @@
1
1
  module Throttler
2
2
  class Timer
3
- def initialize(name)
4
- path = "/tmp/.#{name}"
3
+ def initialize(scope)
4
+ path = "/tmp/.#{scope}"
5
5
  @file = File.open(path, File::RDWR|File::CREAT)
6
6
  end
7
7
 
data/lib/throttler.rb CHANGED
@@ -7,20 +7,17 @@ module Throttler
7
7
 
8
8
  # Throttles the frequency in which a block is run.
9
9
  #
10
- # Pass name of throttler and optionally, the interval between each
11
- # moment of execution. Latter defaults to one second.
10
+ # Pass throttler scope and the interval between each execution.
11
+ # Latter defaults to one second.
12
12
  #
13
13
  # throttle("foo") { some_code }
14
14
  #
15
- def throttle(name, interval=1.0)
16
- timer = Timer.new(name)
15
+ def throttle(scope, interval=1.0)
16
+ timer = Timer.new(scope)
17
17
  timer.lock
18
- begin
19
- sleep [timer.timestamp + interval - Time.now.to_f, 0.0].max
20
- yield if block_given?
21
- timer.timestamp = Time.now.to_f
22
- ensure
23
- timer.unlock
24
- end
18
+ sleep [timer.timestamp + interval - Time.now.to_f, 0.0].max
19
+ timer.timestamp = Time.now.to_f
20
+ timer.unlock
21
+ yield if block_given?
25
22
  end
26
23
  end
data/spec/fixtures/foo.rb CHANGED
@@ -4,10 +4,14 @@ class Foo
4
4
  include Throttler
5
5
 
6
6
  def bar
7
- throttle("bar", 2.0) do
8
- #noop
9
- end
7
+ throttle("foo", 2.0) { }
10
8
  end
9
+
10
+ def control; end
11
11
  end
12
12
 
13
- Foo.new.bar
13
+ if ARGV.size == 0
14
+ Foo.new.bar
15
+ else
16
+ Foo.new.control
17
+ end
@@ -0,0 +1,33 @@
1
+ require "fileutils"
2
+ require "spec_helper"
3
+
4
+ describe "Throttler" do
5
+ before do
6
+ class Foo
7
+ include Throttler
8
+
9
+ def bar
10
+ throttle("foo") do
11
+ yield # so we can count
12
+ sleep 10
13
+ end
14
+ end
15
+ end
16
+
17
+ FileUtils.rm "/tmp/.foo", :force => true
18
+ end
19
+
20
+ it "throttles threads with long execution times" do
21
+ count = 0
22
+
23
+ threads = 10.times.collect do
24
+ Thread.new do
25
+ loop{ Foo.new.bar{ count += 1 } }
26
+ end
27
+ end
28
+ sleep 3.25
29
+ threads.each { |t| Thread.kill(t) }
30
+
31
+ count.should eql 4
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ require "benchmark"
2
+ require "fiber"
3
+ require "fileutils"
4
+ require "spec_helper"
5
+
6
+ describe "Throttler" do
7
+ before do
8
+ class Foo
9
+ include Throttler
10
+
11
+ def bar
12
+ throttle("foo"){ }
13
+ end
14
+ end
15
+
16
+ FileUtils.rm "/tmp/.foo", :force => true
17
+ end
18
+
19
+ it "throttles fibers" do
20
+ time = Benchmark.realtime do
21
+ fib = Fiber.new do
22
+ loop{ Foo.new.bar; Fiber.yield }
23
+ end
24
+ 3.times { fib.resume }
25
+ end
26
+
27
+ time.should be_close 2, 0.01
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ require "benchmark"
2
+ require "fileutils"
3
+ require "spec_helper"
4
+
5
+ describe "Throttler" do
6
+ before do
7
+ class Foo
8
+ include Throttler
9
+
10
+ def bar
11
+ throttle("foo"){ }
12
+ end
13
+ end
14
+
15
+ FileUtils.rm "/tmp/.foo", :force => true
16
+ end
17
+
18
+ it "throttles a loop" do
19
+ time = Benchmark.realtime do
20
+ 3.times do
21
+ Foo.new.bar
22
+ end
23
+ end
24
+
25
+ time.should be_close 2, 0.1
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ require "benchmark"
2
+ require "fileutils"
3
+ require "spec_helper"
4
+
5
+
6
+ describe "Throttler" do
7
+ before do
8
+ FileUtils.rm "/tmp/.foo", :force => true
9
+ end
10
+
11
+ it "throttles concurrent processes" do
12
+ startup_time = Benchmark.realtime do
13
+ `ruby #{File.dirname(__FILE__) + "/../fixtures/foo.rb"} control`
14
+ end
15
+
16
+ time = Benchmark.realtime do
17
+ 4.times{ `ruby #{File.dirname(__FILE__) + "/../fixtures/foo.rb"}` }
18
+ end
19
+
20
+ (time - startup_time).should be_close(6.0, 0.5)
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ require "fileutils"
2
+ require "spec_helper"
3
+
4
+ describe "Throttler" do
5
+ before do
6
+ class Foo
7
+ include Throttler
8
+
9
+ def bar
10
+ throttle("foo"){ }
11
+ end
12
+
13
+ def baz
14
+ throttle("foo-prime"){ }
15
+ end
16
+ end
17
+
18
+ %w{foo foo-prime}.each do |file|
19
+ FileUtils.rm "/tmp/.#{file}", :force => true
20
+ end
21
+ end
22
+
23
+ it "throttles threads" do
24
+ count = 0
25
+ threads = 10.times.collect do
26
+ Thread.new do
27
+ loop { Foo.new.bar; count += 1 }
28
+ end
29
+ end
30
+ sleep 3.25
31
+ threads.each { |t| Thread.kill(t) }
32
+
33
+ count.should eql 4
34
+ end
35
+
36
+ it "throttles by scope" do
37
+ count = 0
38
+ threads = 10.times.collect do
39
+ Thread.new do
40
+ loop do
41
+ Foo.new.send(count % 2 == 0 ? :bar : :baz)
42
+ count += 1
43
+ end
44
+ end
45
+ end
46
+ sleep 3.25
47
+ threads.each { |t| Thread.kill(t) }
48
+
49
+ count.should eql 8
50
+ end
51
+ end
@@ -2,24 +2,23 @@ require "benchmark"
2
2
  require "fileutils"
3
3
  require "spec_helper"
4
4
 
5
- class Foo
6
- include Throttler
7
-
8
- def bar
9
- throttle("foo"){ raise }
10
- end
11
- end
12
-
13
5
  describe Throttler do
14
6
  before do
7
+ class Foo
8
+ include Throttler
9
+
10
+ def bar
11
+ throttle("foo"){ raise }
12
+ end
13
+ end
14
+
15
15
  FileUtils.rm "/tmp/.foo", :force => true
16
-
17
- class Foo; include Throttler; end
16
+
18
17
  @foo = Foo.new
19
18
  end
20
19
 
21
20
  context "#throttle" do
22
- it "removes lock after block raises an exception" do
21
+ it "removes lock even if block raises an exception" do
23
22
  lambda{ @foo.throttle("foo"){ raise } }.should raise_error
24
23
 
25
24
  File.open("/tmp/.foo"){ |f| f.flock(File::LOCK_EX | File::LOCK_NB).should_not be_false }
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 0
9
- version: 0.2.0
8
+ - 1
9
+ version: 0.2.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Hakan Ensari
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-26 00:00:00 +01:00
18
+ date: 2010-07-27 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -34,7 +34,11 @@ files:
34
34
  - lib/throttler/timer.rb
35
35
  - README.md
36
36
  - spec/fixtures/foo.rb
37
- - spec/integration/throttler_spec.rb
37
+ - spec/integration/blocks_with_long_execution_time_spec.rb
38
+ - spec/integration/fibers_spec.rb
39
+ - spec/integration/loop_spec.rb
40
+ - spec/integration/processes_spec.rb
41
+ - spec/integration/threads_spec.rb
38
42
  - spec/spec_helper.rb
39
43
  - spec/unit/throttler/timer_spec.rb
40
44
  - spec/unit/throttler_spec.rb
@@ -52,7 +56,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
52
56
  requirements:
53
57
  - - ">="
54
58
  - !ruby/object:Gem::Version
55
- hash: 4404238439013896602
59
+ hash: -3148749189245223368
56
60
  segments:
57
61
  - 0
58
62
  version: "0"
@@ -73,7 +77,11 @@ specification_version: 3
73
77
  summary: Throttles the frequency in which asynchronously-executed Ruby blocks are run on a single server or network interface.
74
78
  test_files:
75
79
  - spec/fixtures/foo.rb
76
- - spec/integration/throttler_spec.rb
80
+ - spec/integration/blocks_with_long_execution_time_spec.rb
81
+ - spec/integration/fibers_spec.rb
82
+ - spec/integration/loop_spec.rb
83
+ - spec/integration/processes_spec.rb
84
+ - spec/integration/threads_spec.rb
77
85
  - spec/spec_helper.rb
78
86
  - spec/unit/throttler/timer_spec.rb
79
87
  - spec/unit/throttler_spec.rb
@@ -1,100 +0,0 @@
1
- require "benchmark"
2
- require "fileutils"
3
- require "spec_helper"
4
-
5
- class Bar
6
- include Throttler
7
-
8
- def bar
9
- throttle("bar"){ noop }
10
- end
11
-
12
- def baz
13
- throttle("baz"){ noop }
14
- end
15
-
16
- def exceptional
17
- throttle("bar"){ raise }
18
- end
19
-
20
- def noop; end
21
- end
22
-
23
- describe "Throttler" do
24
- before do
25
- FileUtils.rm "/tmp/.bar", :force => true
26
- FileUtils.rm "/tmp/.baz", :force => true
27
- end
28
-
29
- it "throttles a loop" do
30
- time = Benchmark.realtime do
31
- 3.times do
32
- Bar.new.bar
33
- end
34
- end
35
-
36
- time.should be_close 2, 0.1
37
- end
38
-
39
- it "throttles threads" do
40
- count = 0
41
- threads = 10.times.collect do
42
- Thread.new do
43
- bar = Bar.new
44
- loop do
45
- bar.bar
46
- count += 1
47
- end
48
- end
49
- end
50
- sleep 2.2
51
- threads.each { |t| Thread.kill(t) }
52
-
53
- count.should eql 3
54
- end
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
-
74
- it "throttles concurrently-running scripts" do
75
- time = Benchmark.realtime do
76
- 3.times do
77
- `ruby #{File.dirname(__FILE__) + "/../fixtures/foo.rb"}`
78
- end
79
- end
80
-
81
- time.should be > 4.0
82
- end
83
-
84
- it "supports multiple throttles" do
85
- count = 0
86
- threads = 10.times.collect do
87
- Thread.new do
88
- bar = Bar.new
89
- loop do
90
- count % 2 == 0 ? bar.bar : bar.baz
91
- count += 1
92
- end
93
- end
94
- end
95
- sleep 2.2
96
- threads.each { |t| Thread.kill(t) }
97
-
98
- count.should eql 6
99
- end
100
- end