sneakers 2.6.0 → 2.7.0

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.
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
-