jetstream_bridge 5.0.1 → 5.1.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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '087fb2993c66be217aa3b544a333d0b166aec88f9a006051010e46309faac51f'
|
|
4
|
+
data.tar.gz: 2489d32b3047955699c11ae640014baec66855cad384543b3220fe778ff2b65a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88e316fb7cfc4f5048290ca37e3c065fc7cb6e059627f5900455c14adf59957c9f8db8e428b82f879511979db2e52ff1a15decb754c7156f9a51f81d766d30f3
|
|
7
|
+
data.tar.gz: 897a1695d32323c7d33dcc8a3811cb9a7952e45088d14b8c81713f08085869805d97a40de34c3c10f33ae8c39861972ff4f9c1cff1af57699c10aa5e8bf05a50
|
|
@@ -104,6 +104,8 @@ module JetstreamBridge
|
|
|
104
104
|
@reconnect_attempts = 0
|
|
105
105
|
@running = true
|
|
106
106
|
@shutdown_requested = false
|
|
107
|
+
@signal_received = nil
|
|
108
|
+
@signal_logged = false
|
|
107
109
|
@start_time = Time.now
|
|
108
110
|
@iterations = 0
|
|
109
111
|
@last_health_check = Time.now
|
|
@@ -197,6 +199,12 @@ module JetstreamBridge
|
|
|
197
199
|
tag: 'JetstreamBridge::Consumer'
|
|
198
200
|
)
|
|
199
201
|
while @running
|
|
202
|
+
# Check if signal was received and log it (safe from main loop)
|
|
203
|
+
if @signal_received && !@signal_logged
|
|
204
|
+
Logging.info("Received #{@signal_received}, stopping consumer...", tag: 'JetstreamBridge::Consumer')
|
|
205
|
+
@signal_logged = true
|
|
206
|
+
end
|
|
207
|
+
|
|
200
208
|
processed = process_batch
|
|
201
209
|
idle_sleep(processed)
|
|
202
210
|
|
|
@@ -368,8 +376,11 @@ module JetstreamBridge
|
|
|
368
376
|
def setup_signal_handlers
|
|
369
377
|
%w[INT TERM].each do |sig|
|
|
370
378
|
Signal.trap(sig) do
|
|
371
|
-
|
|
372
|
-
|
|
379
|
+
# CRITICAL: Only set flags in trap context, no I/O or mutex operations
|
|
380
|
+
# Logging and other operations are unsafe from signal handlers
|
|
381
|
+
@signal_received = sig
|
|
382
|
+
@running = false
|
|
383
|
+
@shutdown_requested = true
|
|
373
384
|
end
|
|
374
385
|
end
|
|
375
386
|
rescue ArgumentError => e
|
|
@@ -440,7 +451,11 @@ module JetstreamBridge
|
|
|
440
451
|
Logging.info('Draining in-flight messages...', tag: 'JetstreamBridge::Consumer')
|
|
441
452
|
# Process any pending messages with a short timeout
|
|
442
453
|
5.times do
|
|
443
|
-
msgs =
|
|
454
|
+
msgs = if JetstreamBridge.config.push_consumer?
|
|
455
|
+
drain_messages_push
|
|
456
|
+
else
|
|
457
|
+
@psub.fetch(@batch_size, timeout: 1)
|
|
458
|
+
end
|
|
444
459
|
break if msgs.nil? || msgs.empty?
|
|
445
460
|
|
|
446
461
|
msgs.each { |m| process_one(m) }
|
|
@@ -455,6 +470,18 @@ module JetstreamBridge
|
|
|
455
470
|
Logging.error("Drain failed: #{e.class} #{e.message}", tag: 'JetstreamBridge::Consumer')
|
|
456
471
|
end
|
|
457
472
|
|
|
473
|
+
def drain_messages_push
|
|
474
|
+
# For push consumers during drain, collect messages with a shorter timeout
|
|
475
|
+
messages = []
|
|
476
|
+
@batch_size.times do
|
|
477
|
+
msg = @psub.next_msg(timeout: 1)
|
|
478
|
+
messages << msg if msg
|
|
479
|
+
rescue NATS::Timeout, NATS::IO::Timeout
|
|
480
|
+
break
|
|
481
|
+
end
|
|
482
|
+
messages
|
|
483
|
+
end
|
|
484
|
+
|
|
458
485
|
def safe_nak_message(msg)
|
|
459
486
|
return unless msg.respond_to?(:nak)
|
|
460
487
|
|
|
@@ -237,7 +237,36 @@ module JetstreamBridge
|
|
|
237
237
|
'NATS reconnected, refreshing JetStream context',
|
|
238
238
|
tag: 'JetstreamBridge::Connection'
|
|
239
239
|
)
|
|
240
|
-
|
|
240
|
+
|
|
241
|
+
# Retry JetStream context refresh with exponential backoff
|
|
242
|
+
max_attempts = 3
|
|
243
|
+
attempts = 0
|
|
244
|
+
success = false
|
|
245
|
+
|
|
246
|
+
while attempts < max_attempts && !success
|
|
247
|
+
attempts += 1
|
|
248
|
+
begin
|
|
249
|
+
refresh_jetstream_context
|
|
250
|
+
success = true
|
|
251
|
+
rescue StandardError => e
|
|
252
|
+
if attempts < max_attempts
|
|
253
|
+
delay = 0.5 * (2**(attempts - 1)) # 0.5s, 1s, 2s
|
|
254
|
+
Logging.warn(
|
|
255
|
+
"JetStream context refresh attempt #{attempts}/#{max_attempts} failed: #{e.message}. " \
|
|
256
|
+
"Retrying in #{delay}s...",
|
|
257
|
+
tag: 'JetstreamBridge::Connection'
|
|
258
|
+
)
|
|
259
|
+
sleep(delay)
|
|
260
|
+
else
|
|
261
|
+
Logging.error(
|
|
262
|
+
"Failed to refresh JetStream context after #{attempts} attempts. " \
|
|
263
|
+
'Will retry on next reconnect.',
|
|
264
|
+
tag: 'JetstreamBridge::Connection'
|
|
265
|
+
)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
241
270
|
@reconnecting = false
|
|
242
271
|
end
|
|
243
272
|
|
|
@@ -458,15 +487,21 @@ module JetstreamBridge
|
|
|
458
487
|
@last_reconnect_error = e
|
|
459
488
|
@last_reconnect_error_at = Time.now
|
|
460
489
|
@state = State::FAILED
|
|
461
|
-
|
|
490
|
+
|
|
491
|
+
# Clear JetStream context but keep NATS connection alive for retry
|
|
492
|
+
@jts = nil
|
|
493
|
+
|
|
494
|
+
# Invalidate health check cache to force re-check
|
|
495
|
+
@cached_health_status = false
|
|
496
|
+
@last_health_check = Time.now.to_i
|
|
497
|
+
|
|
462
498
|
Logging.error(
|
|
463
499
|
"Failed to refresh JetStream context: #{e.class} #{e.message}",
|
|
464
500
|
tag: 'JetstreamBridge::Connection'
|
|
465
501
|
)
|
|
466
502
|
|
|
467
|
-
#
|
|
468
|
-
|
|
469
|
-
@last_health_check = Time.now.to_i
|
|
503
|
+
# Re-raise so caller (on_reconnect handler) can retry
|
|
504
|
+
raise
|
|
470
505
|
end
|
|
471
506
|
|
|
472
507
|
# Expose for class-level helpers (not part of public API)
|
|
@@ -491,11 +526,22 @@ module JetstreamBridge
|
|
|
491
526
|
rescue StandardError
|
|
492
527
|
# ignore cleanup errors
|
|
493
528
|
end
|
|
494
|
-
|
|
495
|
-
|
|
529
|
+
|
|
530
|
+
# Only clear connection references if we're closing the connection
|
|
531
|
+
# When close_nc is false (e.g., during reconnect failures), keep @nc
|
|
532
|
+
# so the connection can recover when NATS auto-reconnects
|
|
533
|
+
if close_nc
|
|
534
|
+
@nc = nil
|
|
535
|
+
@jts = nil
|
|
536
|
+
@connected_at = nil
|
|
537
|
+
else
|
|
538
|
+
# Clear JetStream context but keep NATS connection reference
|
|
539
|
+
@jts = nil
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
# Always invalidate health check cache
|
|
496
543
|
@cached_health_status = nil
|
|
497
544
|
@last_health_check = nil
|
|
498
|
-
@connected_at = nil
|
|
499
545
|
end
|
|
500
546
|
|
|
501
547
|
def config_auto_provision
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jetstream_bridge
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0
|
|
4
|
+
version: 5.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Attara
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|