beaneater 0.3.1 → 1.1.1

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.
@@ -1,16 +1,36 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  # Represents collection of tube related commands.
3
- class Tubes < PoolCommand
3
+
4
+ class Tubes
5
+ include Enumerable
6
+
7
+ # @!attribute client
8
+ # @return [Beaneater] returns the client instance
9
+ attr_reader :client
4
10
 
5
11
  # Creates new tubes instance.
6
12
  #
7
- # @param [Beaneater::Pool] pool The beaneater pool for this tube.
13
+ # @param [Beaneater] client The beaneater client instance.
8
14
  # @example
9
- # Beaneater::Tubes.new(@pool)
15
+ # Beaneater::Tubes.new(@client)
16
+ #
17
+ def initialize(client)
18
+ @client = client
19
+ end
20
+
21
+ def last_used
22
+ client.connection.tube_used
23
+ end
24
+
25
+ def last_used=(tube_name)
26
+ client.connection.tube_used = tube_name
27
+ end
28
+
29
+ # Delegates transmit to the connection object.
10
30
  #
11
- def initialize(pool)
12
- @last_used = 'default'
13
- super
31
+ # @see Beaneater::Connection#transmit
32
+ def transmit(command, **options)
33
+ client.connection.transmit(command, **options)
14
34
  end
15
35
 
16
36
  # Finds the specified beanstalk tube.
@@ -24,7 +44,7 @@ module Beaneater
24
44
  #
25
45
  # @api public
26
46
  def find(tube_name)
27
- Tube.new(self.pool, tube_name)
47
+ Tube.new(client, tube_name)
28
48
  end
29
49
  alias_method :[], :find
30
50
 
@@ -35,13 +55,14 @@ module Beaneater
35
55
  # @yield [job] Reserved beaneater job.
36
56
  # @return [Beaneater::Job] Reserved beaneater job.
37
57
  # @example
38
- # @conn.tubes.reserve { |job| process(job) }
58
+ # @client.tubes.reserve { |job| process(job) }
39
59
  # # => <Beaneater::Job id=5 body="foo">
40
60
  #
41
61
  # @api public
42
62
  def reserve(timeout=nil, &block)
43
- res = transmit_to_rand(timeout ? "reserve-with-timeout #{timeout}" : 'reserve')
44
- job = Job.new(res)
63
+ res = transmit(
64
+ timeout ? "reserve-with-timeout #{timeout}" : 'reserve')
65
+ job = Job.new(client, res)
45
66
  block.call(job) if block_given?
46
67
  job
47
68
  end
@@ -50,36 +71,54 @@ module Beaneater
50
71
  #
51
72
  # @return [Array<Beaneater::Tube>] List of all beanstalk tubes.
52
73
  # @example
53
- # @pool.tubes.all
74
+ # @client.tubes.all
54
75
  # # => [<Beaneater::Tube name="tube2">, <Beaneater::Tube name="tube3">]
55
76
  #
56
77
  # @api public
57
78
  def all
58
- transmit_to_all('list-tubes', :merge => true)[:body].map { |tube_name| Tube.new(self.pool, tube_name) }
79
+ transmit('list-tubes')[:body].map do |tube_name|
80
+ Tube.new(client, tube_name)
81
+ end
82
+ end
83
+
84
+ # Calls the given block once for each known beanstalk tube, passing that element as a parameter.
85
+ #
86
+ # @return An Enumerator is returned if no block is given.
87
+ # @example
88
+ # @pool.tubes.each {|t| puts t.name}
89
+ #
90
+ # @api public
91
+ def each(&block)
92
+ all.each(&block)
59
93
  end
60
94
 
61
95
  # List of watched beanstalk tubes.
62
96
  #
63
97
  # @return [Array<Beaneater::Tube>] List of watched beanstalk tubes.
64
98
  # @example
65
- # @pool.tubes.watched
99
+ # @client.tubes.watched
66
100
  # # => [<Beaneater::Tube name="tube2">, <Beaneater::Tube name="tube3">]
67
101
  #
68
102
  # @api public
69
103
  def watched
70
- transmit_to_all('list-tubes-watched', :merge => true)[:body].map { |tube_name| Tube.new(self.pool, tube_name) }
104
+ last_watched = transmit('list-tubes-watched')[:body]
105
+ client.connection.tubes_watched = last_watched.dup
106
+ last_watched.map do |tube_name|
107
+ Tube.new(client, tube_name)
108
+ end
71
109
  end
72
110
 
73
111
  # Currently used beanstalk tube.
74
112
  #
75
113
  # @return [Beaneater::Tube] Currently used beanstalk tube.
76
114
  # @example
77
- # @pool.tubes.used
115
+ # @client.tubes.used
78
116
  # # => <Beaneater::Tube name="tube2">
79
117
  #
