throttler 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2009 Paper Cavalier
3
+ Copyright (c) 2010 Paper Cavalier
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,47 +1,27 @@
1
1
  Throttler
2
2
  =========
3
3
 
4
- Put a throttle on concurrency.
4
+ Throttler is a tired, cranky module that throttles parallel-running Ruby scripts on a single machine.
5
5
 
6
- ![Mapplethorpe](http://github.com/papercavalier/throttler/raw/master/mapplethorpe_chains.jpg)
6
+ ![Mapplethorpe](https://github.com/papercavalier/throttler/raw/master/mapplethorpe_chains.jpg)
7
7
 
8
8
  Example
9
- --------
9
+ -------
10
10
 
11
- Let's assume we have multiple workers hitting the Amazon API on three IP addresses.
11
+ Our very own peculiar use case: We have multiple Resque workers hitting the Amazon IP on multiple addresses and want to make sure they don't fire more than once per locale per IP.
12
12
 
13
13
  class Worker
14
14
  include Throttler
15
-
16
- attr_accessor :interface
17
-
18
- def request
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) }
28
- end
29
- end
30
-
31
- worker, count = Worker.new, 0
32
-
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 }
15
+
16
+ attr_accessor :locale,
17
+ :interface
18
+
19
+ def perform
20
+ scope = "#{locale}-#{interface}"
21
+ freq = 1.0
22
+
23
+ throttle(scope, interval) do
24
+ # perform a request
39
25
  end
40
26
  end
41
27
  end
42
-
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,27 +1,27 @@
1
- module Throttler
1
+ module Throttler #:nodoc:
2
2
  class Timer
3
3
  def initialize(scope)
4
4
  path = "/tmp/.#{scope}"
5
- @file = File.open(path, File::RDWR|File::CREAT)
5
+ @timer = File.open(path, File::RDWR|File::CREAT)
6
6
  end
7
7
 
8
8
  def lock
9
- @file.flock(File::LOCK_EX)
9
+ @timer.flock(File::LOCK_EX)
10
10
  end
11
11
 
12
12
  def timestamp
13
- @timestamp ||= @file.gets.to_f
13
+ @timestamp ||= @timer.gets.to_f
14
14
  end
15
15
 
16
16
  def timestamp=(time)
17
- @file.rewind
18
- @file.write(time)
17
+ @timer.rewind
18
+ @timer.write(time)
19
19
  @timestamp = time
20
20
  end
21
21
 
22
22
  def unlock
23
- @file.flock(File::LOCK_UN)
24
- @file.close
23
+ @timer.flock(File::LOCK_UN)
24
+ @timer.close
25
25
  end
26
26
  end
27
27
  end
@@ -0,0 +1,4 @@
1
+ module Throttler #:nodoc:
2
+ VERSION = "0.2.2"
3
+ end
4
+
data/lib/throttler.rb CHANGED
@@ -1,18 +1,14 @@
1
- require File.dirname(__FILE__) + "/throttler/timer"
1
+ require "throttler/timer"
2
2
 
3
- # The Throttler module.
3
+ # = Throttler
4
4
  #
5
- # Include this in the class where you wish to use the throttler.
5
+ # An old, rusty throttler that gets the job done
6
6
  module Throttler
7
7
 
8
- # Throttles the frequency in which a block is run.
8
+ # Throttles the frequency in which a block is run
9
9
  #
10
- # Pass throttler scope and the interval between each execution.
11
- # Latter defaults to one second.
12
- #
13
- # throttle("foo") { some_code }
14
- #
15
- def throttle(scope, interval=1.0)
10
+ # Optionally pass a scope and interval.
11
+ def throttle(scope="throttler", interval=1.0)
16
12
  timer = Timer.new(scope)
17
13
  timer.lock
18
14
  sleep [timer.timestamp + interval - Time.now.to_f, 0.0].max
data/spec/fixtures/foo.rb CHANGED
@@ -1,4 +1,5 @@
1
- require File.dirname(__FILE__) + "/../../lib/throttler"
1
+ $:.push File.expand_path("../../../lib", __FILE__)
2
+ require "throttler"
2
3
 
3
4
  class Foo
4
5
  include Throttler
@@ -9,11 +9,11 @@ describe "Throttler" do
9
9
  include Throttler
10
10
 
11
11
  def bar
12
- throttle("foo"){ }
12
+ throttle { }
13
13
  end
14
14
  end
15
15
 
16
- FileUtils.rm "/tmp/.foo", :force => true
16
+ FileUtils.rm "/tmp/.throttler", :force => true
17
17
  end
18
18
 
19
19
  it "throttles fibers" do
