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 +4 -4
- data/.gitignore +1 -0
- data/ChangeLog.md +67 -0
- data/Gemfile +2 -0
- data/README.md +3 -3
- data/lib/sneakers.rb +1 -1
- data/lib/sneakers/error_reporter.rb +3 -3
- data/lib/sneakers/metrics/newrelic_metrics.rb +2 -7
- data/lib/sneakers/publisher.rb +14 -10
- data/lib/sneakers/queue.rb +1 -1
- data/lib/sneakers/runner.rb +2 -2
- data/lib/sneakers/version.rb +1 -1
- data/lib/sneakers/worker.rb +46 -43
- data/lib/sneakers/workergroup.rb +5 -15
- data/sneakers.gemspec +3 -5
- data/spec/sneakers/integration_spec.rb +42 -32
- data/spec/sneakers/publisher_spec.rb +38 -26
- data/spec/sneakers/runner_spec.rb +8 -3
- data/spec/sneakers/worker_spec.rb +20 -5
- data/spec/sneakers/workergroup_spec.rb +83 -0
- metadata +11 -23
- data/ROADMAP.md +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09f8536528b4fa33c1d597f1ca8f513d5ca8f37b'
|
4
|
+
data.tar.gz: c24a191dc06d4457c9f5ac7bfd8f089ca7ed175f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e24898e348b40e79640d216fa9c56b63bf8c94340f23b4e54a4a163fdcbee8c7628a3db7f3128d3ed0220e9e7f83b36239917e610a9ca5b9c481108847cc61c
|
7
|
+
data.tar.gz: 369b932fef00b62847c74e070cd6248203505bf87ff3d932de8eb9ef80bcd65dd7732d59f7b47def85b4746c4b9cce0e933c0bb0b6845fe2f40f8f6a33b8e07b
|
data/.gitignore
CHANGED
data/ChangeLog.md
ADDED
@@ -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
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
|
-
|
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
|
-
*
|
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
|
|
data/lib/sneakers.rb
CHANGED
@@ -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 {|
|
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}"
|
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
|
-
|
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
|
data/lib/sneakers/publisher.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
22
|
+
def ensure_connection!
|
23
|
+
@mutex.synchronize do
|
24
|
+
connect! unless connected?
|
25
|
+
end
|
26
|
+
end
|
20
27
|
|
21
28
|
private
|
22
|
-
def
|
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
|
data/lib/sneakers/queue.rb
CHANGED
data/lib/sneakers/runner.rb
CHANGED
@@ -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
|
|
data/lib/sneakers/version.rb
CHANGED
data/lib/sneakers/worker.rb
CHANGED
@@ -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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
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
|
-
|
data/lib/sneakers/workergroup.rb
CHANGED
@@ -29,13 +29,13 @@ module Sneakers
|
|
29
29
|
worker_classes = worker_classes.call
|
30
30
|
end
|
31
31
|
|
32
|
-
#
|
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
|
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
|
data/sneakers.gemspec
CHANGED
@@ -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.
|
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', '~>
|
22
|
-
gem.add_dependency 'bunny', '~> 2.
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
132
|
+
assert_any_consumers true
|
120
133
|
integration_log "Killing #{pid} now!"
|
121
134
|
Process.kill("TERM", pid)
|
122
|
-
|
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).
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
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.
|
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:
|
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:
|
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:
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|