quebert 1.0.9 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -14,5 +14,4 @@ group :test do
14
14
  gem 'sqlite3-ruby'
15
15
  gem 'guard-rspec'
16
16
  gem 'rb-fsevent'
17
- gem 'growl'
18
17
  end
data/Guardfile CHANGED
@@ -1,7 +1,4 @@
1
- # A sample Guardfile
2
- # More info at https://github.com/guard/guard#readme
3
-
4
- guard 'rspec', :version => 1, :cli => '--colour --format nested' do
1
+ guard 'rspec', :version => 2, :cli => '--colour --format nested' do
5
2
  watch(%r{^spec/.+_spec\.rb$})
6
3
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
4
  watch('spec/spec_helper.rb') { "spec" }
@@ -1,36 +1,48 @@
1
- require 'beanstalk-client'
1
+ require 'beaneater'
2
2
 
3
3
  module Quebert
4
4
  module Backend
5
5
 
6
6
  # Manage jobs on a Beanstalk queue out of process
7
- class Beanstalk < Beanstalk::Pool
7
+ class Beanstalk
8
8
  def put(job, *args)
9
- super job.to_json, *args
9
+ priority, delay, ttr = args
10
+ opts = {}
11
+ opts[:pri] = priority unless priority.nil?
12
+ opts[:delay] = delay unless delay.nil?
13
+ opts[:ttr] = ttr unless ttr.nil?
14
+ @tube.put job.to_json, opts
10
15
  end
11
-
12
- def reserve_with_controller
13
- Controller::Beanstalk.new(reserve_without_controller, self)
16
+
17
+ def reserve_without_controller(timeout=nil)
18
+ @tube.reserve timeout
14
19
  end
15
- alias :reserve_without_controller :reserve
16
- alias :reserve :reserve_with_controller
17
-
20
+
21
+ def reserve(timeout=nil)
22
+ Controller::Beanstalk.new reserve_without_controller(timeout), self
23
+ end
24
+
25
+ def peek(state)
26
+ @tube.peek state
27
+ end
28
+
18
29
  # For testing purposes... I think there's a better way to do this though.
19
30
  def drain!
20
- while peek_ready do
31
+ while peek(:ready) do
21
32
  reserve_without_controller.delete
22
33
  end
23
- while peek_delayed do
34
+ while peek(:delayed) do
24
35
  reserve_without_controller.delete
25
36
  end
26
- while job = peek_buried do
27
- last_conn.kick 1 # what? Why the 1? it kicks them all?
37
+ while peek(:buried) do
38
+ @tube.kick
28
39
  reserve_without_controller.delete
29
40
  end
30
41
  end
31
42
 
32
- def initialize(host, *args)
33
- super Array(host), *args
43
+ def initialize(host, tube)
44
+ @pool = Beaneater::Pool.new Array(host)
45
+ @tube = @pool.tubes[tube]
34
46
  end
35
47
  def self.configure(opts={})
36
48
  opts[:host] ||= ['127.0.0.1:11300']
@@ -1,45 +1,47 @@
1
+ require 'optparse'
2
+
1
3
  module Quebert
2
4
  class CommandLineRunner
3
-
5
+
4
6
  attr_accessor :arguments, :command, :options
5
-
7
+
6
8
  def initialize(argv)
7
9
  @argv = argv
8
-
10
+
9
11
  # Default options values
10
12
  @options = {
11
13
  :chdir => Dir.pwd
12
14
  }
13
-
15
+
14
16
  parse!
15
17
  end
16
-
18
+
17
19
  def parser
18
20
  @parser ||= OptionParser.new do |opts|
19
21
  opts.banner = "Usage: quebert [options]"
20
-
22
+
21
23
  opts.separator ""