@@ -24,6 +24,6 @@ describe "Throttler" do
24
24
  3.times { fib.resume }
25
25
  end
26
26
 
27
- time.should be_close 2, 0.01
27
+ time.should be_within(0.1).of(2)
28
28
  end
29
29
  end
@@ -8,11 +8,11 @@ describe "Throttler" do
8
8
  include Throttler
9
9
 
10
10
  def bar
11
- throttle("foo"){ }
11
+ throttle { }
12
12
  end
13
13
  end
14
14
 
15
- FileUtils.rm "/tmp/.foo", :force => true
15
+ FileUtils.rm "/tmp/.throttler", :force => true
16
16
  end
17
17
 
18
18
  it "throttles a loop" do
@@ -22,6 +22,6 @@ describe "Throttler" do
22
22
  end
23
23
  end
24
24
 
25
- time.should be_close 2, 0.1
25
+ time.should be_within(0.1).of(2)
26
26
  end
27
27
  end
@@ -17,6 +17,6 @@ describe "Throttler" do
17
17
  4.times{ `ruby #{File.dirname(__FILE__) + "/../fixtures/foo.rb"}` }
18
18
  end
19
19
 
20
- (time - startup_time).should be_close(6.0, 0.5)
20
+ (time - startup_time).should be_within(0.5).of(6.0)
21
21
  end
22
22
  end
@@ -7,7 +7,7 @@ describe "Throttler" do
7
7
  include Throttler
8
8
 
9
9
  def bar
10
- throttle("foo"){ }
10
+ throttle { }
11
11
  end
12
12
 
13
13
  def baz
@@ -15,7 +15,7 @@ describe "Throttler" do
15
15
  end
16
16
  end
17
17
 
18
- %w{foo foo-prime}.each do |file|
18
+ %w{throttler foo-prime}.each do |file|
19
19
  FileUtils.rm "/tmp/.#{file}", :force => true
20
20
  end
21
21
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,2 @@
1
1
  require "rspec"
2
2
  require File.expand_path("../../lib/throttler", __FILE__)
3
-
4
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each{ |f| require f }
@@ -6,7 +6,10 @@ module Throttler
6
6
  describe Timer do
7
7
  before do
8
8
  FileUtils.rm "/tmp/.foo", :force => true
9
- @timer = Timer.new("foo")
9
+ end
10
+
11
+ let!(:timer) do
12
+ Timer.new("foo")
10
13
  end
11
14
 
12
15
  it "creates a file" do
@@ -15,8 +18,10 @@ module Throttler
15
18
 
16
19
  context "#lock" do
17
20
  it "locks the file" do
18
- @timer.lock
19
- File.open("/tmp/.foo") { |f| f.flock(File::LOCK_EX | File::LOCK_NB).should be_false }
21
+ timer.lock
22
+ File.open("/tmp/.foo") do |f|
23
+ f.flock(File::LOCK_EX | File::LOCK_NB).should be_false
24
+ end
20
25
  end
21
26
  end
22
27
 
@@ -24,32 +29,36 @@ module Throttler
24
29
  it "gets the timestamp" do
25
30
  now = Time.now.to_f
26
31
  File.open("/tmp/.foo", "w") { |f| f.write(now) }
27
- @timer.timestamp.should be_close(now, 0.1)
32
+ timer.timestamp.should be_within(0.1).of(now)
28
33
  end
29
34
 
30
35
  it "returns 0.0 when file is first created" do
31
- @timer.timestamp.should eql(0.0)
36
+ timer.timestamp.should eql(0.0)
32
37
  end
33
38
  end
34
-
39
+
35
40
  context "#timestamp=" do
36
41
  it "sets the timestamp" do
37
42
  now = Time.now.to_f
38
- @timer.timestamp= now
43
+ timer.timestamp= now
39
44
 
40
45
  # We need to close the file first
41
- @timer.instance_variable_get(:@file).close
46
+ timer.instance_variable_get(:@timer).close
42
47
 
43
- File.open("/tmp/.foo") { |f| f.gets.to_f.should be_close(now, 0.1) }
48
+ File.open("/tmp/.foo") do |f|
49
+ f.gets.to_f.should be_within(0.1).of(now)
50
+ end
44
51
  end
45
52
  end
46
53
 
47
54
  context "#unlock" do
48
55
  it "unlocks the file" do
49
- @timer.lock
50
- @timer.unlock
56
+ timer.lock
57
+ timer.unlock
51
58
 
52
- File.open("/tmp/.foo") { |f| f.flock(File::LOCK_EX | File::LOCK_NB).should_not be_false }
59
+ File.open("/tmp/.foo") do |f|
60
+ f.flock(File::LOCK_EX | File::LOCK_NB).should_not be_false
61
+ end
53
62
  end
