kicks 3.1.1 → 3.3.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
  SHA256:
3
- metadata.gz: 5ccc9ea63784a73fbeddcce04080502702a7313867998bd41ef1322654a97a88
4
- data.tar.gz: f986416420a86a75817fa8ae44750543349b19d1cce5db162d68da9c7d261f8f
3
+ metadata.gz: a7bbca9eba169dacb7dc06c6536049d59ccf469f7c006918850dff45e7f6248a
4
+ data.tar.gz: a9c635d5d1404a4b090df86217c5ffbd28c7dd2dabf3bed1a7d3403236513528
5
5
  SHA512:
6
- metadata.gz: ff97a24291dde8fb237d6e54318de1dfdf53a40361dc4172ec117a93027ffc49082e5c041a46617c628073004367985cfc866be356e7592e6fd5913e57ac2a1c
7
- data.tar.gz: 819e52775f26c55fede5697467cd7154db41e5ce86f7e00eddeb260e8b1f0e922688bb241b4c71e08fb0a17f98541989fa23963867fd4cbaab6d8f7f2a20339c
6
+ metadata.gz: 13a7f0407c737d4a263283bd07b44bdba929c23b0e3fe2a724e4c900b9edc5ab60fe1d9695e1fca1b9deba4e6ed435b4a80794ed8f3bf9188335ba1b58c1b373
7
+ data.tar.gz: 8a0bc7b766d90f3d0789cf5948d2046e7096d9b8ffecd6af9f3dff80166dfcaa948b21b91e10e0172b5a311d2b51b16085f0f0a3e34e2412fc944dfd7a2073a8
@@ -8,9 +8,13 @@ jobs:
8
8
  runs-on: ubuntu-latest
9
9
  strategy:
10
10
  matrix:
11
- ruby-version: [3.0, 3.1, 3.2, 3.3]
11
+ ruby-version:
12
+ - "3.4.2"
13
+ - "3.3.7"
14
+ - "3.2.7"
15
+ - "3.1.6"
12
16
  steps:
13
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@v6
14
18
 
15
19
  - name: Install Ruby ${{ matrix.ruby-version }}
16
20
  uses: ruby/setup-ruby@v1
data/AGENTS.md ADDED
@@ -0,0 +1,74 @@
1
+ # Instructions for AI Agents
2
+
3
+ ## Overview
4
+
5
+ Kicks is a high-performance RabbitMQ background processing framework for Ruby,
6
+ a community-driven continuation of the original [Sneakers](https://github.com/jondot/sneakers) project.
7
+ The internal module name is still `Sneakers`.
8
+
9
+ Its key dependencies are [Bunny](https://github.com/ruby-amqp/bunny) (a RabbitMQ client that uses AMQP 0-9-1)
10
+ and [ServerEngine](https://github.com/treasure-data/serverengine) for process management.
11
+
12
+ ## Target Ruby Version
13
+
14
+ This library targets Ruby 3.0 and later versions.
15
+
16
+ ## Build and Test
17
+
18
+ ```bash
19
+ bundle install
20
+
21
+ bundle exec rake
22
+ ```
23
+
24
+ Tests use Minitest. The default Rake task runs `spec/**/*_spec.rb`.
25
+
26
+ ## Key Files
27
+
28
+ * `lib/sneakers/worker.rb`: the core `Sneakers::Worker` module (work processing, [delivery acknowledgements](https://www.rabbitmq.com/docs/confirms))
29
+ * `lib/sneakers/configuration.rb`: configuration and defaults
30
+ * `lib/sneakers/queue.rb`: queue declaration and consumer registration
31
+ * `lib/sneakers/runner.rb`: the main runner that spawns and manages workers
32
+ * `lib/sneakers/publisher.rb`: message publishing
33
+ * `lib/sneakers/cli.rb`: CLI, based on Thor
34
+ * `lib/sneakers/handlers/maxretry.rb`: retry handler with error queue support
35
+ * `lib/sneakers/handlers/oneshot.rb`: the default handler
36
+ * `lib/sneakers/version.rb`: `Sneakers::VERSION` constant
37
+ * `lib/active_job/queue_adapters/sneakers_adapter.rb`: an `ActiveJob` adapter for Ruby on Rails
38
+
39
+ ## Comments
40
+
41
+ * Only add important comments that express the non-obvious intent, both in tests and in the implementation
42
+ * Keep the comments short
43
+ * Pay attention to the grammar of your comments, including punctuation, full stops, articles, and so on
44
+
45
+ ## Change Log
46
+
47
+ If asked to perform change log updates, consult and modify `ChangeLog.md` and stick to its
48
+ existing writing style.
49
+
50
+ ## Releases
51
+
52
+ ### How to Roll (Produce) a New Release
53
+
54
+ Suppose the current development version in `ChangeLog.md` has
55
+ a `## Changes Between Kicks X.Y.0 and X.(Y+1).0 (in development)` section at the top.
56
+
57
+ To produce a new release:
58
+
59
+ 1. Update `ChangeLog.md`: replace `(in development)` with today's date, e.g. `(Mar 30, 2026)`. Make sure all notable changes since the previous release are listed
60
+ 2. Update the version in `lib/sneakers/version.rb` to match (remove the `.pre` suffix)
61
+ 3. Commit with the message `X.(Y+1).0` (just the version number, nothing else)
62
+ 4. Tag the commit: `git tag X.(Y+1).0`
63
+ 5. Bump the dev version: add a new `## Changes Between Kicks X.(Y+1).0 and X.(Y+2).0 (in development)` section to `ChangeLog.md` with `No changes yet.` underneath, and update `lib/sneakers/version.rb` to the next dev version with a `.pre` suffix
64
+ 6. Commit with the message `Bump dev version`
65
+ 7. Push: `git push && git push --tags`
66
+
67
+ ## Git Instructions
68
+
69
+ * Never add yourself to the list of commit co-authors
70
+ * Never mention yourself in commit messages in any way (no "Generated by", no AI tool links, etc)
71
+
72
+ ## Style Guide
73
+
74
+ * Never add full stops to Markdown list items
data/ChangeLog.md CHANGED
@@ -1,12 +1,62 @@
1
1
  # Change Log
2
2
 
3
- ## Changes Between 3.1.0 and 3.1.1 (Nov 1, 2024)
3
+ ## Changes Between 3.2.0 and 3.3.0 (Mar 31, 2026)
4
+
5
+ ### Queue Type Inheritance for Retry and Error Queues
6
+
7
+ The `maxretry` handler now inherits the queue type from the worker queue's
8
+ arguments when declaring retry and error queues.
9
+
10
+ This can be overridden with the `:retry_queue_arguments` option.
11
+
12
+ GitHub issue: [#38](https://github.com/ruby-amqp/kicks/pull/38)
13
+
14
+ Contributed by @chris72205.
15
+
16
+ ### Improved Bunny Exception Handling for Consumers
17
+
18
+ Contributed by @shashankmehra.
19
+
20
+ GitHub issue: [#35](https://github.com/ruby-amqp/kicks/pull/35)
21
+
22
+ ### Improved Logger Configuration
23
+
24
+ Contributed by @cdhagmann.
25
+
26
+ GitHub issue: [#34](https://github.com/ruby-amqp/kicks/pull/34)
27
+
28
+ ### Bunny Version Bump
29
+
30
+ Kicks now requires the latest (at the time of writing) Bunny `2.24.x`.
31
+
32
+ ### Support Rails 7.2
33
+
34
+ Contributed by @sekrett.
35
+
36
+ GitHub issue: [#32](https://github.com/ruby-amqp/kicks/pull/32)
37
+
38
+
39
+ ## Changes Between 3.1.0 and 3.2.0 (Jan 26, 2025)
40
+
41
+ ### Improved Support for Bring-Your-Own-Connection (BYOC)
42
+
43
+ Kicks now supports passing in a callable (e.g. a proc) instead of an externally-initialized
44
+ and managed Bunny connection.
45
+
46
+ In this case, it is entirely up to the caller
47
+ to configure the connection and call `Bunny::Session#start` on it
48
+ at the right moment.
49
+
50
+ Contributed by @tie.
51
+
52
+ GitHub issue: [#29](https://github.com/ruby-amqp/kicks/pull/29)
53
+
4
54
 
5
55
  ### ActiveJob Adapter Compatibility with Ruby on Rails Older Than 7.2
6
56
 
7
57
  Contributed by @dixpac.
8
58
 
9
- GitHub issue: [#19](https://github.com/ruby-amqp/kicks/pull/19)
59
+ GitHub issues: [#19](https://github.com/ruby-amqp/kicks/pull/19), [#28](https://github.com/ruby-amqp/kicks/pull/28)
10
60
 
11
61
 
12
62
  ## Changes Between 3.0.0 and 3.1.0 (Oct 20, 2024)
@@ -15,7 +65,7 @@ GitHub issue: [#19](https://github.com/ruby-amqp/kicks/pull/19)
15
65
 
16
66
  Kicks now ships with an ActiveJob adapter for Ruby on Rails.
17
67
 
18
- Contributed by @dixpac.
68
+ Contributed by dixpac.
19
69
 
20
70
  GitHub issue: [#12](https://github.com/ruby-amqp/kicks/pull/12)
21
71
 
data/README.md CHANGED
@@ -212,5 +212,6 @@ See [LICENSE](LICENSE.txt) for further details.
212
212
 
213
213
  ## Copyright
214
214
 
215
- Copyright (c) 2023-2024 Kicks contributors
215
+ Copyright (c) 2023-2024 Kicks contributors.
216
+
216
217
  Copyright (c) 2015-2023 [Dotan Nahum](http://gplus.to/dotan) [@jondot](http://twitter.com/jondot).
data/kicks.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |gem|
25
25
  gem.require_paths = ['lib']
26
26
 
27
27
  gem.add_dependency 'serverengine', '~> 2.1'
28
- gem.add_dependency 'bunny', '~> 2.19'
28
+ gem.add_dependency 'bunny', '~> 2.24'
29
29
  gem.add_dependency 'concurrent-ruby', '~> 1.0'
30
30
  gem.add_dependency 'thor'
31
31
  gem.add_dependency 'rake', '>= 12.3', '< 14.0'
@@ -1,39 +1,37 @@
1
- if defined?(ActiveJob) && ActiveJob.version >= "7.2"
2
- module ActiveJob
3
- module QueueAdapters
4
- # Explicitly remove the implementation existing in older Rails versions'.
5
- remove_const(:SneakersAdapter) if defined?("::#{name}::SneakersAdapter")
1
+ module ActiveJob
2
+ module QueueAdapters
3
+ # Explicitly remove the implementation existing in older Rails versions'.
4
+ remove_const(:SneakersAdapter) if const_defined?(:SneakersAdapter)
6
5
 
7
- # = Sneakers adapter for Active Job
8
- #
9
- # To use Sneakers set the queue_adapter config to +:sneakers+.
10
- #
11
- # Rails.application.config.active_job.queue_adapter = :sneakers
12
- class SneakersAdapter < ::ActiveJob::QueueAdapters::AbstractAdapter
13
- def initialize
14
- @monitor = Monitor.new
15
- end
6
+ # = Sneakers adapter for Active Job
7
+ #
8
+ # To use Sneakers set the queue_adapter config to +:sneakers+.
9
+ #
10
+ # Rails.application.config.active_job.queue_adapter = :sneakers
11
+ class SneakersAdapter < (const_defined?(:AbstractAdapter) ? AbstractAdapter : Object)
12
+ def initialize
13
+ @monitor = Monitor.new
14
+ end
16
15
 
17
- def enqueue(job)
18
- @monitor.synchronize do
19
- JobWrapper.from_queue job.queue_name
20
- JobWrapper.enqueue ActiveSupport::JSON.encode(job.serialize)
21
- end
16
+ def enqueue(job)
17
+ @monitor.synchronize do
18
+ JobWrapper.from_queue job.queue_name
19
+ JobWrapper.enqueue ActiveSupport::JSON.encode(job.serialize)
22
20
  end
21
+ end
23
22
 
24
- def enqueue_at(job, timestamp)
25
- raise NotImplementedError, 'This queueing backend does not support scheduling jobs.'
26
- end
23
+ def enqueue_at(job, timestamp)
24
+ raise NotImplementedError, 'This queueing backend does not support scheduling jobs.'
25
+ end
27
26
 
28
- class JobWrapper
29
- include Sneakers::Worker
30
- from_queue 'default'
27
+ class JobWrapper
28
+ include Sneakers::Worker
29
+ from_queue 'default'
31
30
 
32
- def work(msg)
33
- job_data = ActiveSupport::JSON.decode(msg)
34
- Base.execute job_data
35
- ack!
36
- end
31
+ def work(msg)
32
+ job_data = ActiveSupport::JSON.decode(msg)
33
+ Base.execute job_data
34
+ ack!
37
35
  end
38
36
  end
39
37
  end
@@ -5,7 +5,7 @@ module Sneakers
5
5
  class Configuration
6
6
 
7
7
  extend Forwardable
8
- def_delegators :@hash, :to_hash, :[], :[]=, :==, :fetch, :delete, :has_key?
8
+ def_delegators :@hash, :to_hash, :[], :[]=, :==, :fetch, :delete, :has_key?, :dig, :slice
9
9
 
10
10
  EXCHANGE_OPTION_DEFAULTS = {
11
11
  :type => :direct,
@@ -35,6 +35,11 @@ module Sneakers
35
35
  :log => STDOUT,
36
36
  :pid_path => 'sneakers.pid',
37
37
  :amqp_heartbeat => 30,
38
+
39
+ # Default values from serverengine
40
+ :log_rotate_age => 5,
41
+ :log_rotate_size => 1048576,
42
+ :log_level => 'debug',
38
43
 
39
44
  # workers
40
45
  :prefetch => 10,
@@ -60,19 +60,24 @@ module Sneakers
60
60
  Sneakers.logger.debug do
61
61
  "#{log_prefix} creating queue=#{retry_name} x-dead-letter-exchange=#{requeue_name}"
62
62
  end
63
+ retry_args = retry_queue_arguments.merge(
64
+ :'x-dead-letter-exchange' => requeue_name,
65
+ :'x-message-ttl' => @opts[:retry_timeout] || 60000
66
+ )
63
67
  @retry_queue = @channel.queue(retry_name,
64
68
  :durable => queue_durable?,
65
- :arguments => {
66
- :'x-dead-letter-exchange' => requeue_name,
67
- :'x-message-ttl' => @opts[:retry_timeout] || 60000
68
- })
69
+ :arguments => retry_args)
69
70
  @retry_queue.bind(@retry_exchange, :routing_key => '#')
70
71
 
71
72
  Sneakers.logger.debug do
72
73
  "#{log_prefix} creating queue=#{error_name}"
73
74
  end
74
- @error_queue = @channel.queue(error_name,
75
- :durable => queue_durable?)
75
+ error_args = retry_queue_arguments
76
+ if error_args.empty?
77
+ @error_queue = @channel.queue(error_name, :durable => queue_durable?)
78
+ else
79
+ @error_queue = @channel.queue(error_name, :durable => queue_durable?, :arguments => error_args)
80
+ end
76
81
  @error_queue.bind(@error_exchange, :routing_key => '#')
77
82
 
78
83
  # Finally, bind the worker queue to our requeue exchange
@@ -84,8 +89,12 @@ module Sneakers
84
89
 
85
90
  def self.configure_queue(name, opts)
86
91
  retry_name = opts.fetch(:retry_exchange, "#{name}-retry")
87
- opt_args = opts[:queue_options][:arguments] ? opts[:queue_options][:arguments].inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} : {}
88
- opts[:queue_options][:arguments] = { :'x-dead-letter-exchange' => retry_name }.merge(opt_args)
92
+ opt_args = if opts.dig(:queue_options, :arguments).blank?
93
+ {}
94
+ else
95
+ opts.dig(:queue_options, :arguments).transform_keys(&:to_sym)
96
+ end
97
+ opts[:queue_options][:arguments] = { :'x-dead-letter-exchange' => retry_name }.merge!(opt_args)
89
98
  opts[:queue_options]
90
99
  end
91
100
 
@@ -214,6 +223,17 @@ module Sneakers
214
223
  def exchange_durable?
215
224
  queue_durable?
216
225
  end
226
+
227
+ def retry_queue_arguments
228
+ if @opts[:retry_queue_arguments]
229
+ @opts[:retry_queue_arguments].transform_keys(&:to_sym)
230
+ elsif (queue_type = @opts.dig(:queue_options, :arguments, :'x-queue-type') ||
231
+ @opts.dig(:queue_options, :arguments, 'x-queue-type'))
232
+ { :'x-queue-type' => queue_type }
233
+ else
234
+ {}
235
+ end
236
+ end
217
237
  end
218
238
  end
219
239
  end
@@ -6,9 +6,6 @@ module Sneakers
6
6
  def initialize(opts = {})
7
7
  @mutex = Mutex.new
8
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]
12
9
  end
13
10
 
14
11
  def publish(msg, options = {})
@@ -29,8 +26,15 @@ module Sneakers
29
26
 
30
27
  private
31
28
  def connect!
32
- @bunny ||= create_bunny_connection
33
- @bunny.start
29
+ # If we've already got a bunny object, use it. This allows people to
30
+ # specify all kinds of options we don't need to know about (e.g. for ssl).
31
+ @bunny = @opts[:connection]
32
+ if @bunny.respond_to?(:call)
33
+ @bunny = @bunny.call
34
+ else
35
+ @bunny ||= create_bunny_connection
36
+ @bunny.start
37
+ end
34
38
  @channel = @bunny.create_channel
35
39
  @exchange = @channel.exchange(@opts[:exchange], **@opts[:exchange_options])
36
40
  end
@@ -19,8 +19,12 @@ class Sneakers::Queue
19
19
  # If we've already got a bunny object, use it. This allows people to
20
20
  # specify all kinds of options we don't need to know about (e.g. for ssl).
21
21
  @bunny = @opts[:connection]
22
- @bunny ||= create_bunny_connection
23
- @bunny.start
22
+ if @bunny.respond_to?(:call)
23
+ @bunny = @bunny.call
24
+ else
25
+ @bunny ||= create_bunny_connection
26
+ @bunny.start
27
+ end
24
28
 
25
29
  @channel = @bunny.create_channel
26
30
  @channel.prefetch(@opts[:prefetch])
@@ -1,3 +1,3 @@
1
1
  module Sneakers
2
- VERSION = "3.1.1"
2
+ VERSION = "3.3.0"
3
3
  end
@@ -76,7 +76,7 @@ module Sneakers
76
76
  end
77
77
  res = block_to_call.call(deserialized_msg, delivery_info, metadata, handler)
78
78
  end
79
- rescue SignalException, SystemExit
79
+ rescue SignalException, SystemExit, Bunny::Exception
80
80
  # ServerEngine handles these exceptions, so they are not expected to be raised within the worker.
81
81
  # Nevertheless, they are listed here to ensure that they are not caught by the rescue block below.
82
82
  raise
data/lib/sneakers.rb CHANGED
@@ -46,11 +46,11 @@ module Sneakers
46
46
  @configured = false
47
47
  end
48
48
 
49
- def daemonize!(loglevel=Logger::INFO)
49
+ def daemonize!(loglevel=nil)
50
50
  CONFIG[:log] = 'sneakers.log'
51
+ CONFIG[:log_level] = loglevel || Logger::INFO
51
52
  CONFIG[:daemonize] = true
52
53
  setup_general_logger!
53
- logger.level = loglevel
54
54
  end
55
55
 
56
56
  def rake_worker_classes=(worker_classes)
@@ -109,7 +109,7 @@ module Sneakers
109
109
  if [:info, :debug, :error, :warn].all?{ |meth| CONFIG[:log].respond_to?(meth) }
110
110
  @logger = CONFIG[:log]
111
111
  else
112
- @logger = ServerEngine::DaemonLogger.new(CONFIG[:log])
112
+ @logger = ServerEngine::DaemonLogger.new(CONFIG[:log], CONFIG.slice(:log_level, :log_rotate_age, :log_rotate_size))
113
113
  @logger.formatter = Sneakers::Support::ProductionFormatter
114
114
  end
115
115
  end
@@ -124,4 +124,3 @@ module Sneakers
124
124
  @publisher = Sneakers::Publisher.new
125
125
  end
126
126
  end
127
-
@@ -122,19 +122,44 @@ describe Sneakers::Publisher do
122
122
  durable: false
123
123
  )
124
124
  @existing_session = existing_session
125
+ @exchange = exchange
126
+ @channel = channel
125
127
  end
126
128
 
127
- it 'can handle an existing connection that is offline' do
129
+ it 'can handle an existing connection object' do
128
130
  p = Sneakers::Publisher.new
129
131
  p.publish('test msg', my_vars)
130
132
  _(p.instance_variable_get(:@bunny)).must_equal @existing_session
131
133
  end
132
134
 
135
+ it 'can handle an existing connection function' do
136
+ @existing_session.start
137
+ p = Sneakers::Publisher.new(connection: ->() { @existing_session })
138
+ p.publish('test msg', my_vars)
139
+ _(p.instance_variable_get(:@bunny)).must_equal @existing_session
140
+ end
141
+
133
142
  it 'can handle an existing connection that is online' do
143
+ p = Sneakers::Publisher.new
144
+ p.publish('test msg', my_vars)
145
+ _(p.instance_variable_get(:@bunny)).must_equal @existing_session
134
146
  mock(@existing_session).connected? { true }
147
+ mock(@exchange).publish('test msg 2', my_vars)
148
+ p.publish('test msg 2', my_vars)
149
+ end
150
+
151
+ it 'can handle an existing connection that goes offline' do
135
152
  p = Sneakers::Publisher.new
136
153
  p.publish('test msg', my_vars)
137
154
  _(p.instance_variable_get(:@bunny)).must_equal @existing_session
155
+ mock(@existing_session).connected? { false }
156
+ mock(@existing_session).start
157
+ mock(@existing_session).create_channel { @channel }
158
+ mock(@channel).exchange('another_exchange', type: :topic, durable: false, :auto_delete => false, arguments: { 'x-arg' => 'value' }) do
159
+ @exchange
160
+ end
161
+ mock(@exchange).publish('test msg 2', my_vars)
162
+ p.publish('test msg 2', my_vars)
138
163
  end
139
164
  end
140
165
 
@@ -150,19 +150,24 @@ describe Sneakers::Queue do
150
150
  :type => :direct,
151
151
  :durable => true,
152
152
  :arguments => { 'x-arg' => 'value' }){ @mkex }
153
-
154
- queue_name = 'foo'
155
- mock(@mkchan).queue(queue_name, :durable => true) { @mkqueue }
156
- mock(@mkqueue).bind(@mkex, :routing_key => queue_name)
153
+ mock(@mkchan).queue('foo', :durable => true) { @mkqueue }
154
+ mock(@mkqueue).bind(@mkex, :routing_key => 'foo')
157
155
  mock(@mkqueue).subscribe(:block => false, :manual_ack => true)
158
-
159
- my_vars = queue_vars.merge(:connection => @external_connection)
160
- @q = Sneakers::Queue.new(queue_name, my_vars)
161
156
  end
162
157
 
163
158
  it 'uses that object' do
164
- @q.subscribe(@mkworker)
165
- _(@q.instance_variable_get(:@bunny)).must_equal @external_connection
159
+ q = Sneakers::Queue.new('foo',
160
+ queue_vars.merge(:connection => @external_connection))
161
+ q.subscribe(@mkworker)
162
+ _(q.instance_variable_get(:@bunny)).must_equal @external_connection
163
+ end
164
+
165
+ it 'uses that function' do
166
+ @external_connection.start
167
+ q = Sneakers::Queue.new('foo',
168
+ queue_vars.merge(:connection => ->() { @external_connection }))
169
+ q.subscribe(@mkworker)
170
+ _(q.instance_variable_get(:@bunny)).must_equal @external_connection
166
171
  end
167
172
  end
168
173
  end
@@ -466,4 +466,118 @@ describe 'Handlers' do
466
466
  end
467
467
  end
468
468
  end
469
+
470
+ describe 'Maxretry queue arguments inheritance' do
471
+ let(:channel) { Object.new }
472
+ let(:queue) { Object.new }
473
+
474
+ before(:each) do
475
+ Sneakers.configure(:daemonize => true, :log => 'sneakers.log')
476
+ Sneakers::Worker.configure_logger(Logger.new('/dev/null'))
477
+ Sneakers::Worker.configure_metrics
478
+ end
479
+
480
+ describe 'with x-queue-type in queue_options' do
481
+ before do
482
+ @opts = {
483
+ :exchange => 'sneakers',
484
+ :queue_options => {
485
+ :durable => 'true',
486
+ :arguments => { :'x-queue-type' => 'quorum' }
487
+ }
488
+ }
489
+
490
+ mock(queue).name { 'downloads' }
491
+
492
+ @retry_exchange = Object.new
493
+ @error_exchange = Object.new
494
+ @requeue_exchange = Object.new
495
+ @retry_queue = Object.new
496
+ @error_queue = Object.new
497
+
498
+ mock(channel).exchange('downloads-retry',
499
+ :type => 'topic',
500
+ :durable => 'true').once { @retry_exchange }
501
+ mock(channel).exchange('downloads-error',
502
+ :type => 'topic',
503
+ :durable => 'true').once { @error_exchange }
504
+ mock(channel).exchange('downloads-retry-requeue',
505
+ :type => 'topic',
506
+ :durable => 'true').once { @requeue_exchange }
507
+
508
+ mock(channel).queue('downloads-retry',
509
+ :durable => 'true',
510
+ :arguments => {
511
+ :'x-dead-letter-exchange' => 'downloads-retry-requeue',
512
+ :'x-message-ttl' => 60000,
513
+ :'x-queue-type' => 'quorum'
514
+ }).once { @retry_queue }
515
+ mock(@retry_queue).bind(@retry_exchange, :routing_key => '#')
516
+
517
+ mock(channel).queue('downloads-error',
518
+ :durable => 'true',
519
+ :arguments => { :'x-queue-type' => 'quorum' }).once { @error_queue }
520
+ mock(@error_queue).bind(@error_exchange, :routing_key => '#')
521
+
522
+ mock(queue).bind(@requeue_exchange, :routing_key => '#')
523
+ end
524
+
525
+ it 'inherits queue type for retry and error queues' do
526
+ handler = Sneakers::Handlers::Maxretry.new(channel, queue, @opts)
527
+ _(handler).wont_be_nil
528
+ end
529
+ end
530
+
531
+ describe 'with explicit retry_queue_arguments override' do
532
+ before do
533
+ @opts = {
534
+ :exchange => 'sneakers',
535
+ :queue_options => {
536
+ :durable => 'true',
537
+ :arguments => { :'x-queue-type' => 'quorum' }
538
+ },
539
+ :retry_queue_arguments => { :'x-queue-type' => 'classic' }
540
+ }
541
+
542
+ mock(queue).name { 'downloads' }
543
+
544
+ @retry_exchange = Object.new
545
+ @error_exchange = Object.new
546
+ @requeue_exchange = Object.new
547
+ @retry_queue = Object.new
548
+ @error_queue = Object.new
549
+
550
+ mock(channel).exchange('downloads-retry',
551
+ :type => 'topic',
552
+ :durable => 'true').once { @retry_exchange }
553
+ mock(channel).exchange('downloads-error',
554
+ :type => 'topic',
555
+ :durable => 'true').once { @error_exchange }
556
+ mock(channel).exchange('downloads-retry-requeue',
557
+ :type => 'topic',
558
+ :durable => 'true').once { @requeue_exchange }
559
+
560
+ mock(channel).queue('downloads-retry',
561
+ :durable => 'true',
562
+ :arguments => {
563
+ :'x-dead-letter-exchange' => 'downloads-retry-requeue',
564
+ :'x-message-ttl' => 60000,
565
+ :'x-queue-type' => 'classic'
566
+ }).once { @retry_queue }
567
+ mock(@retry_queue).bind(@retry_exchange, :routing_key => '#')
568
+
569
+ mock(channel).queue('downloads-error',
570
+ :durable => 'true',
571
+ :arguments => { :'x-queue-type' => 'classic' }).once { @error_queue }
572
+ mock(@error_queue).bind(@error_exchange, :routing_key => '#')
573
+
574
+ mock(queue).bind(@requeue_exchange, :routing_key => '#')
575
+ end
576
+
577
+ it 'uses explicit retry_queue_arguments over inherited' do
578
+ handler = Sneakers::Handlers::Maxretry.new(channel, queue, @opts)
579
+ _(handler).wont_be_nil
580
+ end
581
+ end
582
+ end
469
583
  end
@@ -218,7 +218,10 @@ describe Sneakers::Worker do
218
218
  :hooks => {},
219
219
  :handler => Sneakers::Handlers::Oneshot,
220
220
  :heartbeat => 30,
221
- :amqp_heartbeat => 30
221
+ :amqp_heartbeat => 30,
222
+ :log_rotate_age => 5,
223
+ :log_rotate_size => 1048576,
224
+ :log_level => "debug"
222
225
  )
223
226
  end
224
227
 
@@ -256,7 +259,10 @@ describe Sneakers::Worker do
256
259
  :hooks => {},
257
260
  :handler => Sneakers::Handlers::Oneshot,
258
261
  :heartbeat => 5,
259
- :amqp_heartbeat => 30
262
+ :amqp_heartbeat => 30,
263
+ :log_rotate_age => 5,
264
+ :log_rotate_size => 1048576,
265
+ :log_level => "debug"
260
266
  )
261
267
  end
262
268
 
@@ -294,7 +300,10 @@ describe Sneakers::Worker do
294
300
  :hooks => {},
295
301
  :handler => Sneakers::Handlers::Oneshot,
296
302
  :heartbeat => 30,
297
- :amqp_heartbeat => 30
303
+ :amqp_heartbeat => 30,
304
+ :log_rotate_age => 5,
305
+ :log_rotate_size => 1048576,
306
+ :log_level => "debug"
298
307
  )
299
308
  end
300
309
  end
@@ -437,6 +446,14 @@ describe Sneakers::Worker do
437
446
  w.do_work(header, nil, "msg", handler)
438
447
  end
439
448
 
449
+ it "should not catch bunny exceptions" do
450
+ w = DummyWorker.new(@queue, TestPool.new)
451
+ mock(w).work("msg").once{ raise Bunny::Exception }
452
+ assert_raises(Bunny::Exception) do
453
+ w.do_work(nil, nil, "msg", nil)
454
+ end
455
+ end
456
+
440
457
  it "should log exceptions from workers" do
441
458
  handler = Object.new
442
459
  header = Object.new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kicks
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dotan Nahum
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-11-01 00:00:00.000000000 Z
12
+ date: 2026-04-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: serverengine
@@ -31,14 +31,14 @@ dependencies:
31
31
  requirements:
32
32
  - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: '2.19'
34
+ version: '2.24'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: '2.19'
41
+ version: '2.24'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: concurrent-ruby
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -266,6 +266,7 @@ files:
266
266
  - ".github/dependabot.yml"
267
267
  - ".github/workflows/ci.yml"
268
268
  - ".gitignore"
269
+ - AGENTS.md
269
270
  - ChangeLog.md
270
271
  - Dockerfile
271
272
  - Dockerfile.slim
@@ -358,7 +359,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
358
359
  - !ruby/object:Gem::Version
359
360
  version: '0'
360
361
  requirements: []
361
- rubygems_version: 3.5.22
362
+ rubygems_version: 3.4.19
362
363
  signing_key:
363
364
  specification_version: 4
364
365
  summary: Fast background processing framework for Ruby and RabbitMQ