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 +4 -4
- data/.github/workflows/ci.yml +6 -2
- data/AGENTS.md +74 -0
- data/ChangeLog.md +53 -3
- data/README.md +2 -1
- data/kicks.gemspec +1 -1
- data/lib/active_job/queue_adapters/sneakers_adapter.rb +28 -30
- data/lib/sneakers/configuration.rb +6 -1
- data/lib/sneakers/handlers/maxretry.rb +28 -8
- data/lib/sneakers/publisher.rb +9 -5
- data/lib/sneakers/queue.rb +6 -2
- data/lib/sneakers/version.rb +1 -1
- data/lib/sneakers/worker.rb +1 -1
- data/lib/sneakers.rb +3 -4
- data/spec/sneakers/publisher_spec.rb +26 -1
- data/spec/sneakers/queue_spec.rb +14 -9
- data/spec/sneakers/worker_handlers_spec.rb +114 -0
- data/spec/sneakers/worker_spec.rb +20 -3
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a7bbca9eba169dacb7dc06c6536049d59ccf469f7c006918850dff45e7f6248a
|
|
4
|
+
data.tar.gz: a9c635d5d1404a4b090df86217c5ffbd28c7dd2dabf3bed1a7d3403236513528
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13a7f0407c737d4a263283bd07b44bdba929c23b0e3fe2a724e4c900b9edc5ab60fe1d9695e1fca1b9deba4e6ed435b4a80794ed8f3bf9188335ba1b58c1b373
|
|
7
|
+
data.tar.gz: 8a0bc7b766d90f3d0789cf5948d2046e7096d9b8ffecd6af9f3dff80166dfcaa948b21b91e10e0172b5a311d2b51b16085f0f0a3e34e2412fc944dfd7a2073a8
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -8,9 +8,13 @@ jobs:
|
|
|
8
8
|
runs-on: ubuntu-latest
|
|
9
9
|
strategy:
|
|
10
10
|
matrix:
|
|
11
|
-
ruby-version:
|
|
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@
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
2
|
-
module
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
def enqueue_at(job, timestamp)
|
|
24
|
+
raise NotImplementedError, 'This queueing backend does not support scheduling jobs.'
|
|
25
|
+
end
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
class JobWrapper
|
|
28
|
+
include Sneakers::Worker
|
|
29
|
+
from_queue 'default'
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
|
88
|
-
|
|
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
|
data/lib/sneakers/publisher.rb
CHANGED
|
@@ -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
|
-
|
|
33
|
-
|
|
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
|
data/lib/sneakers/queue.rb
CHANGED
|
@@ -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
|
|
23
|
-
|
|
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])
|
data/lib/sneakers/version.rb
CHANGED
data/lib/sneakers/worker.rb
CHANGED
|
@@ -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=
|
|
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
|
|
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
|
|
data/spec/sneakers/queue_spec.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
165
|
-
|
|
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.
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
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
|