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 CHANGED
@@ -5,22 +5,31 @@ Throttler
5
5
 
6
6
  Throttle concurrency.
7
7
 
8
- include Throttler
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
- loop do
13
- throttle("foo") do
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
- count.should eql 3
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. You gotta do something about it.
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
- # Throttler
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
- path = "/tmp/.#{name}"
8
-
9
- FileUtils.touch path unless File.exist? path
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.flock(File::LOCK_UN)
23
- file.close
23
+ file.unlock
24
24
  end
25
25
  end
26
26
  end
data/spec/fixtures/foo.rb CHANGED
@@ -4,10 +4,10 @@ class Foo
4
4
  include Throttler
5
5
 
6
6
  def bar
7
- throttle("bar") { noop }
7
+ throttle("bar", 2.0) do
8
+ #noop
9
+ end
8
10
  end
9
-
10
- def noop; end
11
11
  end
12
12
 
13
13
  Foo.new.bar
@@ -2,7 +2,7 @@ require "benchmark"
2
2
  require "fileutils"
3
3
  require "spec_helper"
4
4
 
5
- class Foo
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 Throttler do
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
- Foo.new.bar
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
- foo = Foo.new
43
+ bar = Bar.new
44
44
  loop do
45
- foo.bar
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__) + "/fixtures/foo.rb"}`
77
+ `ruby #{File.dirname(__FILE__) + "/../fixtures/foo.rb"}`
60
78
  end
61
79
  end
62
80
 
63
- time.should be > 2.0
81
+ time.should be > 4.0
64
82
  end
65
83
 
66
- it "throttles by name" do
84
+ it "supports multiple throttles" do
67
85
  count = 0
68
86
  threads = 10.times.collect do
69
87
  Thread.new do
70
- foo = Foo.new
88
+ bar = Bar.new
71
89
  loop do
72
- count % 2 == 0 ? foo.bar : foo.baz
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
- - 3
10
- version: 0.1.3
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-23 00:00:00 +01:00
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/throttler_spec.rb
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: 3
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/throttler_spec.rb
78
+ - spec/unit/throttler/throttle_spec.rb
79
+ - spec/unit/throttler_spec.rb