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 +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
|
[](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
|
-
|