bug_bunny 4.1.1 → 4.2.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 +17 -4
- data/README.md +27 -1
- data/lib/bug_bunny/configuration.rb +10 -1
- data/lib/bug_bunny/consumer.rb +63 -46
- data/lib/bug_bunny/controller.rb +2 -2
- data/lib/bug_bunny/producer.rb +7 -10
- data/lib/bug_bunny/session.rb +8 -5
- data/lib/bug_bunny/version.rb +1 -1
- data/lib/bug_bunny.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26437c12bdcd679124739cffc4d1c9769587bc6ffd241d6a03a75880c3bd910d
|
|
4
|
+
data.tar.gz: cc185721833cbf505d1fe44ac1dac7ac496fda1677b54898ce950a1d1badff68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71da0a97f33b38263957a878350e8a2daa935a0f211e0fa59af589b507a7d11635e84ac6170ee2a4fafb16b0c4f8ddeec61b388f38d7f9856d171e3059b004f3
|
|
7
|
+
data.tar.gz: 962de8a788d263c04cf0d9fa5da2e6c3a5ec3a2d6156b80ff542b8d2fd333c4d9d0915f94775f8fb9c8101d507691cf757701904fe1d0477d5dabfcd006efa7e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [4.
|
|
3
|
+
## [4.2.0] - 2026-03-22
|
|
4
4
|
|
|
5
|
-
###
|
|
6
|
-
* **
|
|
7
|
-
* **
|
|
5
|
+
### 🔠Observability & Structured Logging
|
|
6
|
+
* **Structured Logs (Key-Value):** Se migraron todos los logs del framework a un formato \`key=value\` estructurado, ideal para herramientas de monitoreo como Datadog o CloudWatch. Se eliminaron emojis y texto libre para mejorar el parseo automático.
|
|
7
|
+
* **Lazy Evaluation (Debug Blocks):** Las llamadas a \`logger.debug\` ahora utilizan bloques para evitar la interpolación de strings innecesaria en producción, optimizando el uso de CPU y memoria.
|
|
8
|
+
|
|
9
|
+
### ðŸ›¡ï¸ Resilience & Connectivity
|
|
10
|
+
* **Exponential Backoff:** El \`Consumer\` ahora implementa un algoritmo de reintento exponencial para reconectarse a RabbitMQ, evitando picos de carga durante caÃdas del broker.
|
|
11
|
+
* **Max Reconnect Attempts:** Nueva configuración \`max_reconnect_attempts\` que permite que el worker falle definitivamente tras N intentos, facilitando el reinicio del Pod por parte de orquestadores como Kubernetes.
|
|
12
|
+
* **Performance Tuning:** Se desactivaron los \`publisher_confirms\` en el canal del \`Consumer\` al responder RPCs para reducir la latencia de respuesta (round-trips innecesarios).
|
|
13
|
+
|
|
14
|
+
## [4.1.2] - 2026-03-22
|
|
8
15
|
|
|
9
16
|
### ✨ Improvements
|
|
10
17
|
* **Controller:** Ahora lanza una excepción \`BugBunny::BadRequest\` (400) si el cuerpo de la petición contiene un JSON inválido, mejorando la depuración en el cliente.
|
|
11
18
|
* **Resource:** Se añadió una protección a \`.with\` (\`ScopeProxy\`) para asegurar que el contexto sea de un solo uso, evitando efectos secundarios en llamadas encadenadas.
|
|
12
19
|
|
|
20
|
+
## [4.1.1] - 2026-03-22
|
|
21
|
+
|
|
22
|
+
### 🐛 Bug Fixes
|
|
23
|
+
* **Consumer:** Previene memory leak al detener el `TimerTask` de health check previo antes de realizar una reconexión.
|
|
24
|
+
* **Controller:** Corrige la mutación accidental de \`log_tags\` globales al usar una lógica de herencia no destructiva en \`compute_tags\`.
|
|
25
|
+
|
|
13
26
|
## [4.1.0] - 2026-03-22
|
|
14
27
|
|
|
15
28
|
### 🚀 New Features & Improvements
|
data/README.md
CHANGED
|
@@ -74,7 +74,9 @@ BugBunny.configure do |config|
|
|
|
74
74
|
|
|
75
75
|
# 2. Timeouts y Recuperación
|
|
76
76
|
config.rpc_timeout = 10 # Segundos máx para esperar respuesta (Síncrono)
|
|
77
|
-
config.network_recovery_interval = 5 #
|
|
77
|
+
config.network_recovery_interval = 5 # Base del backoff de reconexión (segundos)
|
|
78
|
+
config.max_reconnect_interval = 60 # Techo del backoff exponencial (segundos)
|
|
79
|
+
config.max_reconnect_attempts = nil # nil = reintenta infinitamente; Integer = falla hard
|
|
78
80
|
|
|
79
81
|
# 3. Health Checks (Opcional, para Docker Swarm / K8s)
|
|
80
82
|
config.health_check_file = '/tmp/bug_bunny_health'
|
|
@@ -420,6 +422,30 @@ Para máxima velocidad, BugBunny usa `amq.rabbitmq.reply-to`.
|
|
|
420
422
|
### Seguridad
|
|
421
423
|
El Router incluye protecciones contra **Remote Code Execution (RCE)**. El Consumer verifica estrictamente que el Controlador resuelto a través del archivo de rutas herede de `BugBunny::Controller` antes de ejecutarla, impidiendo la inyección de clases arbitrarias. Además, las llamadas a rutas no registradas fallan rápido con un `404 Not Found`.
|
|
422
424
|
|
|
425
|
+
### Reconexión con Backoff Exponencial
|
|
426
|
+
|
|
427
|
+
Cuando el Consumer pierde la conexión a RabbitMQ, reintenta automáticamente usando un backoff exponencial basado en `network_recovery_interval`:
|
|
428
|
+
|
|
429
|
+
| Intento | Espera (base = 5s, techo = 60s) |
|
|
430
|
+
|---------|----------------------------------|
|
|
431
|
+
| 1 | 5s |
|
|
432
|
+
| 2 | 10s |
|
|
433
|
+
| 3 | 20s |
|
|
434
|
+
| 4 | 40s |
|
|
435
|
+
| 5+ | 60s (cap) |
|
|
436
|
+
|
|
437
|
+
Por defecto reintenta indefinidamente (`max_reconnect_attempts: nil`). En entornos orquestados (Kubernetes, Docker Swarm), es preferible dejar que el orquestador reinicie el contenedor cuando la infraestructura no está disponible:
|
|
438
|
+
|
|
439
|
+
```ruby
|
|
440
|
+
BugBunny.configure do |config|
|
|
441
|
+
config.network_recovery_interval = 5 # Base del backoff
|
|
442
|
+
config.max_reconnect_interval = 60 # Techo máximo de espera
|
|
443
|
+
config.max_reconnect_attempts = 10 # Falla hard después de 10 intentos
|
|
444
|
+
end
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Con esta configuración, si RabbitMQ no vuelve en ~10 reintentos el proceso levanta la excepción y el orquestador lo reinicia con su propia política de restart.
|
|
448
|
+
|
|
423
449
|
### Health Checks en Docker Swarm / Kubernetes
|
|
424
450
|
Dado que un Worker se ejecuta en segundo plano sin exponer un servidor web tradicional, orquestadores como Docker Swarm o Kubernetes no pueden usar un endpoint HTTP para verificar si el proceso está saludable.
|
|
425
451
|
|
|
@@ -38,9 +38,16 @@ module BugBunny
|
|
|
38
38
|
# @return [Boolean] Si `true`, Bunny intentará reconectar automáticamente.
|
|
39
39
|
attr_accessor :automatically_recover
|
|
40
40
|
|
|
41
|
-
# @return [Integer] Tiempo en segundos a esperar antes de intentar reconectar.
|
|
41
|
+
# @return [Integer] Tiempo en segundos a esperar antes de intentar reconectar (base del backoff).
|
|
42
42
|
attr_accessor :network_recovery_interval
|
|
43
43
|
|
|
44
|
+
# @return [Integer, nil] Número máximo de intentos de reconexión del Consumer antes de rendirse.
|
|
45
|
+
# Si es `nil` (default), reintenta indefinidamente.
|
|
46
|
+
attr_accessor :max_reconnect_attempts
|
|
47
|
+
|
|
48
|
+
# @return [Integer] Techo en segundos para el backoff exponencial de reconexión (default: 60).
|
|
49
|
+
attr_accessor :max_reconnect_interval
|
|
50
|
+
|
|
44
51
|
# @return [Integer] Timeout en segundos para establecer la conexión TCP inicial.
|
|
45
52
|
attr_accessor :connection_timeout
|
|
46
53
|
|
|
@@ -106,6 +113,8 @@ module BugBunny
|
|
|
106
113
|
@bunny_logger.level = Logger::WARN
|
|
107
114
|
@automatically_recover = true
|
|
108
115
|
@network_recovery_interval = 5
|
|
116
|
+
@max_reconnect_attempts = nil
|
|
117
|
+
@max_reconnect_interval = 60
|
|
109
118
|
@connection_timeout = 10
|
|
110
119
|
@read_timeout = 30
|
|
111
120
|
@write_timeout = 30
|
data/lib/bug_bunny/consumer.rb
CHANGED
|
@@ -43,7 +43,7 @@ module BugBunny
|
|
|
43
43
|
#
|
|
44
44
|
# @param connection [Bunny::Session] Conexión nativa de Bunny.
|
|
45
45
|
def initialize(connection)
|
|
46
|
-
@session = BugBunny::Session.new(connection)
|
|
46
|
+
@session = BugBunny::Session.new(connection, publisher_confirms: false)
|
|
47
47
|
@health_timer = nil
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -61,41 +61,58 @@ module BugBunny
|
|
|
61
61
|
# @param block [Boolean] Si es `true`, bloquea el hilo actual (loop infinito).
|
|
62
62
|
# @return [void]
|
|
63
63
|
def subscribe(queue_name:, exchange_name:, routing_key:, exchange_type: 'direct', exchange_opts: {}, queue_opts: {}, block: true)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
logger
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
64
|
+
attempt = 0
|
|
65
|
+
|
|
66
|
+
begin
|
|
67
|
+
# Declaración de Infraestructura
|
|
68
|
+
x = session.exchange(name: exchange_name, type: exchange_type, opts: exchange_opts)
|
|
69
|
+
q = session.queue(queue_name, queue_opts)
|
|
70
|
+
q.bind(x, routing_key: routing_key)
|
|
71
|
+
|
|
72
|
+
# 📊 LOGGING DE OBSERVABILIDAD: Calculamos las opciones finales para mostrarlas en consola
|
|
73
|
+
final_x_opts = BugBunny::Session::DEFAULT_EXCHANGE_OPTIONS
|
|
74
|
+
.merge(BugBunny.configuration.exchange_options || {})
|
|
75
|
+
.merge(exchange_opts || {})
|
|
76
|
+
final_q_opts = BugBunny::Session::DEFAULT_QUEUE_OPTIONS
|
|
77
|
+
.merge(BugBunny.configuration.queue_options || {})
|
|
78
|
+
.merge(queue_opts || {})
|
|
79
|
+
|
|
80
|
+
BugBunny.configuration.logger.info("component=bug_bunny event=consumer_start queue=#{queue_name} queue_opts=#{final_q_opts}")
|
|
81
|
+
BugBunny.configuration.logger.info("component=bug_bunny event=consumer_bound exchange=#{exchange_name} exchange_type=#{exchange_type} routing_key=#{routing_key} exchange_opts=#{final_x_opts}")
|
|
82
|
+
|
|
83
|
+
start_health_check(queue_name)
|
|
84
|
+
|
|
85
|
+
q.subscribe(manual_ack: true, block: block) do |delivery_info, properties, body|
|
|
86
|
+
trace_id = properties.correlation_id
|
|
87
|
+
|
|
88
|
+
logger = BugBunny.configuration.logger
|
|
89
|
+
|
|
90
|
+
if logger.respond_to?(:tagged)
|
|
91
|
+
logger.tagged(trace_id) { process_message(delivery_info, properties, body) }
|
|
92
|
+
elsif defined?(Rails) && Rails.logger.respond_to?(:tagged)
|
|
93
|
+
Rails.logger.tagged(trace_id) { process_message(delivery_info, properties, body) }
|
|
94
|
+
else
|
|
95
|
+
process_message(delivery_info, properties, body)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
rescue StandardError => e
|
|
99
|
+
attempt += 1
|
|
100
|
+
max_attempts = BugBunny.configuration.max_reconnect_attempts
|
|
101
|
+
|
|
102
|
+
if max_attempts && attempt >= max_attempts
|
|
103
|
+
BugBunny.configuration.logger.error { "component=bug_bunny event=reconnect_exhausted attempts=#{max_attempts} error=#{e.message.inspect}" }
|
|
104
|
+
raise
|
|
93
105
|
end
|
|
106
|
+
|
|
107
|
+
wait = [
|
|
108
|
+
BugBunny.configuration.network_recovery_interval * (2 ** (attempt - 1)),
|
|
109
|
+
BugBunny.configuration.max_reconnect_interval
|
|
110
|
+
].min
|
|
111
|
+
|
|
112
|
+
BugBunny.configuration.logger.error { "component=bug_bunny event=connection_error error=#{e.message.inspect} attempt=#{attempt} max_attempts=#{max_attempts || 'infinity'} retry_in=#{wait}" }
|
|
113
|
+
sleep wait
|
|
114
|
+
retry
|
|
94
115
|
end
|
|
95
|
-
rescue StandardError => e
|
|
96
|
-
BugBunny.configuration.logger.error("[BugBunny::Consumer] 💥 Connection Error: #{e.message}. Retrying in #{BugBunny.configuration.network_recovery_interval}s...")
|
|
97
|
-
sleep BugBunny.configuration.network_recovery_interval
|
|
98
|
-
retry
|
|
99
116
|
end
|
|
100
117
|
|
|
101
118
|
private
|
|
@@ -113,7 +130,7 @@ module BugBunny
|
|
|
113
130
|
path = properties.type || (properties.headers && properties.headers['path'])
|
|
114
131
|
|
|
115
132
|
if path.nil? || path.empty?
|
|
116
|
-
BugBunny.configuration.logger.error(
|
|
133
|
+
BugBunny.configuration.logger.error('component=bug_bunny event=message_rejected reason=missing_type_header')
|
|
117
134
|
session.channel.reject(delivery_info.delivery_tag, false)
|
|
118
135
|
return
|
|
119
136
|
end
|
|
@@ -122,8 +139,8 @@ module BugBunny
|
|
|
122
139
|
headers_hash = properties.headers || {}
|
|
123
140
|
http_method = (headers_hash['x-http-method'] || headers_hash['method'] || 'GET').to_s.upcase
|
|
124
141
|
|
|
125
|
-
BugBunny.configuration.logger.info("
|
|
126
|
-
BugBunny.configuration.logger.debug
|
|
142
|
+
BugBunny.configuration.logger.info("component=bug_bunny event=message_received method=#{http_method} path=#{path} routing_key=#{delivery_info.routing_key}")
|
|
143
|
+
BugBunny.configuration.logger.debug { "component=bug_bunny event=message_received_body body=#{body.truncate(200).inspect}" }
|
|
127
144
|
|
|
128
145
|
# ===================================================================
|
|
129
146
|
# 3. Ruteo Declarativo
|
|
@@ -140,7 +157,7 @@ module BugBunny
|
|
|
140
157
|
route_info = BugBunny.routes.recognize(http_method, uri.path)
|
|
141
158
|
|
|
142
159
|
if route_info.nil?
|
|
143
|
-
BugBunny.configuration.logger.warn("
|
|
160
|
+
BugBunny.configuration.logger.warn("component=bug_bunny event=route_not_found method=#{http_method} path=#{uri.path}")
|
|
144
161
|
handle_fatal_error(properties, 404, "Not Found", "No route matches [#{http_method}] \"/#{uri.path}\"")
|
|
145
162
|
session.channel.reject(delivery_info.delivery_tag, false)
|
|
146
163
|
return
|
|
@@ -159,7 +176,7 @@ module BugBunny
|
|
|
159
176
|
begin
|
|
160
177
|
controller_class = controller_class_name.constantize
|
|
161
178
|
rescue NameError
|
|
162
|
-
BugBunny.configuration.logger.warn("
|
|
179
|
+
BugBunny.configuration.logger.warn("component=bug_bunny event=controller_not_found controller=#{controller_class_name}")
|
|
163
180
|
handle_fatal_error(properties, 404, "Not Found", "Controller #{controller_class_name} not found")
|
|
164
181
|
session.channel.reject(delivery_info.delivery_tag, false)
|
|
165
182
|
return
|
|
@@ -167,13 +184,13 @@ module BugBunny
|
|
|
167
184
|
|
|
168
185
|
# Verificación estricta de Seguridad (RCE Prevention)
|
|
169
186
|
unless controller_class < BugBunny::Controller
|
|
170
|
-
BugBunny.configuration.logger.error("
|
|
187
|
+
BugBunny.configuration.logger.error("component=bug_bunny event=security_violation reason=invalid_controller controller=#{controller_class}")
|
|
171
188
|
handle_fatal_error(properties, 403, "Forbidden", "Invalid Controller Class")
|
|
172
189
|
session.channel.reject(delivery_info.delivery_tag, false)
|
|
173
190
|
return
|
|
174
191
|
end
|
|
175
192
|
|
|
176
|
-
BugBunny.configuration.logger.debug
|
|
193
|
+
BugBunny.configuration.logger.debug { "component=bug_bunny event=route_matched controller=#{controller_class_name} action=#{route_info[:action]}" }
|
|
177
194
|
|
|
178
195
|
request_metadata = {
|
|
179
196
|
type: path,
|
|
@@ -199,8 +216,8 @@ module BugBunny
|
|
|
199
216
|
session.channel.ack(delivery_info.delivery_tag)
|
|
200
217
|
|
|
201
218
|
rescue StandardError => e
|
|
202
|
-
BugBunny.configuration.logger.error
|
|
203
|
-
BugBunny.configuration.logger.debug
|
|
219
|
+
BugBunny.configuration.logger.error { "component=bug_bunny event=execution_error error_class=#{e.class} error=#{e.message.inspect}" }
|
|
220
|
+
BugBunny.configuration.logger.debug { "component=bug_bunny event=execution_error backtrace=#{e.backtrace.first(5).join(' | ').inspect}" }
|
|
204
221
|
handle_fatal_error(properties, 500, "Internal Server Error", e.message)
|
|
205
222
|
session.channel.reject(delivery_info.delivery_tag, false)
|
|
206
223
|
end
|
|
@@ -212,7 +229,7 @@ module BugBunny
|
|
|
212
229
|
# @param correlation_id [String] ID para correlacionar la respuesta con la petición original.
|
|
213
230
|
# @return [void]
|
|
214
231
|
def reply(payload, reply_to, correlation_id)
|
|
215
|
-
BugBunny.configuration.logger.debug
|
|
232
|
+
BugBunny.configuration.logger.debug { "component=bug_bunny event=rpc_reply reply_to=#{reply_to} correlation_id=#{correlation_id}" }
|
|
216
233
|
session.channel.default_exchange.publish(
|
|
217
234
|
payload.to_json,
|
|
218
235
|
routing_key: reply_to,
|
|
@@ -261,7 +278,7 @@ module BugBunny
|
|
|
261
278
|
# 2. Si llegamos aquí, RabbitMQ y la cola están vivos. Avisamos al orquestador actualizando el archivo.
|
|
262
279
|
touch_health_file(file_path) if file_path
|
|
263
280
|
rescue StandardError => e
|
|
264
|
-
BugBunny.configuration.logger.warn("
|
|
281
|
+
BugBunny.configuration.logger.warn("component=bug_bunny event=health_check_failed queue=#{q_name} error=#{e.message.inspect}")
|
|
265
282
|
session.close
|
|
266
283
|
end
|
|
267
284
|
@health_timer.execute
|
|
@@ -276,7 +293,7 @@ module BugBunny
|
|
|
276
293
|
def touch_health_file(file_path)
|
|
277
294
|
FileUtils.touch(file_path)
|
|
278
295
|
rescue StandardError => e
|
|
279
|
-
BugBunny.configuration.logger.error("
|
|
296
|
+
BugBunny.configuration.logger.error("component=bug_bunny event=health_check_file_error path=#{file_path} error=#{e.message.inspect}")
|
|
280
297
|
end
|
|
281
298
|
end
|
|
282
299
|
end
|
data/lib/bug_bunny/controller.rb
CHANGED
|
@@ -204,8 +204,8 @@ module BugBunny
|
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
# Fallback genérico si la excepción no fue mapeada
|
|
207
|
-
BugBunny.configuration.logger.error
|
|
208
|
-
BugBunny.configuration.logger.error
|
|
207
|
+
BugBunny.configuration.logger.error { "component=bug_bunny event=unhandled_exception error_class=#{exception.class} error=#{exception.message.inspect}" }
|
|
208
|
+
BugBunny.configuration.logger.error { "component=bug_bunny event=unhandled_exception backtrace=#{exception.backtrace.first(5).join(' | ').inspect}" }
|
|
209
209
|
|
|
210
210
|
{
|
|
211
211
|
status: 500,
|
data/lib/bug_bunny/producer.rb
CHANGED
|
@@ -73,7 +73,7 @@ module BugBunny
|
|
|
73
73
|
begin
|
|
74
74
|
fire(request)
|
|
75
75
|
|
|
76
|
-
BugBunny.configuration.logger.debug
|
|
76
|
+
BugBunny.configuration.logger.debug { "component=bug_bunny event=rpc_waiting correlation_id=#{cid} timeout=#{wait_timeout}" }
|
|
77
77
|
|
|
78
78
|
# Bloqueamos el hilo aquí hasta que llegue la respuesta o expire el timeout
|
|
79
79
|
response_payload = future.value(wait_timeout)
|
|
@@ -106,12 +106,9 @@ module BugBunny
|
|
|
106
106
|
.merge(BugBunny.configuration.exchange_options || {})
|
|
107
107
|
.merge(request.exchange_options || {})
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
BugBunny.configuration.logger.
|
|
111
|
-
|
|
112
|
-
# DEBUG: Detalle completo de Infraestructura y Payload
|
|
113
|
-
BugBunny.configuration.logger.debug("[BugBunny::Producer] ⚙️ Exchange #{request.exchange} | Opts: #{final_x_opts}")
|
|
114
|
-
BugBunny.configuration.logger.debug("[BugBunny::Producer] 📦 Payload: #{payload.truncate(300)}") if payload.is_a?(String)
|
|
109
|
+
BugBunny.configuration.logger.info("component=bug_bunny event=publish method=#{verb} path=#{target} routing_key=#{rk} correlation_id=#{id}")
|
|
110
|
+
BugBunny.configuration.logger.debug { "component=bug_bunny event=publish_detail exchange=#{request.exchange} exchange_opts=#{final_x_opts}" }
|
|
111
|
+
BugBunny.configuration.logger.debug { "component=bug_bunny event=publish_payload payload=#{payload.truncate(300).inspect}" } if payload.is_a?(String)
|
|
115
112
|
end
|
|
116
113
|
|
|
117
114
|
# Serializa el mensaje para su transporte.
|
|
@@ -145,16 +142,16 @@ module BugBunny
|
|
|
145
142
|
@reply_listener_mutex.synchronize do
|
|
146
143
|
return if @reply_listener_started
|
|
147
144
|
|
|
148
|
-
BugBunny.configuration.logger.debug
|
|
145
|
+
BugBunny.configuration.logger.debug { 'component=bug_bunny event=reply_listener_start queue=amq.rabbitmq.reply-to' }
|
|
149
146
|
|
|
150
147
|
# Consumimos sin ack (auto-ack) porque reply-to no soporta acks manuales de forma estándar
|
|
151
148
|
@session.channel.basic_consume('amq.rabbitmq.reply-to', '', true, false, nil) do |_, props, body|
|
|
152
149
|
cid = props.correlation_id.to_s
|
|
153
|
-
BugBunny.configuration.logger.debug
|
|
150
|
+
BugBunny.configuration.logger.debug { "component=bug_bunny event=rpc_response_received correlation_id=#{cid}" }
|
|
154
151
|
if (future = @pending_requests[cid])
|
|
155
152
|
future.set(body)
|
|
156
153
|
else
|
|
157
|
-
BugBunny.configuration.logger.warn("
|
|
154
|
+
BugBunny.configuration.logger.warn("component=bug_bunny event=rpc_response_orphaned correlation_id=#{cid}")
|
|
158
155
|
end
|
|
159
156
|
end
|
|
160
157
|
@reply_listener_started = true
|
data/lib/bug_bunny/session.rb
CHANGED
|
@@ -24,8 +24,12 @@ module BugBunny
|
|
|
24
24
|
# Inicializa una nueva sesión sin abrir canales todavía.
|
|
25
25
|
#
|
|
26
26
|
# @param connection [Bunny::Session] Una conexión (puede estar abierta o cerrada temporalmente).
|
|
27
|
-
|
|
27
|
+
# @param publisher_confirms [Boolean] Si es `true`, el canal se abre en modo Publisher Confirms.
|
|
28
|
+
# Activar solo en sesiones de Producer. En sesiones de Consumer genera overhead innecesario
|
|
29
|
+
# ya que los replies RPC son fire-and-forget desde la perspectiva del servidor.
|
|
30
|
+
def initialize(connection, publisher_confirms: true)
|
|
28
31
|
@connection = connection
|
|
32
|
+
@publisher_confirms = publisher_confirms
|
|
29
33
|
@channel = nil
|
|
30
34
|
end
|
|
31
35
|
|
|
@@ -105,8 +109,7 @@ module BugBunny
|
|
|
105
109
|
def create_channel!
|
|
106
110
|
@channel = @connection.create_channel
|
|
107
111
|
|
|
108
|
-
|
|
109
|
-
@channel.confirm_select
|
|
112
|
+
@channel.confirm_select if @publisher_confirms
|
|
110
113
|
|
|
111
114
|
if BugBunny.configuration.channel_prefetch
|
|
112
115
|
@channel.prefetch(BugBunny.configuration.channel_prefetch)
|
|
@@ -122,10 +125,10 @@ module BugBunny
|
|
|
122
125
|
def ensure_connection!
|
|
123
126
|
return if @connection.open?
|
|
124
127
|
|
|
125
|
-
BugBunny.configuration.logger.warn(
|
|
128
|
+
BugBunny.configuration.logger.warn('component=bug_bunny event=reconnect_attempt')
|
|
126
129
|
@connection.start
|
|
127
130
|
rescue StandardError => e
|
|
128
|
-
BugBunny.configuration.logger.error
|
|
131
|
+
BugBunny.configuration.logger.error { "component=bug_bunny event=reconnect_failed error=#{e.message.inspect}" }
|
|
129
132
|
raise BugBunny::CommunicationError, "Could not reconnect to RabbitMQ: #{e.message}"
|
|
130
133
|
end
|
|
131
134
|
end
|
data/lib/bug_bunny/version.rb
CHANGED
data/lib/bug_bunny.rb
CHANGED
|
@@ -83,7 +83,7 @@ module BugBunny
|
|
|
83
83
|
|
|
84
84
|
@global_connection.close if @global_connection.open?
|
|
85
85
|
@global_connection = nil
|
|
86
|
-
configuration.logger.info('
|
|
86
|
+
configuration.logger.info('component=bug_bunny event=disconnect')
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
# @api private
|