80
118
  # @api public
81
119
  def used
82
- Tube.new(self.pool, transmit_to_rand('list-tube-used')[:id])
120
+ last_used = transmit('list-tube-used')[:id]
121
+ Tube.new(client, last_used)
83
122
  end
84
123
 
85
124
  # Add specified beanstalkd tubes as watched.
@@ -87,12 +126,13 @@ module Beaneater
87
126
  # @param [*String] names Name of tubes to watch
88
127
  # @raise [Beaneater::InvalidTubeName] Tube to watch was invalid.
89
128
  # @example
90
- # @pool.tubes.watch('foo', 'bar')
129
+ # @client.tubes.watch('foo', 'bar')
91
130
  #
92
131
  # @api public
93
132
  def watch(*names)
94
133
  names.each do |t|
95
- transmit_to_all "watch #{t}"
134
+ transmit "watch #{t}"
135
+ client.connection.add_to_watched(t)
96
136
  end
97
137
  rescue BadFormatError => ex
98
138
  raise InvalidTubeName, "Tube in '#{ex.cmd}' is invalid!"
@@ -103,7 +143,7 @@ module Beaneater
103
143
  # @param [*String] names Name of tubes to watch
104
144
  # @raise [Beaneater::InvalidTubeName] Tube to watch was invalid.
105
145
  # @example
106
- # @pool.tubes.watch!('foo', 'bar')
146
+ # @client.tubes.watch!('foo', 'bar')
107
147
  #
108
148
  # @api public
109
149
  def watch!(*names)
@@ -116,12 +156,13 @@ module Beaneater
116
156
  #
117
157
  # @param [*String] names Name of tubes to ignore
118
158
  # @example
119
- # @pool.tubes.ignore('foo', 'bar')
159
+ # @client.tubes.ignore('foo', 'bar')
120
160
  #
121
161
  # @api public
122
162
  def ignore(*names)
123
163
  names.each do |w|
124
- transmit_to_all "ignore #{w}"
164
+ transmit "ignore #{w}"
165
+ client.connection.remove_from_watched(w)
125
166
  end
126
167
  end
127
168
 
@@ -132,11 +173,11 @@ module Beaneater
132
173
  # @conn.tubes.use("some-tube")
133
174
  #
134
175
  def use(tube)
135
- return tube if @last_used == tube
136
- res = transmit_to_all("use #{tube}")
137
- @last_used = tube
176
+ return tube if last_used == tube
177
+ transmit("use #{tube}")
178
+ self.last_used = tube
138
179
  rescue BadFormatError
139
180
  raise InvalidTubeName, "Tube cannot be named '#{tube}'"
140
181
  end
141
182
  end # Tubes
142
- end # Beaneater
183
+ end # Beaneater
@@ -1,22 +1,31 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  # Beanstalk tube which contains jobs which can be inserted, reserved, et al.
3
- class Tube < PoolCommand
3
+ class Tube
4
4
 
5
5
  # @!attribute name
6
6
  # @return [String] name of the tube
7
- attr_reader :name
7
+ # @!attribute client
8
+ # @return [Beaneater] returns the client instance
9
+ attr_reader :name, :client
8
10
 
9
11
  # Fetches the specified tube.
10
12
  #
11
- # @param [Beaneater::Pool] pool The beaneater pool for this tube.
13
+ # @param [Beaneater] client The beaneater client instance.
12
14
  # @param [String] name The name for this tube.
13
15
  # @example
14
- # Beaneater::Tube.new(@pool, 'tube-name')
16
+ # Beaneater::Tube.new(@client, 'tube-name')
15
17
  #
16
- def initialize(pool, name)
18
+ def initialize(client, name)
19
+ @client = client
17
20
  @name = name.to_s
18
21
  @mutex = Mutex.new
19
- super(pool)
22
+ end
23
+
24
+ # Delegates transmit to the connection object.
25
+ #
26
+ # @see Beaneater::Connection#transmit
27
+ def transmit(command, options={})
28
+ client.connection.transmit(command, **options)
20
29
  end
21
30
 
22
31
  # Inserts job with specified body onto tube.
@@ -33,10 +42,16 @@ module Beaneater
33
42
  # @api public
34
43
  def put(body, options={})
35
44
  safe_use do
36
- options = { :pri => config.default_put_pri, :delay => config.default_put_delay,
37
- :ttr => config.default_put_ttr }.merge(options)
38
- cmd_options = "#{options[:pri]} #{options[:delay]} #{options[:ttr]} #{body.bytesize}"
39
- transmit_to_rand("put #{cmd_options}\r\n#{body}")
45
+ serialized_body = config.job_serializer.call(body)
46
+
47
+ options = {
48
+ :pri => config.default_put_pri,
49
+ :delay => config.default_put_delay,
50
+ :ttr => config.default_put_ttr
51
+ }.merge(options)
52
+
53
+ cmd_options = "#{options[:pri]} #{options[:delay]} #{options[:ttr]} #{serialized_body.bytesize}"
54
+ transmit("put #{cmd_options}\r\n#{serialized_body}")
40
55
  end
