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: 6b75d900f4ede2b0dbc787641f532cfcbe07619435eaf3a1673b163d8f87658b
4
- data.tar.gz: 7295f58824d037c799c6aa3487b650fe3030b50acd24fe858878dfbafa8c3cba
3
+ metadata.gz: '087fb2993c66be217aa3b544a333d0b166aec88f9a006051010e46309faac51f'
4
+ data.tar.gz: 2489d32b3047955699c11ae640014baec66855cad384543b3220fe778ff2b65a
5
5
  SHA512:
6
- metadata.gz: 5788fd8947c2d9d3f6e7dc9c7da0bd9fe072c9682fb13a220b12977e4c53e3ec9b373917f96a7ee415488b3081c3dd939afedd56a9ec98f324e0e31919a48106
7
- data.tar.gz: a8223affe27e1cf99bf2db07bf8672b49ecece81e410b40f3727366813ba6d39e18ae828052b6c9cf68693354af2eb382a3566338fb4312ceb78008c2a993b4b
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
- Logging.info("Received #{sig}, stopping consumer...", tag: 'JetstreamBridge::Consumer')
372
- stop!
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 = @psub.fetch(@batch_size, timeout: 1)
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
- refresh_jetstream_context
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
- cleanup_connection!(close_nc: false)
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
- # Invalidate health check cache to force re-check
468
- @cached_health_status = false
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
- @nc = nil
495
- @jts = nil
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
@@ -4,5 +4,5 @@
4
4
  #
5
5
  # Version constant for the gem.
6
6
  module JetstreamBridge
7
- VERSION = '5.0.1'
7
+ VERSION = '5.1.0'
8
8
  end
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.1
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-27 00:00:00.000000000 Z
11
+ date: 2026-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord