sneakers 2.6.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c52a398aecf272a36fcb3431f7b242a0d36dc0d1
4
- data.tar.gz: c5974b0f7823f4d1e91c99d88adc3bbe50a2e572
3
+ metadata.gz: '09f8536528b4fa33c1d597f1ca8f513d5ca8f37b'
4
+ data.tar.gz: c24a191dc06d4457c9f5ac7bfd8f089ca7ed175f
5
5
  SHA512:
6
- metadata.gz: 85bc282b42260ebb6a08fdab01fc2396a30450f3cabb93bed27ccac801bc216e5e139613349cfb874a9c5c2a453234591759671de64c9f7654ddd5076384f746
7
- data.tar.gz: 06fd1978121e6e59971152fd202e1cb2d5b13225d0f08f75583a5d021b0cfb8d52768102a007cef7d1dc191ed9264505a9e4157987054bc9631ee3eaaf749605
6
+ metadata.gz: 8e24898e348b40e79640d216fa9c56b63bf8c94340f23b4e54a4a163fdcbee8c7628a3db7f3128d3ed0220e9e7f83b36239917e610a9ca5b9c481108847cc61c
7
+ data.tar.gz: 369b932fef00b62847c74e070cd6248203505bf87ff3d932de8eb9ef80bcd65dd7732d59f7b47def85b4746c4b9cce0e933c0bb0b6845fe2f40f8f6a33b8e07b
data/.gitignore CHANGED
@@ -7,3 +7,4 @@ coverage/
7
7
  tmp/
8
8
  .ruby-version
9
9
  .ruby-gemset