41
56
  end
42
57
 
@@ -50,10 +65,10 @@ module Beaneater
50
65
  # @api public
51
66
  def peek(state)
52
67
  safe_use do
53
- res = transmit_until_res "peek-#{state}", :status => "FOUND"
54
- Job.new(res)
68
+ res = transmit("peek-#{state}")
69
+ Job.new(client, res)
55
70
  end
56
- rescue Beaneater::NotFoundError => ex
71
+ rescue Beaneater::NotFoundError
57
72
  # Return nil if not found
58
73
  nil
59
74
  end
@@ -69,8 +84,8 @@ module Beaneater
69
84
  #
70
85
  # @api public
71
86
  def reserve(timeout=nil, &block)
72
- pool.tubes.watch!(self.name)
73
- pool.tubes.reserve(timeout, &block)
87
+ client.tubes.watch!(self.name)
88
+ client.tubes.reserve(timeout, &block)
74
89
  end
75
90
 
76
91
  # Kick specified number of jobs from buried to ready state.
@@ -82,18 +97,18 @@ module Beaneater
82
97
  #
83
98
  # @api public
84
99
  def kick(bounds=1)
85
- safe_use { transmit_to_rand("kick #{bounds}") }
100
+ safe_use { transmit("kick #{bounds}") }
86
101
  end
87
102
 
88
103
  # Returns related stats for this tube.
89
104
  #
90
105
  # @return [Beaneater::StatStruct] Struct of tube related values
91
106
  # @example
92
- # @tube.stats.delayed # => 24
107
+ # @tube.stats.current_jobs_delayed # => 24
93
108
  #
94
109
  # @api public
95
110
  def stats
96
- res = transmit_to_all("stats-tube #{name}", :merge => true)
111
+ res = transmit("stats-tube #{name}")
97
112
  StatStruct.from_hash(res[:body])
98
113
  end
99
114
 
@@ -106,7 +121,7 @@ module Beaneater
106
121
  #
107
122
  # @api public
108
123
  def pause(delay)
109
- transmit_to_all("pause-tube #{name} #{delay}")
124
+ transmit("pause-tube #{name} #{delay}")
110
125
  end
111
126
 
112
127
  # Clears all unreserved jobs in all states from the tube
@@ -115,14 +130,18 @@ module Beaneater
115
130
  # @tube.clear
116
131
  #
117
132
  def clear
118
- pool.tubes.watch!(self.name)
133
+ client.tubes.watch!(self.name)
119
134
  %w(delayed buried ready).each do |state|
120
135
  while job = self.peek(state.to_sym)
121
- job.delete
136
+ begin
137
+ job.delete
138
+ rescue Beaneater::UnexpectedResponse, Beaneater::NotFoundError
139
+ # swallow any issues
140
+ end
122
141
  end
123
142
  end
124
- pool.tubes.ignore(name)
125
- rescue Beaneater::UnexpectedResponse
143
+ client.tubes.ignore(name)
144
+ rescue Beaneater::NotIgnoredError
126
145
  # swallow any issues
127
146
  end
128
147
 
@@ -144,12 +163,12 @@ module Beaneater
144
163
  # @param [Proc] block Beanstalk command to transmit.
145
164
  # @return [Object] Result of block passed
146
165
  # @example
147
- # safe_use { transmit_to_rand("kick 1") }
166
+ # safe_use { transmit("kick 1") }
148
167
  # # => "Response to kick command"
149
168
  #
150
169
  def safe_use(&block)
151
170
  @mutex.lock
152
- tubes.use(self.name)
171
+ client.tubes.use(self.name)
153
172
  yield
154
173
  ensure
155
174
  @mutex.unlock
@@ -161,6 +180,5 @@ module Beaneater
161
180
  def config
162
181
  Beaneater.configuration
163
182
  end
164
-
165
183
  end # Tube
166
184
  end # Beaneater
@@ -1,4 +1,4 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  # Current version of gem.
3
- VERSION = "0.3.1"
3
+ VERSION = "1.1.1"
4
4
  end
@@ -2,8 +2,7 @@ require File.expand_path('../test_helper', __FILE__)
2
2
 
3
3
  describe "beanstalk-client" do
4
4
  before do
5
- @beanstalk = Beaneater::Pool.new(['127.0.0.1:11300'])
6
- # @beanstalk = Beaneater::Pool.new(['127.0.0.1:11300', '127.0.0.1:11301'])
5
+ @beanstalk = Beaneater.new('127.0.0.1:11300')
7
6
  @tubes = ['one', 'two', 'three']
