harmoniser 0.12.0 → 0.13.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/CHANGELOG.md +22 -0
- data/harmoniser.gemspec +1 -1
- data/lib/harmoniser/configuration.rb +5 -12
- data/lib/harmoniser/connectable.rb +8 -3
- data/lib/harmoniser/connection.rb +9 -3
- data/lib/harmoniser/error_handler.rb +51 -0
- data/lib/harmoniser/launcher/base.rb +2 -4
- data/lib/harmoniser/options.rb +17 -1
- data/lib/harmoniser/version.rb +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71c030f6ce0d93a7e9bb55a88c3d3cad300f4ab05809944e08442d2bca43cafb
|
4
|
+
data.tar.gz: da8f3f99f2a9846b35b10a8e986f28e53087a81d2a1a6fd149778e3d82222de1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0774eab4d7682a72e880e764acdb55e05e6e9d10048bbdb3e74ed010293d6dc8c416d9de9d73c3a6752e3c9d1ace45d70dc212a3cc01fb2a33b77d4a494d56d2
|
7
|
+
data.tar.gz: 2eebe48b9779d08b0e516d13daa9f94b358f851de3999d4ca0ee600cab89ae48396bdd351ff583bc9be566e44f113f2fe14c0ef59ce3378d5fe1f35d88802c83
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.13.0] - 2025-05-19
|
4
|
+
|
5
|
+
### Added
|
6
|
+
- Introduce Harmoniser.configuration#on_error callback to handle errors that occur while running the application, (e.g. when a subscriber raises an error, or when a connection cannot be established to RabbitMQ). This method permits appending as many handlers as needed. Usage:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
Harmoniser.configuration do |config|
|
10
|
+
config.on_error do |error, ctx|
|
11
|
+
# error is the exception, i.e. StandardError class raised by the application whereas ctx is the context of the error passed as Hash. The ctx hash contains at least the key `description` which is a string describing the error.
|
12
|
+
|
13
|
+
puts "An error occurred: exception = `#{error.detailed_message}`; description = `#{ctx[:description]}`"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- Update Bunny to the latest version, i.e., 2.24
|
20
|
+
- Delegate into Bunny::Channel to cancel consumers and close channel through
|
21
|
+
Channel#cancel_consumers_before_closing!
|
22
|
+
- Change severity level from error to warning for errors occurring at channel level
|
23
|
+
|
24
|
+
|
3
25
|
## [0.12.0] - 2024-12-22
|
4
26
|
|
5
27
|
### Changed
|
data/harmoniser.gemspec
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "forwardable"
|
2
2
|
require "harmoniser/connection"
|
3
|
+
require "harmoniser/error_handler"
|
3
4
|
require "harmoniser/topology"
|
4
5
|
require "harmoniser/options"
|
5
6
|
|
@@ -7,14 +8,16 @@ module Harmoniser
|
|
7
8
|
class Configuration
|
8
9
|
extend Forwardable
|
9
10
|
|
10
|
-
attr_reader :logger, :options
|
11
|
+
attr_reader :error_handler, :logger, :options
|
11
12
|
def_delegators :options, :concurrency, :environment, :require, :verbose, :timeout
|
13
|
+
def_delegators :error_handler, :handle_error, :on_error
|
12
14
|
|
13
15
|
def initialize
|
14
16
|
@logger = Harmoniser.logger
|
15
|
-
@options = Options.new
|
17
|
+
@options = Options.new
|
16
18
|
set_logger_severity
|
17
19
|
@topology = Topology.new
|
20
|
+
@error_handler = ErrorHandler.default
|
18
21
|
end
|
19
22
|
|
20
23
|
def connection_opts
|
@@ -40,16 +43,6 @@ module Harmoniser
|
|
40
43
|
|
41
44
|
private
|
42
45
|
|
43
|
-
def default_options
|
44
|
-
{
|
45
|
-
concurrency: Float::INFINITY,
|
46
|
-
environment: ENV.fetch("RAILS_ENV", ENV.fetch("RACK_ENV", "production")),
|
47
|
-
require: ".",
|
48
|
-
timeout: 25,
|
49
|
-
verbose: false
|
50
|
-
}
|
51
|
-
end
|
52
|
-
|
53
46
|
def set_logger_severity
|
54
47
|
@logger.level = @options.verbose? ? Logger::DEBUG : Logger::INFO
|
55
48
|
end
|
@@ -7,7 +7,7 @@ module Harmoniser
|
|
7
7
|
module ClassMethods
|
8
8
|
def connection(configuration = Harmoniser.configuration)
|
9
9
|
MUTEX.synchronize do
|
10
|
-
@connection ||= Connection.new(configuration.connection_opts)
|
10
|
+
@connection ||= Connection.new(configuration.connection_opts, error_handler: configuration.error_handler)
|
11
11
|
@connection.start unless @connection.open? || @connection.recovering_from_network_failure?
|
12
12
|
@connection
|
13
13
|
end
|
@@ -21,6 +21,7 @@ module Harmoniser
|
|
21
21
|
connection
|
22
22
|
.create_channel(nil, consumer_pool_size, false, consumer_pool_shutdown_timeout)
|
23
23
|
.tap do |channel|
|
24
|
+
channel.cancel_consumers_before_closing!
|
24
25
|
attach_callbacks(channel)
|
25
26
|
end
|
26
27
|
end
|
@@ -45,12 +46,12 @@ module Harmoniser
|
|
45
46
|
end
|
46
47
|
|
47
48
|
stringified_attributes = attributes.map { |k, v| "#{k} = `#{v}`" }.join(", ")
|
48
|
-
Harmoniser.logger.
|
49
|
+
Harmoniser.logger.warn("Default on_error handler executed for channel: #{stringified_attributes}")
|
49
50
|
maybe_kill_process(amq_method)
|
50
51
|
end
|
51
52
|
|
52
53
|
def on_uncaught_exception(error, consumer)
|
53
|
-
|
54
|
+
handle_error(error, {description: "Uncaught exception from consumer", arguments: consumer.arguments, channel_id: consumer.channel.id, consumer_tag: consumer.consumer_tag, exclusive: consumer.exclusive, no_ack: consumer.no_ack, queue: consumer.queue})
|
54
55
|
end
|
55
56
|
|
56
57
|
def maybe_kill_process(amq_method)
|
@@ -62,6 +63,10 @@ module Harmoniser
|
|
62
63
|
|
63
64
|
amq_method.reply_text =~ /delivery acknowledgement on channel \d+ timed out/
|
64
65
|
end
|
66
|
+
|
67
|
+
def handle_error(exception, ctx)
|
68
|
+
Harmoniser.configuration.handle_error(exception, ctx)
|
69
|
+
end
|
65
70
|
end
|
66
71
|
|
67
72
|
class << self
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "forwardable"
|
2
2
|
require "bunny"
|
3
|
+
require "harmoniser/error_handler"
|
3
4
|
|
4
5
|
module Harmoniser
|
5
6
|
class Connection
|
@@ -21,7 +22,8 @@ module Harmoniser
|
|
21
22
|
|
22
23
|
def_delegators :@bunny, :create_channel, :open?, :recovering_from_network_failure?
|
23
24
|
|
24
|
-
def initialize(opts, logger: Harmoniser.logger)
|
25
|
+
def initialize(opts, error_handler: ErrorHandler.default, logger: Harmoniser.logger)
|
26
|
+
@error_handler = error_handler
|
25
27
|
@logger = logger
|
26
28
|
@bunny = Bunny.new(maybe_dynamic_opts(opts)).tap do |bunny|
|
27
29
|
attach_callbacks(bunny)
|
@@ -43,7 +45,7 @@ module Harmoniser
|
|
43
45
|
begin
|
44
46
|
@bunny.start
|
45
47
|
rescue => e
|
46
|
-
|
48
|
+
handle_error(e, {description: "Connection attempt failed", retries: retries})
|
47
49
|
sleep(1)
|
48
50
|
retries += 1
|
49
51
|
retry
|
@@ -56,7 +58,7 @@ module Harmoniser
|
|
56
58
|
@logger.info("Connection closed: connection = `#{self}`")
|
57
59
|
end
|
58
60
|
rescue => e
|
59
|
-
|
61
|
+
handle_error(e, {description: "Connection close failed"})
|
60
62
|
false
|
61
63
|
end
|
62
64
|
|
@@ -102,5 +104,9 @@ module Harmoniser
|
|
102
104
|
def vhost
|
103
105
|
@bunny.vhost
|
104
106
|
end
|
107
|
+
|
108
|
+
def handle_error(exception, ctx)
|
109
|
+
@error_handler.handle_error(exception, ctx)
|
110
|
+
end
|
105
111
|
end
|
106
112
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Harmoniser
|
2
|
+
class ErrorHandler
|
3
|
+
TIMEOUT = 25
|
4
|
+
DEFAULT_ERROR_HANDLER = ->(exception, ctx) do
|
5
|
+
Harmoniser.logger.error("Error handler called: exception = `#{exception.detailed_message}`, context = `#{ctx}`")
|
6
|
+
end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def default
|
10
|
+
@default ||= new([DEFAULT_ERROR_HANDLER])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(handlers = [])
|
15
|
+
@handlers = handlers
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_error(handler = nil, &block)
|
19
|
+
h = handler || block
|
20
|
+
raise ArgumentError, "Please, provide a handler or a block" if h.nil?
|
21
|
+
raise TypeError, "Handler must respond to call" unless h.respond_to?(:call)
|
22
|
+
|
23
|
+
@handlers << h
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_error(exception, ctx = {})
|
28
|
+
coordinator = Thread::Queue.new
|
29
|
+
|
30
|
+
Thread.new do
|
31
|
+
handlers
|
32
|
+
.each { |handler| trigger_handler(handler, exception, ctx) }
|
33
|
+
.tap { coordinator.push(true) }
|
34
|
+
end.tap do |thread|
|
35
|
+
thread.report_on_exception = true
|
36
|
+
end
|
37
|
+
|
38
|
+
!!coordinator.pop(timeout: TIMEOUT)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :handlers
|
44
|
+
|
45
|
+
def trigger_handler(handler, exception, ctx)
|
46
|
+
handler.call(exception, ctx)
|
47
|
+
rescue => e
|
48
|
+
warn "An error occurred while handling a previous error: #{e.detailed_message}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -61,15 +61,13 @@ module Harmoniser
|
|
61
61
|
def maybe_close_subscriber
|
62
62
|
return unless Subscriber.connection?
|
63
63
|
|
64
|
-
|
64
|
+
report_subscribers_to_cancel
|
65
65
|
report_work_pool
|
66
66
|
Subscriber.connection.close
|
67
67
|
end
|
68
68
|
|
69
|
-
def
|
69
|
+
def report_subscribers_to_cancel
|
70
70
|
@logger.info("Subscribers will be cancelled from queues: klasses = `#{@subscribers}`")
|
71
|
-
@subscribers.each(&:harmoniser_subscriber_stop)
|
72
|
-
@logger.info("Subscribers cancelled: klasses = `#{@subscribers}`")
|
73
71
|
end
|
74
72
|
|
75
73
|
def report_work_pool
|
data/lib/harmoniser/options.rb
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
module Harmoniser
|
2
|
-
Options
|
2
|
+
class Options < Data.define(:concurrency, :environment, :require, :verbose, :timeout)
|
3
|
+
DEFAULT = {
|
4
|
+
concurrency: -> { Float::INFINITY },
|
5
|
+
environment: -> { ENV.fetch("RAILS_ENV", ENV.fetch("RACK_ENV", "production")) },
|
6
|
+
require: -> { "." },
|
7
|
+
timeout: -> { 25 },
|
8
|
+
verbose: -> { false }
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
def initialize(**kwargs)
|
12
|
+
options = DEFAULT
|
13
|
+
.map { |k, v| [k, v.call] }
|
14
|
+
.to_h
|
15
|
+
.merge(kwargs)
|
16
|
+
super(**options)
|
17
|
+
end
|
18
|
+
|
3
19
|
def production?
|
4
20
|
environment == "production"
|
5
21
|
end
|
data/lib/harmoniser/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: harmoniser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jose Lloret
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
19
|
+
version: '2.24'
|
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: '2.
|
26
|
+
version: '2.24'
|
27
27
|
description: A declarative approach to communication with RabbitMQ that simplifies
|
28
28
|
the integration of publishing and consuming messages.
|
29
29
|
email:
|
@@ -48,6 +48,7 @@ files:
|
|
48
48
|
- lib/harmoniser/connectable.rb
|
49
49
|
- lib/harmoniser/connection.rb
|
50
50
|
- lib/harmoniser/definition.rb
|
51
|
+
- lib/harmoniser/error_handler.rb
|
51
52
|
- lib/harmoniser/launcher.rb
|
52
53
|
- lib/harmoniser/launcher/base.rb
|
53
54
|
- lib/harmoniser/launcher/bounded.rb
|
@@ -68,7 +69,7 @@ metadata:
|
|
68
69
|
homepage_uri: https://github.com/jollopre/harmoniser
|
69
70
|
source_code_uri: https://github.com/jollopre/harmoniser
|
70
71
|
changelog_uri: https://github.com/jollopre/harmoniser/blob/master/CHANGELOG.md
|
71
|
-
post_install_message:
|
72
|
+
post_install_message:
|
72
73
|
rdoc_options: []
|
73
74
|
require_paths:
|
74
75
|
- lib
|
@@ -84,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
85
|
version: '0'
|
85
86
|
requirements: []
|
86
87
|
rubygems_version: 3.4.10
|
87
|
-
signing_key:
|
88
|
+
signing_key:
|
88
89
|
specification_version: 4
|
89
90
|
summary: A minimalistic approach to interact with RabbitMQ
|
90
91
|
test_files: []
|