22
-
23
- opts.on("-l", "--log FILE", "File to redirect output " +
24
+
25
+ opts.on("-l", "--log FILE", "File to redirect output " +
24
26
  "(default: #{@options[:log]})") { |file| @options[:log] = file }
25
- opts.on("-P", "--pid FILE", "File to store PID " +
27
+ opts.on("-P", "--pid FILE", "File to store PID " +
26
28
  "(default: #{@options[:pid]})") { |file| @options[:pid] = file }
27
29
  opts.on("-C", "--config FILE", "Load options from config file") { |file| @options[:config] = file }
28
30
  opts.on("-c", "--chdir DIR", "Change to dir before starting") { |dir| @options[:chdir] = File.expand_path(dir) }
29
31
  end
30
32
  end
31
-
33
+
32
34
  # Parse the options.
33
35
  def parse!
34
36
  parser.parse! @argv
35
37
  @command = @argv.shift
36
38
  @arguments = @argv
37
39
  end
38
-
40
+
39
41
  def self.dispatch(args = ARGV)
40
42
  runner = new(args)
41
43
  params = runner.options
42
-
44
+
43
45
  if dir = params[:chdir]
44
46
  Dir.chdir dir
45
47
  end
@@ -55,10 +57,10 @@ module Quebert
55
57
  if config = params[:config] || auto_config
56
58
  require config
57
59
  end
58
-
60
+
59
61
  Worker.new.start
60
62
  end
61
-
63
+
62
64
  private
63
65
  def self.auto_config
64
66
  rails_env_path = './config/environment.rb'
@@ -52,7 +52,7 @@ module Quebert
52
52
  log "Job deleted", :error
53
53
  rescue Job::Release
54
54
  log "Releasing with priority: #{@job.priority} and delay: #{@job.delay}", :error
55
- beanstalk_job.release @job.priority, @job.delay
55
+ beanstalk_job.release :pri => @job.priority, :delay => @job.delay
56
56
  log "Job released", :error
57
57
  rescue Job::Bury
58
58
  log "Burrying job", :error
@@ -86,10 +86,10 @@ module Quebert
86
86
  log "Job burried"
87
87
  else
88
88
  log "TTR exceeded. Releasing with priority: #{@job.priority} and delay: #{delay}"
89
- beanstalk_job.release @job.priority, delay
89
+ beanstalk_job.release :pri => @job.priority, :delay => delay
90
90
  log "Job released"
91
91
  end
92
- rescue ::Beanstalk::NotFoundError => e
92
+ rescue ::Beaneater::NotFoundError => e
93
93
  log "Job ran longer than allowed. Beanstalk already deleted it!!!!", :error
94
94
  # Sometimes the timer doesn't behave correctly and this job actually runs longer than
95
95
  # allowed. At that point the beanstalk job no longer exists anymore. Lets let it go and don't blow up.
data/lib/quebert/job.rb CHANGED
@@ -6,20 +6,20 @@ module Quebert
6
6
 
7
7
  attr_reader :args
8
8
  attr_accessor :priority, :delay, :ttr
9
-
9
+
10
10
  DEFAULT_JOB_PRIORITY = 65536
11
11
  DEFAULT_JOB_DELAY = 0
12
12
  DEFAULT_JOB_TTR = 10
13
13
 
14
- # A buffer time in seconds added to the Beanstalk TTR for Quebert to do its own job cleanup
14
+ # A buffer time in seconds added to the Beanstalk TTR for Quebert to do its own job cleanup
15
15
  # The job will perform based on the Beanstalk TTR, but Beanstalk hangs on to the job just a
16
16
  # little longer so that Quebert can bury the job or schedule a retry with the appropriate delay
17
17
  QUEBERT_TTR_BUFFER = 5
18
18
 
19
19
  NotImplemented = Class.new(StandardError)
20
-
20
+
21
21
  Action = Class.new(Exception)
22
-
22
+
23
23
  Bury = Class.new(Action)
24
24
  Delete = Class.new(Action)
25
25
  Release = Class.new(Action)
@@ -28,7 +28,7 @@ module Quebert
28
28
 
29
29
  def initialize(*args)
30
30
  opts = args.last.is_a?(::Hash) ? args.pop : nil
31
-
31
+
32
32
  @priority = DEFAULT_JOB_PRIORITY
33
33
  @delay = DEFAULT_JOB_DELAY
34
34
  @ttr = DEFAULT_JOB_TTR
@@ -36,7 +36,7 @@ module Quebert
36
36
  if opts
37
37
  beanstalk_opts = opts.delete(:beanstalk)
38
38
  args << opts unless opts.empty?
39
-
39
+
40
40
  if beanstalk_opts
41
41
  @priority = beanstalk_opts[:priority] if beanstalk_opts[:priority]
42
42
  @delay = beanstalk_opts[:delay] if beanstalk_opts[:delay]
@@ -46,41 +46,37 @@ module Quebert
46
46
 
47
47
  @args = args.dup.freeze
48
48
  end
49
-
49
+
50
50
  def perform(*args)
51
51
  raise NotImplemented
52
52
  end
53
-
53
+
54
54
  # Runs the perform method that somebody else should be implementing
55
55
  def perform!
56
56
  # Honor the timeout and kill the job in ruby-space. Beanstalk
57
57
  # should be cleaning up this job and returning it to the queue
58
58
  # as well.
59
- begin
60
- Quebert::Timeout.timeout(@ttr){ perform(*args) }
61
- rescue ::Timeout::Error => e
62
- raise Job::Timeout, e.message, e.backtrace
63
- end
59
+ Quebert::Timeout.timeout(@ttr, Job::Timeout){ perform(*args) }
64
60
  end
65
-
61
+
66
62
  def enqueue
67
63
  self.class.backend.put self, @priority, @delay, @ttr + QUEBERT_TTR_BUFFER
68
64
  end
69
-
65
+
70
66
  def to_json
71
67
  JSON.generate(Serializer::Job.serialize(self))
72
68
  end
73
-
69
+
74
70
  def self.from_json(json)
75
71
  if hash = JSON.parse(json) and not hash.empty?
76
72
  Serializer::Job.deserialize(hash)
77
73
  end
78
74
  end
79
-
75
+
80
76
  def self.backend=(backend)
81
77
  @backend = backend
82
78
  end
83
-
79
+
84
80
  def self.backend
85
81
  @backend || Quebert.configuration.backend
86
82
  end
@@ -89,13 +85,13 @@ module Quebert
89
85
  def delete!
90
86
  raise Delete
91
87
  end
92
-
88
+
93
89
  def release!
94
90
  raise Release
95
91
  end
96
-
92
+
97
93
  def bury!
98
94
  raise Bury
99
95
  end
100
96
  end
101
- end
97
+ end
@@ -1,3 +1,3 @@
1
1
  module Quebert
2
- VERSION = "1.0.9"
3
- end
2
+ VERSION = "1.11.0"
3
+ end
@@ -3,29 +3,38 @@ module Quebert
3
3
  include Logging
4
4
 
5
5
  attr_accessor :exception_handler, :backend
6
-
6
+
7
7
  def initialize
8
8
  yield self if block_given?
9
9
  end
10
-
10
+
11
11
  # Start the worker backend and intercept exceptions if a handler is provided
12
12
  def start
13
13
  Signal.trap('TERM') { safe_stop }
14
14
  Signal.trap('INT') { safe_stop }
15
15
 
16
16
  logger.info "Worker started with #{backend.class.name} backend\n"
17
- while @controller = backend.reserve do
17
+ while @controller = backend.reserve do
18
18
  begin
19
19
  @controller.perform
20
- rescue Exception => e
21
- exception_handler ? exception_handler.call(e) : raise(e)
22
- end
20
+ rescue Exception => error
21
+ if exception_handler
22
+ exception_handler.call(
23
+ error,
24
+ :controller => @controller,
25
+ :pid => $$,
26
+ :worker => self
27
+ )
28
+ else
29
+ raise error
30
+ end
31
+ end
23
32
  @controller = nil
24
33
 
25
34
  stop if @terminate_sent
26
35
  end
27
36
  end
28
-
37
+
29
38
  def safe_stop
30
39
  if @terminate_sent
31
40
  logger.info "Ok! I get the point. Shutting down immediately."
@@ -41,14 +50,14 @@ module Quebert
41
50
  logger.info "Worker stopping\n"
42
51
  exit 0
43
52
  end
44
-
53
+
45
54
  protected
46
55
  def backend
47
56
  @backend ||= Quebert.config.backend
48
57
  end
49
-
58
+
50
59
  def exception_handler
51
60
  @exception_handler ||= Quebert.config.worker.exception_handler
52
61
  end
53
62
  end
54
- end
63
+ end
data/quebert.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
  # specify any dependencies here; for example:
22
22
  # s.add_development_dependency "rspec"
23
23
  s.add_runtime_dependency "json"
24
- s.add_runtime_dependency "beanstalk-client"
24
+ s.add_runtime_dependency "beaneater"
25
25
 
26
26
  s.add_development_dependency 'rspec', '2.7.0'
27
27
  end
@@ -10,8 +10,10 @@ describe Configuration do
10
10
  backend = @config.backend
11
11
  backend.should be_instance_of(Quebert::Backend::Beanstalk)
12
12
  # Blech, gross nastiness in their lib, but we need to look in to see if this stuff as configed
13
- backend.instance_variable_get('@addrs').should eql(['localhost:11300'])
14
- backend.instance_variable_get('@default_tube').should eql('quebert-config-test')
13
+ pool = backend.instance_variable_get('@pool')
14
+ pool.connections.first.address.should eql('localhost:11300')
15
+ tube = backend.instance_variable_get('@tube')
16
+ tube.name.should eql('quebert-config-test')
15
17
  end
16
18
 
17
19
  end
@@ -25,63 +25,63 @@ describe Controller::Beanstalk do
25
25
 
26
26
  it "should delete job off queue after succesful run" do
27
27
  @q.put Adder.new(1, 2)
28
- @q.peek_ready.should_not be_nil
28
+ @q.peek(:ready).should_not be_nil
29
29
  @q.reserve.perform.should eql(3)
30
- @q.peek_ready.should be_nil
30
+ @q.peek(:ready).should be_nil
31
31
  end
32
32
 
33
33
  it "should bury job if an exception occurs in job" do
34
34
  @q.put Exceptional.new
35
- @q.peek_ready.should_not be_nil
35
+ @q.peek(:ready).should_not be_nil
36
36
  lambda{ @q.reserve.perform }.should raise_exception
37
- @q.peek_buried.should_not be_nil
37
+ @q.peek(:buried).should_not be_nil
38
38
  end
39
39
 
40
40
  it "should bury an AR job if an exception occurs deserializing it" do
41
41
  @user = User.new(:first_name => "John", :last_name => "Doe", :email => "jdoe@gmail.com")
42
42
  @user.id = 1
43
43
  @q.put Serializer::ActiveRecord.serialize(@user)
44
- @q.peek_ready.should_not be_nil
44
+ @q.peek(:ready).should_not be_nil
45
45
  lambda{ @q.reserve.perform }.should raise_exception
46
- @q.peek_buried.should_not be_nil
46
+ @q.peek(:buried).should_not be_nil
47
47
  end
48
48
 
49
49
  context "job actions" do
50
50
  it "should delete job" do
51
51
  @q.put DeleteJob.new
52
- @q.peek_ready.should_not be_nil
52
+ @q.peek(:ready).should_not be_nil
53
53
  @q.reserve.perform
54
- @q.peek_ready.should be_nil
54
+ @q.peek(:ready).should be_nil
55
55
  end
56
56
 
57
57
  it "should release job" do
58
58
  @q.put ReleaseJob.new
59
- @q.peek_ready.should_not be_nil
59
+ @q.peek(:ready).should_not be_nil
60
60
  @q.reserve.perform
61
- @q.peek_ready.should_not be_nil
61
+ @q.peek(:ready).should_not be_nil
62
62
  end
63
63
 
64
64
  it "should bury job" do
65
65
  @q.put BuryJob.new
66
- @q.peek_ready.should_not be_nil
67
- @q.peek_buried.should be_nil
66
+ @q.peek(:ready).should_not be_nil
67
+ @q.peek(:buried).should be_nil
68
68
  @q.reserve.perform
69
- @q.peek_ready.should be_nil
70
- @q.peek_buried.should_not be_nil
69
+ @q.peek(:ready).should be_nil
70
+ @q.peek(:buried).should_not be_nil
71
71
  end
72
72
  end
73
73
 
74
74
  it "should retry a job with a delay and then bury" do
75
75
  TimeoutJob.backend = @q
76
76
  TimeoutJob.new.enqueue
77
- @q.peek_ready.should_not be_nil
77
+ @q.peek(:ready).should_not be_nil
78
78
  job = @q.reserve
79
79
  job.beanstalk_job.stats["releases"].should eql(0)
80
80
  job.beanstalk_job.stats["delay"].should eql(0)
81
81
  lambda{job.perform}.should raise_exception(Quebert::Job::Timeout)
82
82
 
83
- @q.peek_ready.should be_nil
84
- beanstalk_job = @q.peek_delayed
83
+ @q.peek(:ready).should be_nil
84
+ beanstalk_job = @q.peek(:delayed)
85
85
  beanstalk_job.should_not be_nil
86
86
  beanstalk_job.stats["releases"].should eql(1)
87
87
  beanstalk_job.stats["delay"].should eql(Quebert::Controller::Beanstalk::TIMEOUT_RETRY_GROWTH_RATE**beanstalk_job.stats["releases"])
@@ -92,8 +92,8 @@ describe Controller::Beanstalk do
92
92
  Quebert::Controller::Beanstalk::MAX_TIMEOUT_RETRY_DELAY = 1
93
93
  lambda{@q.reserve.perform}.should raise_exception(Quebert::Job::Timeout)
94
94
 
95
- @q.peek_ready.should be_nil
96
- @q.peek_delayed.should be_nil
97
- @q.peek_buried.should_not be_nil
95
+ @q.peek(:ready).should be_nil
96
+ @q.peek(:delayed).should be_nil
97
+ @q.peek(:buried).should_not be_nil
98
98
  end
99
99
  end
data/spec/worker_spec.rb CHANGED
@@ -7,11 +7,11 @@ describe Worker do
7
7
  w.backend = @q
8
8
  end
9
9
  end
10
-
10
+
11
11
  it "should start" do
12
12
  @w.start
13
13
  end
14
-
14
+
15
15
  context "pluggable exception handler" do
16
16
  it "should raise exception if nothing is provided" do
17
17
  @q.put Exceptional.new
@@ -20,15 +20,14 @@ describe Worker do
20
20
 
21
21
  it "should default to Quebert.config.worker.exception_handler handler" do
22
22
  @q.put Exceptional.new
23
- Quebert.config.worker.exception_handler = Proc.new{|e| e.should be_instance_of(Exception) }
24
- @w.exception_handler = Proc.new{|e| e.should be_instance_of(Exception) }
23
+ Quebert.config.worker.exception_handler = Proc.new{|e, opts| e.should be_instance_of(Exception) }
25
24
  lambda{ @w.start }.should_not raise_exception
26
25
  end
27
-
26
+
28
27
  it "should intercept exceptions" do
29
28
  @q.put Exceptional.new
30
- @w.exception_handler = Proc.new{|e| e.should be_instance_of(Exception) }
29
+ @w.exception_handler = Proc.new{|e, opts| e.should be_instance_of(Exception) }
31
30
  lambda{ @w.start }.should_not raise_exception
32
31
  end
33
32
  end
34
- end
33
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quebert
3
3
  version: !ruby/object:Gem::Version
4
- hash: 5
4
+ hash: 59
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
+ - 11
8
9
  - 0
9
- - 9
10
- version: 1.0.9
10
+ version: 1.11.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brad Gessler
@@ -17,7 +17,8 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2012-06-06 00:00:00 Z
20
+ date: 2012-12-21 00:00:00 -06:00
21
+ default_executable:
21
22
  dependencies:
22
23
  - !ruby/object:Gem::Dependency
23
24
  name: json
@@ -34,7 +35,7 @@ dependencies:
34
35
  type: :runtime
35
36
  version_requirements: *id001
36
37
  - !ruby/object:Gem::Dependency
37
- name: beanstalk-client
38
+ name: beaneater
38
39
  prerelease: false
39
40
  requirement: &id002 !ruby/object:Gem::Requirement
40
41
  none: false
@@ -121,6 +122,7 @@ files:
121
122
  - spec/support/jobs.rb
122
123
  - spec/support_spec.rb
123
124
  - spec/worker_spec.rb
125
+ has_rdoc: true
124
126
  homepage: http://github.com/polleverywhere/quebert
125
127
  licenses: []
126
128
 
@@ -150,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
152
  requirements: []
151
153
 
152
154
  rubyforge_project: quebert
153
- rubygems_version: 1.8.22
155
+ rubygems_version: 1.6.2
154
156
  signing_key:
155
157
  specification_version: 3
156
158
  summary: A worker queue framework built around beanstalkd