8
7
 
9
8
  # Put something on each tube so they exist
@@ -23,12 +22,12 @@ describe "beanstalk-client" do
23
22
  # A: put one
24
23
  a = Thread.new do
25
24
  tube_one = @beanstalk.tubes.find('one')
26
- sleep 4
25
+ sleep 0.5
27
26
  tube_one.put('one')
28
27
  end
29
28
 
30
29
  b = Thread.new do
31
- sleep 1
30
+ sleep 0.125
32
31
  tube_two = @beanstalk.tubes.find('two')
33
32
  tube_two.put('two')
34
33
  end
@@ -52,13 +51,13 @@ describe "beanstalk-client" do
52
51
  before do
53
52
  a = Thread.new do
54
53
  tube_one = @beanstalk.tubes.find('one')
55
- sleep 4
54
+ sleep 0.5
56
55
  tube_one.put('one')
57
56
  end
58
57
 
59
58
  b = Thread.new do
60
59
  tube_two = @beanstalk.tubes.find('two')
61
- sleep 1
60
+ sleep 0.125
62
61
  tube_two.put('two')
63
62
  end
64
63
 
@@ -108,9 +107,4 @@ describe "beanstalk-client" do
108
107
  assert_nil @beanstalk.jobs.find(@job.id)
109
108
  end
110
109
  end
111
-
112
- after do
113
- cleanup_tubes!(@tubes, @beanstalk)
114
- end
115
-
116
110
  end
@@ -18,13 +18,25 @@ describe Beaneater::Connection do
18
18
 
19
19
  it "should init connection" do
20
20
  assert_kind_of TCPSocket, @bc.connection
21
- assert_equal '127.0.0.1', @bc.connection.peeraddr[3]
21
+ if @bc.connection.peeraddr[0] == 'AF_INET'
22
+ assert_equal '127.0.0.1', @bc.connection.peeraddr[3]
23
+ else
24
+ assert_equal 'AF_INET6', @bc.connection.peeraddr[0]
25
+ assert_equal '::1', @bc.connection.peeraddr[3]
26
+ end
22
27
  assert_equal 11300, @bc.connection.peeraddr[1]
23
28
  end
24
29
 
25
30
  it "should raise on invalid connection" do
26
31
  assert_raises(Beaneater::NotConnected) { Beaneater::Connection.new("localhost:8544") }
27
32
  end
33
+
34
+ it "should support array connection to single connection" do
35
+ @bc2 = Beaneater::Connection.new([@host])
36
+ assert_equal 'localhost', @bc.address
37
+ assert_equal 'localhost', @bc.host
38
+ assert_equal 11300, @bc.port
39
+ end
28
40
  end # new
29
41
 
30
42
  describe 'for #transmit' do
@@ -60,6 +72,48 @@ describe Beaneater::Connection do
60
72
  res = @bc.transmit "put 0 0 100 256\r\n"+(0..255).to_a.pack("c*")
61
73
  assert_equal 'INSERTED', res[:status]
62
74
  end
75
+
76
+ it "should retry command with success after one connection failure" do
77
+ TCPSocket.any_instance.expects(:readline).times(2).
78
+ raises(EOFError.new).then.
79
+ returns("DELETED 56\nFOO")
80
+
81
+ res = @bc.transmit "delete 56\r\n"
82
+ assert_equal 'DELETED', res[:status]
83
+ end
84
+
85
+ it "should fail after exceeding retries with DrainingError" do
86
+ TCPSocket.any_instance.expects(:readline).times(3).
87
+ raises(Beaneater::UnexpectedResponse.from_status("DRAINING", "delete 56"))
88
+
89
+ assert_raises(Beaneater::DrainingError) { @bc.transmit "delete 56\r\n" }
90
+ end
91
+
92
+ it "should fail after exceeding reconnect max retries" do
93
+ # next connection attempts should fail
94
+ TCPSocket.stubs(:new).times(3).raises(Errno::ECONNREFUSED.new)
95
+ TCPSocket.any_instance.stubs(:readline).times(1).raises(EOFError.new)
96
+
97
+ assert_raises(Beaneater::NotConnected) { @bc.transmit "delete 56\r\n" }
98
+ end
99
+
100
+ it "tubes_watched are restored after reconnect" do
101
+ client = Beaneater.new('127.0.0.1:11300')
102
+ client.tubes.watch! "another"
103
+
104
+ TCPSocket.prepend Module.new {
105
+ def readline
106
+ if !$called
107
+ $called = true
108
+ raise EOFError
109
+ end
110
+
111
+ super
112
+ end
113
+ }
114
+
115
+ assert_equal %w[another], client.tubes.watched.map(&:name)
116
+ end
63
117
  end # transmit
64
118
 
65
119
  describe 'for #close' do