54
63
  end
55
64
  end
@@ -6,30 +6,39 @@ describe Throttler do
6
6
  before do
7
7
  class Foo
8
8
  include Throttler
9
-
10
- def bar
11
- throttle("foo"){ raise }
12
- end
13
9
  end
14
10
 
15
- FileUtils.rm "/tmp/.foo", :force => true
11
+ FileUtils.rm "/tmp/.throttler", :force => true
12
+ end
16
13
 
17
- @foo = Foo.new
14
+ let!(:foo) do
15
+ Foo.new
18
16
  end
19
17
 
20
- context "#throttle" do
21
- it "removes lock even if block raises an exception" do
22
- lambda{ @foo.throttle("foo"){ raise } }.should raise_error
18
+ describe "#throttle" do
19
+ it "removes a lock when an exception is raised" do
20
+ expect do
21
+ foo.throttle { raise }
22
+ end.to raise_error
23
23
 
24
- File.open("/tmp/.foo"){ |f| f.flock(File::LOCK_EX | File::LOCK_NB).should_not be_false }
24
+ File.open("/tmp/.throttler") do |f|
25
+ f.flock(File::LOCK_EX | File::LOCK_NB).should_not be_false
26
+ end
25
27
  end
26
28
 
27
- it "throttles for one second by default" do
28
- time = Benchmark.realtime do
29
- 2.times{ @foo.throttle("foo"){ } }
29
+ context "by default" do
30
+ it "namespaces as `throttler`" do
31
+ foo.throttle
32
+ FileTest.exists?("/tmp/.throttler").should be_true
30
33
  end
31
34
 
32
- time.should be_close 1, 0.1
35
+ it "throttles for one second" do
36
+ time = Benchmark.realtime do
37
+ 2.times{ foo.throttle }
38
+ end
39
+
40
+ time.should be_within(0.1).of(1)
41
+ end
33
42
  end
34
- end
43
+ end
35
44
  end
metadata CHANGED
@@ -5,33 +5,48 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 1
9
- version: 0.2.1
8
+ - 2
9
+ version: 0.2.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Hakan Ensari
13
- - Piotr Laszewski
13
+ - "Piotr \xC5\x81aszewski"
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-27 00:00:00 +01:00
18
+ date: 2010-11-29 00:00:00 +00:00
19
19
  default_executable:
20
- dependencies: []
21
-
22
- description: Throttles the frequency in which asynchronously-executed Ruby blocks are run on a single server or network interface.
23
- email: code@papercavalier.com
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ segments:
30
+ - 2
31
+ - 1
32
+ - 0
33
+ version: 2.1.0
34
+ type: :development
35
+ version_requirements: *id001
36
+ description: A tired, cranky module that throttles parallel-running Ruby scripts on a single machine.
37
+ email:
38
+ - code@papercavalier.com
24
39
  executables: []
25
40
 
26
41
  extensions: []
27
42
 
28
- extra_rdoc_files:
29
- - LICENSE
30
- - README.md
43
+ extra_rdoc_files: []
44
+
31
45
  files:
32
- - LICENSE
33
- - lib/throttler.rb
34
46
  - lib/throttler/timer.rb
47
+ - lib/throttler/version.rb
48
+ - lib/throttler.rb
49
+ - LICENSE
35
50
  - README.md
36
51
  - spec/fixtures/foo.rb
37
52
  - spec/integration/blocks_with_long_execution_time_spec.rb
@@ -47,8 +62,8 @@ homepage: http://github.com/papercavalier/throttler
47
62
  licenses: []
48
63
 
49
64
  post_install_message:
50
- rdoc_options:
51
- - --charset=UTF-8
65
+ rdoc_options: []
66
+
52
67
  require_paths:
53
68
  - lib
54
69
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -56,7 +71,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
71
  requirements:
57
72
  - - ">="
58
73
  - !ruby/object:Gem::Version
59
- hash: -3148749189245223368
60
74
  segments:
61
75
  - 0
62
76
  version: "0"
@@ -70,11 +84,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
84
  version: "0"
71
85
  requirements: []
72
86
 
73
- rubyforge_project:
87
+ rubyforge_project: throttler
74
88
  rubygems_version: 1.3.7
75
89
  signing_key:
76
90
  specification_version: 3
77
- summary: Throttles the frequency in which asynchronously-executed Ruby blocks are run on a single server or network interface.
91
+ summary: A tired, cranky throttler
78
92
  test_files:
79
93
  - spec/fixtures/foo.rb
80
94
  - spec/integration/blocks_with_long_execution_time_spec.rb