10
+ .bundle/*
@@ -0,0 +1,67 @@
1
+ # Sneakers Change Log
2
+
3
+ ## Changes Between 2.6.0 and 2.7.0
4
+
5
+ This release requires Ruby 2.2 and has **breaking API changes**
6
+ around custom error handlers.
7
+
8
+ ### Use Provided Connections in WorkerGroup
9
+
10
+ It is now possible to use a custom connection instance in worker groups.
11
+
12
+ Contributed by @pomnikita.
13
+
14
+ GitHub issue: [#322](https://github.com/jondot/sneakers/pull/322)
15
+
16
+
17
+ ### Worker Context Available to Worker Instances
18
+
19
+ Contributed by Jason Lombardozzi.
20
+
21
+ GitHub issue: [#307](https://github.com/jondot/sneakers/pull/307)
22
+
23
+
24
+ ### Ruby 2.2 Requirement
25
+
26
+ Sneakers now [requires Ruby 2.2](https://github.com/jondot/sneakers/commit/f33246a1bd3b5fe53ee662253dc5bac7864eec97).
27
+
28
+
29
+ ### Bunny 2.9.x
30
+
31
+ Bunny was [upgraded](https://github.com/jondot/sneakers/commit/c7fb0bd23280082e43065d7199668486db005c13) to 2.9.x.
32
+
33
+
34
+
35
+ ### Server Engine 2.0.5
36
+
37
+ Server Engine dependency was [upgraded to 2.0.5](https://github.com/jondot/sneakers/commit/3f60fd5e88822169fb04088f0ce5d2f94f803339).
38
+
39
+
40
+ ### Refactored Publisher Connection
41
+
42
+ Contributed by Christoph Wagner.
43
+
44
+ GitHub issue: [#325](https://github.com/jondot/sneakers/pull/325)
45
+
46
+
47
+ ### New Relic Reporter Migrated to the Modern API
48
+
49
+ Contributed by @adamors.
50
+
51
+ GitHub issue: [#324](https://github.com/jondot/sneakers/pull/324)
52
+
53
+
54
+ ### Configuration Logged at Debug Level
55
+
56
+ To avoid potentially leaking credentials in the log.
57
+
58
+ Contributed by Kimmo Lehto.
59
+
60
+ GitHub issue: [#301](https://github.com/jondot/sneakers/pull/301).
61
+
62
+
63
+ ### Comment Corrections
64
+
65
+ Contributed by Andrew Babichev
66
+
67
+ GitHub issue: [#346](https://github.com/jondot/sneakers/pull/346)
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'ruby-prof', platforms: [:ruby_22, :ruby_23, :ruby_24]
4
+
3
5
  gemspec
data/README.md CHANGED
@@ -18,8 +18,8 @@ Sneakers is being used in production for both I/O and CPU intensive workloads, a
18
18
 
19
19
 
20
20
  Visit the [wiki](https://github.com/jondot/sneakers/wiki) for
21
- complete docs.
22
-
21
+ documentation and [GitHub releases](https://github.com/jondot/sneakers/releases) for release
22
+ notes.
23
23
 
24
24
  [![Build Status](https://travis-ci.org/jondot/sneakers.svg?branch=master)](https://travis-ci.org/jondot/sneakers)
25
25
 
@@ -172,7 +172,7 @@ environment needed locally on your development box.
172
172
  running `script/local_worker`. This will use docker-compose as well. It will
173
173
  also help you get a feeling for how to run Sneakers in a Docker based
174
174
  production environment
175
- * User `Dockerfile.slim` instead of `Dockerfile` for production docker builds.
175
+ * Use `Dockerfile.slim` instead of `Dockerfile` for production docker builds.
176
176
  It generates a more compact image, while the "regular" `Dockerfile` generates
177
177
  a fatter image - yet faster to iterate when developing
178
178
 
@@ -79,7 +79,7 @@ module Sneakers
79
79
 
80
80
  # Register a proc to handle any error which occurs within the Sneakers process.
81
81
  #
82
- # Sneakers.error_reporters << proc {|ex,ctx_hash| MyErrorService.notify(ex, ctx_hash) }
82
+ # Sneakers.error_reporters << proc { |exception, worker, context_hash| MyErrorService.notify(exception, context_hash) }
83
83
  #
84
84
  # The default error handler logs errors to Sneakers.logger.
85
85
  # Ripped off from https://github.com/mperham/sidekiq/blob/6ad6a3aa330deebd76c6cf0d353f66abd3bef93b/lib/sidekiq.rb#L165-L174
@@ -2,10 +2,10 @@
2
2
  module Sneakers
3
3
  module ErrorReporter
4
4
  class DefaultLogger
5
- def call(exception, context_hash)
5
+ def call(exception, worker, context_hash)
6
6
  Sneakers.logger.warn(context_hash) unless context_hash.empty?
7
7
  log_string = ''
8
- log_string += "[Exception error=#{exception.message.inspect} error_class=#{exception.class}" unless exception.nil?
8
+ log_string += "[Exception error=#{exception.message.inspect} error_class=#{exception.class} worker_class=#{worker.class}" unless exception.nil?
9
9
  log_string += " backtrace=#{exception.backtrace.take(50).join(',')}" unless exception.nil? || exception.backtrace.nil?
10
10
  log_string += ']'
11
11
  Sneakers.logger.error log_string
@@ -15,7 +15,7 @@ module Sneakers
15
15
  def worker_error(exception, context_hash = {})
16
16
  Sneakers.error_reporters.each do |handler|
17
17
  begin
18
- handler.call(exception, context_hash)
18
+ handler.call(exception, self, context_hash)
19
19
  rescue => inner_exception
20
20
  Sneakers.logger.error '!!! ERROR REPORTER THREW AN ERROR !!!'
21
21
  Sneakers.logger.error inner_exception
@@ -15,7 +15,8 @@ module Sneakers
15
15
  end
16
16
 
17
17
  def record_stat(metric, num)
18
- stats(metric).record_data_point(num)
18
+ metric_name = "Custom/#{metric.gsub("\.", "\/")}"
19
+ NewrelicMetrics.eagent::Agent.record_metric(metric_name, num)
19
20
  rescue Exception => e
20
21
  puts "NewrelicMetrics#record_stat: #{e}"
21
22
  end
@@ -25,12 +26,6 @@ module Sneakers
25
26
  block.call
26
27
  record_stat(metric, ((Time.now - start)*1000).floor)
27
28
  end
28
-
29
- def stats(metric)
30
- metric.gsub! "\.", "\/"
31
- NewrelicMetrics.eagent::Agent.get_stats("Custom/#{metric}")
32
- end
33
-
34
29
  end
35
30
  end
36
31
  end
@@ -1,28 +1,32 @@
1
1
  module Sneakers
2
2
  class Publisher
3
+
4
+ attr_reader :exchange, :channel
5
+
3
6
  def initialize(opts = {})
4
7
  @mutex = Mutex.new
5
8
  @opts = Sneakers::CONFIG.merge(opts)
9
+ # If we've already got a bunny object, use it. This allows people to
10
+ # specify all kinds of options we don't need to know about (e.g. for ssl).
11
+ @bunny = @opts[:connection]
6
12
  end
7
13
 
8
14
  def publish(msg, options = {})
9
- @mutex.synchronize do
10
- ensure_connection! unless connected?
11
- end
15
+ ensure_connection!
12
16
  to_queue = options.delete(:to_queue)
13
17
  options[:routing_key] ||= to_queue
14
18
  Sneakers.logger.info {"publishing <#{msg}> to [#{options[:routing_key]}]"}
15
19
  @exchange.publish(ContentType.serialize(msg, options[:content_type]), options)
16
20
  end
17
21
 
18
-
19
- attr_reader :exchange
22
+ def ensure_connection!
23
+ @mutex.synchronize do
24
+ connect! unless connected?
25
+ end
26
+ end
20
27
 
21
28
  private
22
- def ensure_connection!
23
- # If we've already got a bunny object, use it. This allows people to
24
- # specify all kinds of options we don't need to know about (e.g. for ssl).
25
- @bunny = @opts[:connection]
29
+ def connect!
26
30
  @bunny ||= create_bunny_connection
27
31
  @bunny.start
28
32
  @channel = @bunny.create_channel
@@ -30,7 +34,7 @@ module Sneakers
30
34
  end
31
35
 
32
36
  def connected?
33
- @bunny && @bunny.connected?
37
+ @bunny && @bunny.connected? && channel
34
38
  end
35
39
 
36
40
  def create_bunny_connection
@@ -1,6 +1,6 @@
1
1
 
2
2
  class Sneakers::Queue
3
- attr_reader :name, :opts, :exchange
3
+ attr_reader :name, :opts, :exchange, :channel
4
4
 
5
5
  def initialize(name, opts)
6
6
  @name = name
@@ -58,8 +58,7 @@ module Sneakers
58
58
  Sneakers::CONFIG[:hooks][hook] = config.delete(hook) if config[hook]
59
59
  end
60
60
 
61
-
62
- Sneakers.logger.info("New configuration: #{config.inspect}")
61
+ Sneakers.logger.debug("New configuration: #{config.inspect}")
63
62
  config
64
63
  end
65
64
 
@@ -73,6 +72,7 @@ module Sneakers
73
72
  serverengine_config = Sneakers::CONFIG.merge(@conf)
74
73
  serverengine_config.merge!(
75
74
  :logger => Sneakers.logger,
75
+ :log_level => Sneakers.logger.level,
76
76
  :worker_type => 'process',
77
77
  :worker_classes => @worker_classes,
78
78
 
@@ -1,3 +1,3 @@
1
1
  module Sneakers
2
- VERSION = "2.6.0"
2
+ VERSION = "2.7.0"
3
3
  end
@@ -19,7 +19,7 @@ module Sneakers
19
19
 
20
20
  @should_ack = opts[:ack]
21
21
  @timeout_after = opts[:timeout_job_after]
22
- @pool = pool || Concurrent::FixedThreadPool.new(opts[:threads])
22
+ @pool = pool || Concurrent::FixedThreadPool.new(opts[:threads] || Sneakers::Configuration::DEFAULTS[:threads])
23
23
  @call_with_params = respond_to?(:work_with_params)
24
24
  @content_type = opts[:content_type]
25
25
 
@@ -47,53 +47,57 @@ module Sneakers
47
47
  worker_trace "Working off: #{msg.inspect}"
48
48
 
49
49
  @pool.post do
50
- res = nil
51
- error = nil
52
-
53
- begin
54
- metrics.increment("work.#{self.class.name}.started")
55
- Timeout.timeout(@timeout_after, WorkerTimeout) do
56
- metrics.timing("work.#{self.class.name}.time") do
57
- deserialized_msg = ContentType.deserialize(msg, @content_type || metadata && metadata[:content_type])
58
- if @call_with_params
59
- res = work_with_params(deserialized_msg, delivery_info, metadata)
60
- else
61
- res = work(deserialized_msg)
62
- end
50
+ process_work(delivery_info, metadata, msg, handler)
51
+ end
52
+ end
53
+
54
+ def process_work(delivery_info, metadata, msg, handler)
55
+ res = nil
56
+ error = nil
57
+
58
+ begin
59
+ metrics.increment("work.#{self.class.name}.started")
60
+ Timeout.timeout(@timeout_after, WorkerTimeout) do
61
+ metrics.timing("work.#{self.class.name}.time") do
62
+ deserialized_msg = ContentType.deserialize(msg, @content_type || metadata && metadata[:content_type])
63
+ if @call_with_params
64
+ res = work_with_params(deserialized_msg, delivery_info, metadata)
65
+ else
66
+ res = work(deserialized_msg)
63
67
  end
64
68
  end
65
- rescue WorkerTimeout => ex
66
- res = :timeout
67
- worker_error(ex, log_msg: log_msg(msg), class: self.class.name,
68
- message: msg, delivery_info: delivery_info, metadata: metadata)
69
- rescue => ex
70
- res = :error
71
- error = ex
72
- worker_error(ex, log_msg: log_msg(msg), class: self.class.name,
73
- message: msg, delivery_info: delivery_info, metadata: metadata)
74
69
  end
70
+ rescue WorkerTimeout => ex
71
+ res = :timeout
72
+ worker_error(ex, log_msg: log_msg(msg), class: self.class.name,
73
+ message: msg, delivery_info: delivery_info, metadata: metadata)
74
+ rescue => ex
75
+ res = :error
76
+ error = ex
77
+ worker_error(ex, log_msg: log_msg(msg), class: self.class.name,
78
+ message: msg, delivery_info: delivery_info, metadata: metadata)
79
+ end
75
80
 
76
- if @should_ack
77
-
78
- if res == :ack
79
- # note to future-self. never acknowledge multiple (multiple=true) messages under threads.
80
- handler.acknowledge(delivery_info, metadata, msg)
81
- elsif res == :timeout
82
- handler.timeout(delivery_info, metadata, msg)
83
- elsif res == :error
84
- handler.error(delivery_info, metadata, msg, error)
85
- elsif res == :reject
86
- handler.reject(delivery_info, metadata, msg)
87
- elsif res == :requeue
88
- handler.reject(delivery_info, metadata, msg, true)
89
- else
90
- handler.noop(delivery_info, metadata, msg)
91
- end
92
- metrics.increment("work.#{self.class.name}.handled.#{res || 'noop'}")
81
+ if @should_ack
82
+
83
+ if res == :ack
84
+ # note to future-self. never acknowledge multiple (multiple=true) messages under threads.
85
+ handler.acknowledge(delivery_info, metadata, msg)
86
+ elsif res == :timeout
87
+ handler.timeout(delivery_info, metadata, msg)
88
+ elsif res == :error
89
+ handler.error(delivery_info, metadata, msg, error)
90
+ elsif res == :reject
91
+ handler.reject(delivery_info, metadata, msg)
92
+ elsif res == :requeue
93
+ handler.reject(delivery_info, metadata, msg, true)
94
+ else
95
+ handler.noop(delivery_info, metadata, msg)
93
96
  end
97
+ metrics.increment("work.#{self.class.name}.handled.#{res || 'noop'}")
98
+ end
94
99
 
95
- metrics.increment("work.#{self.class.name}.ended")
96
- end #post
100
+ metrics.increment("work.#{self.class.name}.ended")
97
101
  end
98
102
 
99
103
  def stop
@@ -152,4 +156,3 @@ module Sneakers
152
156
  end
153
157
  end
154
158
  end
155
-
@@ -29,13 +29,13 @@ module Sneakers
29
29
  worker_classes = worker_classes.call
30
30
  end
31
31
 
32
- # if we don't provide a connection to a worker,
32
+ # If we don't provide a connection to a worker,
33
33
  # the queue used in the worker will create a new one
34
- # so if we want to have a shared bunny connection for the workers
35
- # we must create it here
36
- bunny_connection = create_connection_or_nil
37
34
 
38
- @workers = worker_classes.map{|w| w.new(nil, pool, {connection: bunny_connection}) }
35
+ @workers = worker_classes.map do |worker_class|
36
+ worker_class.new(nil, pool, { connection: config[:connection] })
37
+ end
38
+
39
39
  # if more than one worker this should be per worker
40
40
  # accumulate clients and consumers as well
41
41
  @workers.each do |worker|
@@ -47,7 +47,6 @@ module Sneakers
47
47
  Sneakers.logger.debug("Heartbeat: running threads [#{Thread.list.count}]")
48
48
  # report aggregated stats?
49
49
  end
50
-
51
50
  end
52
51
 
53
52
  def stop
@@ -57,14 +56,5 @@ module Sneakers
57
56
  end
58
57
  @stop_flag.set!
59
58
  end
60
-
61
- def create_bunny_connection
62
- Bunny.new(Sneakers::CONFIG[:amqp], :vhost => Sneakers::CONFIG[:vhost], :heartbeat => Sneakers::CONFIG[:heartbeat], :logger => Sneakers::logger)
63
- end
64
-
65
- def create_connection_or_nil
66
- config[:share_bunny_connection] ? create_bunny_connection : nil
67
- end
68
- private :create_bunny_connection, :create_connection_or_nil
69
59
  end
70
60
  end
@@ -12,14 +12,14 @@ Gem::Specification.new do |gem|
12
12
  gem.summary = %q( Fast background processing framework for Ruby and RabbitMQ )
13
13
  gem.homepage = 'http://sneakers.io'
14
14
  gem.license = 'MIT'
15
- gem.required_ruby_version = Gem::Requirement.new(">= 2.0")
15
+ gem.required_ruby_version = Gem::Requirement.new(">= 2.2")
16
16
 
17
17
  gem.files = `git ls-files`.split($/).reject { |f| f == 'Gemfile.lock' }
18
18
  gem.executables = gem.files.grep(/^bin/).map { |f| File.basename(f) }
19
19
  gem.test_files = gem.files.grep(/^(test|spec|features)\//)
20
20
  gem.require_paths = ['lib']
21
- gem.add_dependency 'serverengine', '~> 1.5.11'
22
- gem.add_dependency 'bunny', '~> 2.7.0'
21
+ gem.add_dependency 'serverengine', '~> 2.0.5'
22
+ gem.add_dependency 'bunny', '~> 2.9.2'
23
23
  gem.add_dependency 'concurrent-ruby', '~> 1.0'
24
24
  gem.add_dependency 'thor'
25
25
 
@@ -29,7 +29,6 @@ Gem::Specification.new do |gem|
29
29
 
30
30
  gem.add_development_dependency 'rr'
31
31
  gem.add_development_dependency 'unparser', '0.2.2' # keep below 0.2.5 for ruby 2.0 compat.
32
- gem.add_development_dependency 'ruby-prof'
33
32
  gem.add_development_dependency 'guard-minitest'
34
33
  gem.add_development_dependency 'metric_fu'
35
34
  gem.add_development_dependency 'simplecov'
@@ -38,4 +37,3 @@ Gem::Specification.new do |gem|
38
37
  gem.add_development_dependency 'minitest'
39
38
  gem.add_development_dependency 'guard'
40
39
  end
41
-
@@ -4,6 +4,7 @@ require 'sneakers/runner'
4
4
  require 'fixtures/integration_worker'
5
5
 
6
6
  require "rabbitmq/http/client"
7
+ require 'timeout'
7
8
 
8
9
 
9
10
  describe "integration" do
@@ -17,23 +18,30 @@ describe "integration" do
17
18
  puts msg if ENV['INTEGRATION_LOG']
18
19
  end
19
20
 
21
+ def rmq_addr
22
+ @rmq_addr ||= compose_or_localhost("rabbitmq")
23
+ end
24
+
25
+ def admin
26
+ @admin ||=
27
+ begin
28
+ puts "RABBITMQ is at #{rmq_addr}"
29
+ RabbitMQ::HTTP::Client.new("http://#{rmq_addr}:15672/", username: "guest", password: "guest")
30
+ rescue
31
+ fail "Rabbitmq admin seems to not exist? you better be running this on Travis or Docker. proceeding.\n#{$!}"
32
+ end
33
+ end
34
+
20
35
  def prepare
21
36
  # clean up all integration queues; admin interface must be installed
22
37
  # in integration env
23
- rmq_addr = compose_or_localhost("rabbitmq")
24
- puts "RABBITMQ is at #{rmq_addr}"
25
- begin
26
- admin = RabbitMQ::HTTP::Client.new("http://#{rmq_addr}:15672/", username: "guest", password: "guest")
27
- qs = admin.list_queues
28
- qs.each do |q|
29
- name = q.name
30
- if name.start_with? 'integration_'
31
- admin.delete_queue('/', name)
32
- integration_log "cleaning up #{name}."
33
- end
38
+ qs = admin.list_queues
39
+ qs.each do |q|
40
+ name = q.name
41
+ if name.start_with? 'integration_'
42
+ admin.delete_queue('/', name)
43
+ integration_log "cleaning up #{name}."
34
44
  end
35
- rescue
36
- puts "Rabbitmq admin seems to not exist? you better be running this on Travis or Docker. proceeding.\n#{$!}"
37
45
  end
38
46
 
39
47
  Sneakers.clear!
@@ -85,22 +93,27 @@ describe "integration" do
85
93
  pid
86
94
  end
87
95
 
88
- def any_consumers
89
- rmq_addr = compose_or_localhost("rabbitmq")
90
- result = false
91
- begin
92
- admin = RabbitMQ::HTTP::Client.new("http://#{rmq_addr}:15672/", username: "guest", password: "guest")
93
- qs = admin.list_queues
94
- qs.each do |q|
95
- if q.name.start_with? 'integration_'
96
- puts "We see #{q.consumers} consumers on #{q.name}"
97
- return true if q.consumers > 0
98
- end
96
+ def consumers_count
97
+ qs = admin.list_queues
98
+ qs.each do |q|
99
+ if q.name.start_with? 'integration_'
100
+ return [q.consumers, q.name]
99
101
  end
100
- return false
101
- rescue
102
- puts "Rabbitmq admin seems to not exist? you better be running this on Travis or Docker. proceeding.\n#{$!}"
103
102
  end
103
+ return [0, nil]
104
+ end
105
+
106
+ def assert_any_consumers(consumers_should_be_there, maximum_wait_time = 15)
107
+ Timeout::timeout(maximum_wait_time) do
108
+ loop do
109
+ consumers, queue = consumers_count
110
+ fail 'no queues so no consumers' if consumers_should_be_there && !queue
111
+ puts "We see #{consumers} consumers on #{queue}"
112
+ (consumers_should_be_there == consumers.zero?) ? sleep(1) : return
113
+ end
114
+ end
115
+ rescue Timeout::Error
116
+ fail "Consumers should #{'not' unless consumers_should_be_there} be here but #{consumers} consumers were after #{maximum_wait_time}s waiting."
104
117
  end
105
118
 
106
119
  it 'should be possible to terminate when queue is full' do
@@ -116,11 +129,10 @@ describe "integration" do
116
129
  end
117
130
 
118
131
  pid = start_worker(IntegrationWorker)
119
- any_consumers.must_equal true
132
+ assert_any_consumers true
120
133
  integration_log "Killing #{pid} now!"
121
134
  Process.kill("TERM", pid)
122
- sleep(2)
123
- any_consumers.must_equal false
135
+ assert_any_consumers false
124
136
  end
125
137
 
126
138
  it 'should pull down 100 jobs from a real queue' do
@@ -144,5 +156,3 @@ describe "integration" do
144
156
 
145
157
  end
146
158
  end
147
-
148
-
@@ -63,7 +63,7 @@ describe Sneakers::Publisher do
63
63
  p.instance_variable_set(:@exchange, xchg)
64
64
 
65
65
  mock(p).connected? { true }
66
- mock(p).ensure_connection!.times(0)
66
+ mock(p).connect!.times(0)
67
67
 
68
68
  p.publish('test msg', to_queue: 'downloads')
69
69
  end
@@ -95,34 +95,46 @@ describe Sneakers::Publisher do
95
95
  p.publish('test msg', to_queue: 'downloads')
96
96
  end
97
97
 
98
- it 'should use an externally instantiated bunny session if provided' do
99
- logger = Logger.new('/dev/null')
100
- channel = Object.new
101
- exchange = Object.new
102
- existing_session = Bunny.new
103
- my_vars = pub_vars.merge(to_queue: 'downloads')
104
-
105
- mock(existing_session).start
106
- mock(existing_session).create_channel { channel }
107
-
108
- mock(channel).exchange('another_exchange', type: :topic, durable: false, :auto_delete => false, arguments: { 'x-arg' => 'value' }) do
109
- exchange
98
+ describe 'externally instantiated bunny session' do
99
+ let(:my_vars) { pub_vars.merge(to_queue: 'downloads') }
100
+ before do
101
+ logger = Logger.new('/dev/null')
102
+ channel = Object.new
103
+ exchange = Object.new
104
+ existing_session = Bunny.new
105
+
106
+ mock(existing_session).start
107
+ mock(existing_session).create_channel { channel }
108
+
109
+ mock(channel).exchange('another_exchange', type: :topic, durable: false, :auto_delete => false, arguments: { 'x-arg' => 'value' }) do
110
+ exchange
111
+ end
112
+
113
+ mock(exchange).publish('test msg', my_vars)
114
+
115
+ Sneakers.configure(
116
+ connection: existing_session,
117
+ heartbeat: 1, exchange: 'another_exchange',
118
+ exchange_type: :topic,
119
+ exchange_arguments: { 'x-arg' => 'value' },
120
+ log: logger,
121
+ durable: false
122
+ )
123
+ @existing_session = existing_session
110
124
  end
111
125
 
112
- mock(exchange).publish('test msg', my_vars)
113
-
114
- Sneakers.configure(
115
- connection: existing_session,
116
- heartbeat: 1, exchange: 'another_exchange',
117
- exchange_type: :topic,
118
- exchange_arguments: { 'x-arg' => 'value' },
119
- log: logger,
120
- durable: false
121
- )
126
+ it 'can handle an existing connection that is offline' do
127
+ p = Sneakers::Publisher.new
128
+ p.publish('test msg', my_vars)
129
+ p.instance_variable_get(:@bunny).must_equal @existing_session
130
+ end
122
131
 
123
- p = Sneakers::Publisher.new
124
- p.publish('test msg', my_vars)
125
- p.instance_variable_get(:@bunny).must_equal existing_session
132
+ it 'can handle an existing connection that is online' do
133
+ mock(@existing_session).connected? { true }
134
+ p = Sneakers::Publisher.new
135
+ p.publish('test msg', my_vars)
136
+ p.instance_variable_get(:@bunny).must_equal @existing_session
137
+ end
126
138
  end
127
139
 
128
140
  it 'should publish using the content type serializer' do
@@ -1,6 +1,7 @@
1
1
  require 'logger'
2
2
  require 'spec_helper'
3
3
  require 'sneakers'
4
+ require 'sneakers/runner'
4
5
 
5
6
  describe Sneakers::Runner do
6
7
  let(:logger) { Logger.new('logtest.log') }
@@ -29,10 +30,10 @@ describe Sneakers::RunnerConfig do
29
30
  let(:logger) { Logger.new("logtest.log") }
30
31
  let(:runner_config) { Sneakers::Runner.new([]).instance_variable_get("@runnerconfig") }
31
32
 
32
-
33
-
34
33
  describe "with a connection" do
35
- before { Sneakers.configure(log: logger, connection: Object.new) }
34
+ let(:connection) { Object.new }
35
+
36
+ before { Sneakers.configure(log: logger, connection: connection) }
36
37
 
37
38
  describe "#reload_config!" do
38
39
  it "does not throw exception" do
@@ -46,6 +47,10 @@ describe Sneakers::RunnerConfig do
46
47
  it "must have :logger key as an instance of Logger" do
47
48
  runner_config.reload_config![:logger].is_a?(Logger).must_equal true
48
49
  end
50
+
51
+ it "must have :connection" do
52
+ runner_config.reload_config![:connection].is_a?(Object).must_equal true
53
+ end
49
54
  end
50
55
  end
51
56
 
@@ -313,6 +313,22 @@ describe Sneakers::Worker do
313
313
  DummyWorker.new.id.must_match(/^worker-/)
314
314
  end
315
315
  end
316
+
317
+ describe 'when connection provided' do
318
+ before do
319
+ @connection = Bunny.new(host: 'any-host.local')
320
+ Sneakers.configure(
321
+ exchange: 'some-exch',
322
+ exchange_options: { type: :direct },
323
+ connection: @connection,
324
+ )
325
+ end
326
+
327
+ it "should build a queue with given connection" do
328
+ @dummy_q = DummyWorker.new.queue
329
+ @dummy_q.opts[:connection].must_equal(@connection)
330
+ end
331
+ end
316
332
  end
317
333
 
318
334
 
@@ -384,7 +400,7 @@ describe Sneakers::Worker do
384
400
  handler = Object.new
385
401
  header = Object.new
386
402
  mock(handler).error(header, nil, "msg", anything)
387
- mock(w.logger).error(/\[Exception error="foo" error_class=RuntimeError backtrace=.*/)
403
+ mock(w.logger).error(/\[Exception error="foo" error_class=RuntimeError worker_class=AcksWorker backtrace=.*/)
388
404
  w.do_work(header, nil, "msg", handler)
389
405
  end
390
406
 
@@ -393,7 +409,7 @@ describe Sneakers::Worker do
393
409
  header = Object.new
394
410
  w = AcksWorker.new(@queue, TestPool.new)
395
411
  mock(w).work("msg").once{ raise "foo" }
396
- mock(w.logger).error(/error="foo" error_class=RuntimeError backtrace=/)
412
+ mock(w.logger).error(/error="foo" error_class=RuntimeError worker_class=AcksWorker backtrace=/)
397
413
  mock(handler).error(header, nil, "msg", anything)
398
414
  w.do_work(header, nil, "msg", handler)
399
415
  end
@@ -406,7 +422,7 @@ describe Sneakers::Worker do
406
422
  header = Object.new
407
423
 
408
424
  mock(handler).timeout(header, nil, "msg")
409
- mock(w.logger).error(/error="execution expired" error_class=Sneakers::WorkerTimeout backtrace=/)
425
+ mock(w.logger).error(/error="execution expired" error_class=Sneakers::WorkerTimeout worker_class=TimeoutWorker backtrace=/)
410
426
 
411
427
  w.do_work(header, nil, "msg", handler)
412
428
  end
@@ -526,11 +542,10 @@ describe Sneakers::Worker do
526
542
  it 'only logs backtraces if present' do
527
543
  w = DummyWorker.new(@queue, TestPool.new)
528
544
  mock(w.logger).warn('cuz')
529
- mock(w.logger).error(/\[Exception error="boom!" error_class=RuntimeError\]/)
545
+ mock(w.logger).error(/\[Exception error="boom!" error_class=RuntimeError worker_class=DummyWorker\]/)
530
546
  w.worker_error(RuntimeError.new('boom!'), 'cuz')
531
547
  end
532
548
  end
533
-
534
549
  end
535
550
 
536
551
 
@@ -0,0 +1,83 @@
1
+ require 'logger'
2
+ require 'spec_helper'
3
+ require 'sneakers'
4
+ require 'sneakers/runner'
5
+
6
+ class DummyFlag
7
+ def wait_for_set(*)
8
+ true
9
+ end
10
+ end
11
+
12
+ class DummyEngine
13
+ include Sneakers::WorkerGroup
14
+
15
+ attr_reader :config
16
+
17
+ def initialize(config)
18
+ @config = config
19
+ @stop_flag = DummyFlag.new
20
+ end
21
+ end
22
+
23
+ class DefaultsWorker
24
+ include Sneakers::Worker
25
+ from_queue 'defaults'
26
+
27
+ def work(msg); end
28
+ end
29
+
30
+ class StubbedWorker
31
+ attr_reader :opts
32
+
33
+ def initialize(_, _, opts)
34
+ @opts = opts
35
+ end
36
+
37
+ def run
38
+ true
39
+ end
40
+ end
41
+
42
+ describe Sneakers::WorkerGroup do
43
+ let(:logger) { Logger.new('logtest.log') }
44
+ let(:connection) { Bunny.new(host: 'any-host.local') }
45
+ let(:runner) { Sneakers::Runner.new([DefaultsWorker]) }
46
+ let(:runner_config) { runner.instance_variable_get('@runnerconfig') }
47
+ let(:config) { runner_config.reload_config! }
48
+ let(:engine) { DummyEngine.new(config) }
49
+
50
+ describe '#run' do
51
+ describe 'with connecion provided' do
52
+ before do
53
+ Sneakers.clear!
54
+ Sneakers.configure(connection: connection, log: logger)
55
+ end
56
+
57
+ it 'creates workers with connection: connection' do
58
+ DefaultsWorker.stub(:new, ->(*args) { StubbedWorker.new(*args) }) do
59
+ engine.run
60
+
61
+ workers = engine.instance_variable_get('@workers')
62
+ workers.first.opts[:connection].must_equal(connection)
63
+ end
64
+ end
65
+ end
66
+
67
+ describe 'without connecion provided' do
68
+ before do
69
+ Sneakers.clear!
70
+ Sneakers.configure(log: logger)
71
+ end
72
+
73
+ it 'creates workers with connection: nil' do
74
+ DefaultsWorker.stub(:new, ->(*args) { StubbedWorker.new(*args) }) do
75
+ engine.run
76
+
77
+ workers = engine.instance_variable_get('@workers')
78
+ assert_nil(workers.first.opts[:connection])
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sneakers
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dotan Nahum
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-30 00:00:00.000000000 Z
11
+ date: 2018-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: serverengine
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.5.11
19
+ version: 2.0.5
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.5.11
26
+ version: 2.0.5
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bunny
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.7.0
33
+ version: 2.9.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.7.0
40
+ version: 2.9.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: concurrent-ruby
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -122,20 +122,6 @@ dependencies:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
124
  version: 0.2.2
125
- - !ruby/object:Gem::Dependency
126
- name: ruby-prof
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
125
  - !ruby/object:Gem::Dependency
140
126
  name: guard-minitest
141
127
  requirement: !ruby/object:Gem::Requirement
@@ -244,13 +230,13 @@ extra_rdoc_files: []
244
230
  files:
245
231
  - ".gitignore"
246
232
  - ".travis.yml"
233
+ - ChangeLog.md
247
234
  - Dockerfile
248
235
  - Dockerfile.slim
249
236
  - Gemfile
250
237
  - Guardfile
251
238
  - LICENSE.txt
252
239
  - README.md
253
- - ROADMAP.md
254
240
  - Rakefile
255
241
  - bin/sneakers
256
242
  - docker-compose.yaml
@@ -304,6 +290,7 @@ files:
304
290
  - spec/sneakers/support/utils_spec.rb
305
291
  - spec/sneakers/worker_handlers_spec.rb
306
292
  - spec/sneakers/worker_spec.rb
293
+ - spec/sneakers/workergroup_spec.rb
307
294
  - spec/spec_helper.rb
308
295
  homepage: http://sneakers.io
309
296
  licenses:
@@ -317,7 +304,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
317
304
  requirements:
318
305
  - - ">="
319
306
  - !ruby/object:Gem::Version
320
- version: '2.0'
307
+ version: '2.2'
321
308
  required_rubygems_version: !ruby/object:Gem::Requirement
322
309
  requirements:
323
310
  - - ">="
@@ -325,7 +312,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
325
312
  version: '0'
326
313
  requirements: []
327
314
  rubyforge_project:
328
- rubygems_version: 2.6.11
315
+ rubygems_version: 2.6.14
329
316
  signing_key:
330
317
  specification_version: 4
331
318
  summary: Fast background processing framework for Ruby and RabbitMQ
@@ -345,4 +332,5 @@ test_files:
345
332
  - spec/sneakers/support/utils_spec.rb
346
333
  - spec/sneakers/worker_handlers_spec.rb
347
334
  - spec/sneakers/worker_spec.rb
335
+ - spec/sneakers/workergroup_spec.rb
348
336
  - spec/spec_helper.rb
data/ROADMAP.md DELETED
@@ -1,18 +0,0 @@
1
- # Roadmap
2
-
3
- It is essential to keep a clear roadmap.
4
- In its core, Sneakers is already production-ready. However, it is vital
5
- to take in feedback and improve what can be improved :)
6
-
7
- This document will communicate the stages that the next version is at.
8
- See `CHANGELOG.md` for new features that are in.
9
-
10
-
11
- # v0.1.0 (next version)
12
-
13
- - Taking in community pull requests and feedback
14
- - Verify production readiness and performance
15
- - Full performance test suite
16
-
17
-
18
-