nats_wave 1.1.5 → 1.1.7

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.
@@ -1,15 +1,478 @@
1
+ # # # # frozen_string_literal: true
2
+ # # #
3
+ # # # module NatsWave
4
+ # # # class Client
5
+ # # # attr_reader :config, :publisher, :subscriber, :client
6
+ # # #
7
+ # # # def initialize(options = {})
8
+ # # # @config = Configuration.new(options)
9
+ # # # @client = nil
10
+ # # # @publisher = nil
11
+ # # # @subscriber = nil
12
+ # # # @middleware_stack = []
13
+ # # #
14
+ # # # setup_connection_pool
15
+ # # # setup_middleware
16
+ # # # establish_connections
17
+ # # # end
18
+ # # #
19
+ # # # def publish(subject:, model:, action:, data:, metadata: {})
20
+ # # # ensure_connected!
21
+ # # # @publisher.publish(
22
+ # # # subject: subject,
23
+ # # # model: model,
24
+ # # # action: action,
25
+ # # # data: data,
26
+ # # # metadata: metadata
27
+ # # # )
28
+ # # # end
29
+ # # #
30
+ # # # def publish_batch(events)
31
+ # # # ensure_connected!
32
+ # # # @publisher.publish_batch(events)
33
+ # # # end
34
+ # # #
35
+ # # # def subscribe(subjects:, model_mappings: {}, &block)
36
+ # # # NatsWave.logger.info "Ensuring connection..."
37
+ # # # ensure_connected!
38
+ # # # raise "Subscriber not initialized" unless @subscriber
39
+ # # #
40
+ # # # NatsWave.logger.info "Subscribing to subjects: #{subjects.inspect}"
41
+ # # # NatsWave.logger.info "Subscribing with model mapping: #{model_mappings.inspect}"
42
+ # # # # puts "Subscribing to subjects: #{subjects.inspect}"
43
+ # # # @subscriber.listen(
44
+ # # # subjects: subjects,
45
+ # # # model_mappings: model_mappings,
46
+ # # # &block
47
+ # # # )
48
+ # # # end
49
+ # # #
50
+ # # #
51
+ # # # def start_subscriber
52
+ # # # ensure_connected!
53
+ # # # @subscriber.begin if @subscriber
54
+ # # # end
55
+ # # #
56
+ # # # def health_check
57
+ # # # {
58
+ # # # nats_connected: connected?,
59
+ # # # database_connected: database_connected?,
60
+ # # # nats_url: @config.nats_url,
61
+ # # # nats_server_url: @config.nats_server_url,
62
+ # # # service_name: @config.service_name,
63
+ # # # version: @config.version,
64
+ # # # instance_id: @config.instance_id,
65
+ # # # published_subjects: @config.subject_patterns,
66
+ # # # timestamp: Time.current.iso8601,
67
+ # # # }
68
+ # # # end
69
+ # # #
70
+ # # # def connected?
71
+ # # # @client && @client.connected?
72
+ # # # end
73
+ # # #
74
+ # # # def disconnect!
75
+ # # # @subscriber&.reset
76
+ # # # @client&.close
77
+ # # # @connection_pool&.shutdown
78
+ # # # @connection_pool&.wait_for_termination(5)
79
+ # # # end
80
+ # # #
81
+ # # # private
82
+ # # #
83
+ # # # def setup_connection_pool
84
+ # # # return unless defined?(Concurrent)
85
+ # # #
86
+ # # # @connection_pool = Concurrent::ThreadPoolExecutor.new(
87
+ # # # min_threads: 5,
88
+ # # # max_threads: @config.connection_pool_size
89
+ # # # )
90
+ # # # end
91
+ # # #
92
+ # # # def setup_middleware
93
+ # # # @middleware_stack = []
94
+ # # #
95
+ # # # if @config.middleware_authentication_enabled
96
+ # # # @middleware_stack << Middleware::Authentication.new(@config)
97
+ # # # end
98
+ # # #
99
+ # # # if @config.middleware_validation_enabled
100
+ # # # @middleware_stack << Middleware::Validation.new(@config)
101
+ # # # end
102
+ # # #
103
+ # # # if @config.middleware_logging_enabled
104
+ # # # @middleware_stack << Middleware::Logging.new(@config)
105
+ # # # end
106
+ # # # end
107
+ # # #
108
+ # # # def establish_connections
109
+ # # # return unless nats_available?
110
+ # # #
111
+ # # # establish_nats_connection
112
+ # # #
113
+ # # # if @config.publishing_enabled
114
+ # # # @publisher = Publisher.new(@config, @client, @middleware_stack)
115
+ # # # end
116
+ # # #
117
+ # # # if @config.subscription_enabled
118
+ # # # @subscriber = Subscriber.new(@config, @client, @middleware_stack)
119
+ # # # end
120
+ # # # end
121
+ # # #
122
+ # # # def establish_nats_connection
123
+ # # # @client = NATS.connect(
124
+ # # # @config.nats_url,
125
+ # # # reconnect_time_wait: @config.retry_delay,
126
+ # # # max_reconnect_attempts: @config.reconnect_attempts
127
+ # # # )
128
+ # # # NatsWave.logger.info("Connected to NATS at #{@config.nats_url}")
129
+ # # # rescue => e
130
+ # # # NatsWave.logger.error("Failed to connect to NATS: #{e.message}")
131
+ # # # raise ConnectionError, "Failed to connect to NATS: #{e.message}"
132
+ # # # end
133
+ # # #
134
+ # # # def ensure_connected!
135
+ # # # raise ConnectionError, "Not connected to NATS" unless connected?
136
+ # # # end
137
+ # # #
138
+ # # # def database_connected?
139
+ # # # return false unless @subscriber
140
+ # # # @subscriber.database_connected?
141
+ # # # end
142
+ # # #
143
+ # # # def nats_available?
144
+ # # # defined?(NATS)
145
+ # # # rescue LoadError
146
+ # # # false
147
+ # # # end
148
+ # # # end
149
+ # # # end
150
+ # #
151
+ # #
152
+ # # # frozen_string_literal: true
153
+ # #
154
+ # # module NatsWave
155
+ # # class Client
156
+ # # attr_reader :config, :publisher, :subscriber, :client
157
+ # #
158
+ # # def initialize(options = {})
159
+ # # @config = Configuration.new(options)
160
+ # # @client = nil
161
+ # # @publisher = nil
162
+ # # @subscriber = nil
163
+ # # @middleware_stack = []
164
+ # # @shutdown = false
165
+ # #
166
+ # # setup_connection_pool
167
+ # # setup_middleware
168
+ # # establish_connections
169
+ # # setup_signal_handlers
170
+ # # end
171
+ # #
172
+ # # def start_subscriber
173
+ # # ensure_connected!
174
+ # # return unless @subscriber
175
+ # #
176
+ # # @subscriber.begin
177
+ # #
178
+ # # # Keep the main thread alive in console/rake tasks
179
+ # # if defined?(Rails::Console) || $PROGRAM_NAME.include?('rake')
180
+ # # keep_main_thread_alive
181
+ # # end
182
+ # # end
183
+ # #
184
+ # # def disconnect!
185
+ # # @shutdown = true
186
+ # #
187
+ # # NatsWave.logger.info "🛑 Shutting down NatsWave client..."
188
+ # #
189
+ # # @subscriber&.reset
190
+ # # @client&.close if @client&.connected?
191
+ # # @connection_pool&.shutdown
192
+ # # @connection_pool&.wait_for_termination(5)
193
+ # #
194
+ # # NatsWave.logger.info "✅ NatsWave client shutdown complete"
195
+ # # end
196
+ # #
197
+ # # private
198
+ # #
199
+ # # def setup_signal_handlers
200
+ # # # Handle graceful shutdown
201
+ # # ['INT', 'TERM'].each do |signal|
202
+ # # Signal.trap(signal) do
203
+ # # NatsWave.logger.info "🛑 Received #{signal}, shutting down gracefully..."
204
+ # # disconnect!
205
+ # # exit(0)
206
+ # # end
207
+ # # end
208
+ # # end
209
+ # #
210
+ # # def keep_main_thread_alive
211
+ # # # This prevents the main thread from exiting in console/rake tasks
212
+ # # Thread.new do
213
+ # # while !@shutdown
214
+ # # sleep 1
215
+ # # end
216
+ # # end.join
217
+ # # rescue Interrupt
218
+ # # NatsWave.logger.info "🛑 Interrupted, shutting down..."
219
+ # # disconnect!
220
+ # # end
221
+ # #
222
+ # # def setup_connection_pool
223
+ # # return unless defined?(Concurrent)
224
+ # #
225
+ # # @connection_pool = Concurrent::ThreadPoolExecutor.new(
226
+ # # min_threads: 5,
227
+ # # max_threads: @config.connection_pool_size
228
+ # # )
229
+ # # end
230
+ # #
231
+ # # def setup_middleware
232
+ # # @middleware_stack = []
233
+ # #
234
+ # # if @config.middleware_authentication_enabled
235
+ # # @middleware_stack << Middleware::Authentication.new(@config)
236
+ # # end
237
+ # #
238
+ # # if @config.middleware_validation_enabled
239
+ # # @middleware_stack << Middleware::Validation.new(@config)
240
+ # # end
241
+ # #
242
+ # # if @config.middleware_logging_enabled
243
+ # # @middleware_stack << Middleware::Logging.new(@config)
244
+ # # end
245
+ # # end
246
+ # #
247
+ # # def establish_connections
248
+ # # return unless nats_available?
249
+ # #
250
+ # # establish_nats_connection
251
+ # #
252
+ # # if @config.publishing_enabled
253
+ # # @publisher = Publisher.new(@config, @client, @middleware_stack)
254
+ # # end
255
+ # #
256
+ # # if @config.subscription_enabled
257
+ # # @subscriber = Subscriber.new(@config, @client, @middleware_stack)
258
+ # # end
259
+ # # end
260
+ # #
261
+ # # def establish_nats_connection
262
+ # # @client = NATS.connect(
263
+ # # @config.nats_url,
264
+ # # reconnect_time_wait: @config.retry_delay,
265
+ # # max_reconnect_attempts: @config.reconnect_attempts
266
+ # # )
267
+ # # NatsWave.logger.info("Connected to NATS at #{@config.nats_url}")
268
+ # # rescue => e
269
+ # # NatsWave.logger.error("Failed to connect to NATS: #{e.message}")
270
+ # # raise ConnectionError, "Failed to connect to NATS: #{e.message}"
271
+ # # end
272
+ # #
273
+ # # def ensure_connected!
274
+ # # raise ConnectionError, "Not connected to NATS" unless connected?
275
+ # # end
276
+ # #
277
+ # # def database_connected?
278
+ # # return false unless @subscriber
279
+ # # @subscriber.database_connected?
280
+ # # end
281
+ # #
282
+ # # def nats_available?
283
+ # # defined?(NATS)
284
+ # # rescue LoadError
285
+ # # false
286
+ # # end
287
+ # # end
288
+ # # end
289
+ #
290
+ # # frozen_string_literal: true
291
+ #
292
+ # module NatsWave
293
+ # class Client
294
+ # attr_reader :config, :publisher, :subscriber, :client
295
+ #
296
+ # def initialize(options = {})
297
+ # @config = Configuration.new(options)
298
+ # @client = nil
299
+ # @publisher = nil
300
+ # @subscriber = nil
301
+ # @middleware_stack = []
302
+ # @shutdown = false
303
+ #
304
+ # setup_connection_pool
305
+ # setup_middleware
306
+ # establish_connections
307
+ # end
308
+ #
309
+ # def publish(subject:, model:, action:, data:, metadata: {})
310
+ # ensure_connected!
311
+ # @publisher.publish(
312
+ # subject: subject,
313
+ # model: model,
314
+ # action: action,
315
+ # data: data,
316
+ # metadata: metadata
317
+ # )
318
+ # end
319
+ #
320
+ # def publish_batch(events)
321
+ # ensure_connected!
322
+ # @publisher.publish_batch(events)
323
+ # end
324
+ #
325
+ # def subscribe(subjects:, model_mappings: {}, &block)
326
+ # NatsWave.logger.info "Ensuring connection..."
327
+ # ensure_connected!
328
+ # raise "Subscriber not initialized" unless @subscriber
329
+ #
330
+ # NatsWave.logger.info "Subscribing to subjects: #{subjects.inspect}"
331
+ # NatsWave.logger.info "Subscribing with model mapping: #{model_mappings.inspect}"
332
+ # @subscriber.listen(
333
+ # subjects: subjects,
334
+ # model_mappings: model_mappings,
335
+ # &block
336
+ # )
337
+ # end
338
+ #
339
+ # def start_subscriber
340
+ # ensure_connected!
341
+ # @subscriber.begin if @subscriber
342
+ # end
343
+ #
344
+ # # ✅ ADD THIS METHOD
345
+ # def connected?
346
+ # @client && @client.respond_to?(:connected?) && @client.connected?
347
+ # end
348
+ #
349
+ # def health_check
350
+ # {
351
+ # nats_connected: connected?,
352
+ # database_connected: database_connected?,
353
+ # nats_url: @config.nats_url,
354
+ # nats_server_url: @config.nats_server_url,
355
+ # service_name: @config.service_name,
356
+ # version: @config.version,
357
+ # instance_id: @config.instance_id,
358
+ # published_subjects: @config.subject_patterns,
359
+ # timestamp: Time.current.iso8601,
360
+ # model_registry_stats: NatsWave::ModelRegistry.subscription_stats
361
+ # }
362
+ # end
363
+ #
364
+ # def disconnect!
365
+ # @shutdown = true
366
+ #
367
+ # NatsWave.logger.info "🛑 Shutting down NatsWave client..."
368
+ #
369
+ # @subscriber&.reset
370
+ #
371
+ # if @client&.respond_to?(:close)
372
+ # @client.close
373
+ # elsif @client&.respond_to?(:disconnect)
374
+ # @client.disconnect
375
+ # end
376
+ #
377
+ # @connection_pool&.shutdown
378
+ # @connection_pool&.wait_for_termination(5)
379
+ #
380
+ # NatsWave.logger.info "✅ NatsWave client shutdown complete"
381
+ # end
382
+ #
383
+ # private
384
+ #
385
+ # def setup_connection_pool
386
+ # return unless defined?(Concurrent)
387
+ #
388
+ # @connection_pool = Concurrent::ThreadPoolExecutor.new(
389
+ # min_threads: 5,
390
+ # max_threads: @config.connection_pool_size
391
+ # )
392
+ # end
393
+ #
394
+ # def setup_middleware
395
+ # @middleware_stack = []
396
+ #
397
+ # if @config.middleware_authentication_enabled
398
+ # @middleware_stack << Middleware::Authentication.new(@config)
399
+ # end
400
+ #
401
+ # if @config.middleware_validation_enabled
402
+ # @middleware_stack << Middleware::Validation.new(@config)
403
+ # end
404
+ #
405
+ # if @config.middleware_logging_enabled
406
+ # @middleware_stack << Middleware::Logging.new(@config)
407
+ # end
408
+ # end
409
+ #
410
+ # def establish_connections
411
+ # return unless nats_available?
412
+ #
413
+ # establish_nats_connection
414
+ #
415
+ # if @config.publishing_enabled
416
+ # @publisher = Publisher.new(@config, @client, @middleware_stack)
417
+ # end
418
+ #
419
+ # if @config.subscription_enabled
420
+ # @subscriber = Subscriber.new(@config, @client, @middleware_stack)
421
+ # end
422
+ # end
423
+ #
424
+ # def establish_nats_connection
425
+ # @client = NATS.connect(
426
+ # @config.nats_url,
427
+ # reconnect_time_wait: @config.retry_delay,
428
+ # max_reconnect_attempts: @config.reconnect_attempts
429
+ # )
430
+ # NatsWave.logger.info("Connected to NATS at #{@config.nats_url}")
431
+ # rescue => e
432
+ # NatsWave.logger.error("Failed to connect to NATS: #{e.message}")
433
+ # raise ConnectionError, "Failed to connect to NATS: #{e.message}"
434
+ # end
435
+ #
436
+ # # ✅ UPDATE THIS METHOD
437
+ # def ensure_connected!
438
+ # unless connected?
439
+ # raise ConnectionError, "Not connected to NATS. NATS client: #{@client.class rescue 'nil'}"
440
+ # end
441
+ # end
442
+ #
443
+ # def database_connected?
444
+ # return false unless @subscriber
445
+ # @subscriber.database_connected?
446
+ # end
447
+ #
448
+ # def nats_available?
449
+ # defined?(NATS)
450
+ # rescue LoadError
451
+ # false
452
+ # end
453
+ # end
454
+ # end
455
+
1
456
  # frozen_string_literal: true
2
457
 
458
+ begin
459
+ require 'nats/client'
460
+ rescue LoadError => e
461
+ # NATS gem not available
462
+ puts "Warning: NATS gem not available: #{e.message}"
463
+ end
464
+
3
465
  module NatsWave
4
466
  class Client
5
- attr_reader :config, :publisher, :subscriber, :nats_client
467
+ attr_reader :config, :publisher, :subscriber, :client
6
468
 
7
469
  def initialize(options = {})
8
470
  @config = Configuration.new(options)
9
- @nats_client = nil
471
+ @client = nil
10
472
  @publisher = nil
11
473
  @subscriber = nil
12
474
  @middleware_stack = []
475
+ @shutdown = false
13
476
 
14
477
  setup_connection_pool
15
478
  setup_middleware
@@ -33,8 +496,13 @@ module NatsWave
33
496
  end
34
497
 
35
498
  def subscribe(subjects:, model_mappings: {}, &block)
499
+ NatsWave.logger.info "Ensuring connection..."
36
500
  ensure_connected!
37
- @subscriber.subscribe(
501
+ raise "Subscriber not initialized" unless @subscriber
502
+
503
+ NatsWave.logger.info "Subscribing to subjects: #{subjects.inspect}"
504
+ NatsWave.logger.info "Subscribing with model mapping: #{model_mappings.inspect}"
505
+ @subscriber.listen(
38
506
  subjects: subjects,
39
507
  model_mappings: model_mappings,
40
508
  &block
@@ -43,7 +511,11 @@ module NatsWave
43
511
 
44
512
  def start_subscriber
45
513
  ensure_connected!
46
- @subscriber.start if @subscriber
514
+ @subscriber.begin if @subscriber
515
+ end
516
+
517
+ def connected?
518
+ @client && @client.respond_to?(:connected?) && @client.connected?
47
519
  end
48
520
 
49
521
  def health_check
@@ -51,24 +523,31 @@ module NatsWave
51
523
  nats_connected: connected?,
52
524
  database_connected: database_connected?,
53
525
  nats_url: @config.nats_url,
54
- nats_server_url: @config.nats_server_url,
55
526
  service_name: @config.service_name,
56
527
  version: @config.version,
57
528
  instance_id: @config.instance_id,
58
- published_subjects: @config.subject_patterns,
59
529
  timestamp: Time.current.iso8601,
530
+ model_registry_stats: NatsWave::ModelRegistry.subscription_stats
60
531
  }
61
532
  end
62
533
 
63
- def connected?
64
- @nats_client && @nats_client.connected?
65
- end
66
-
67
534
  def disconnect!
68
- @subscriber&.unsubscribe_all
69
- @nats_client&.close
535
+ @shutdown = true
536
+
537
+ NatsWave.logger.info "🛑 Shutting down NatsWave client..."
538
+
539
+ @subscriber&.reset
540
+
541
+ if @client&.respond_to?(:close)
542
+ @client.close
543
+ elsif @client&.respond_to?(:disconnect)
544
+ @client.disconnect
545
+ end
546
+
70
547
  @connection_pool&.shutdown
71
548
  @connection_pool&.wait_for_termination(5)
549
+
550
+ NatsWave.logger.info "✅ NatsWave client shutdown complete"
72
551
  end
73
552
 
74
553
  private
@@ -77,7 +556,7 @@ module NatsWave
77
556
  return unless defined?(Concurrent)
78
557
 
79
558
  @connection_pool = Concurrent::ThreadPoolExecutor.new(
80
- min_threads: 2,
559
+ min_threads: 5,
81
560
  max_threads: @config.connection_pool_size
82
561
  )
83
562
  end
@@ -99,33 +578,134 @@ module NatsWave
99
578
  end
100
579
 
101
580
  def establish_connections
102
- return unless nats_available?
581
+ unless nats_available?
582
+ NatsWave.logger.warn "NATS gem not available, skipping connection"
583
+ return
584
+ end
585
+
586
+ unless test_nats_connectivity
587
+ NatsWave.logger.error "Cannot reach NATS server, skipping connection"
588
+ return
589
+ end
103
590
 
591
+ # Always try to establish NATS connection
104
592
  establish_nats_connection
105
593
 
106
- if @config.publishing_enabled
107
- @publisher = Publisher.new(@config, @nats_client, @middleware_stack)
594
+ # Verify connection was successful before creating publisher/subscriber
595
+ unless connected?
596
+ NatsWave.logger.error "NATS connection failed, cannot initialize publisher/subscriber"
597
+ return
598
+ end
599
+
600
+ # Only create publisher/subscriber if we have a valid connection
601
+ if @config.publishing_enabled && connected?
602
+ @publisher = Publisher.new(@config, @client, @middleware_stack)
603
+ NatsWave.logger.debug "Publisher initialized"
108
604
  end
109
605
 
110
- if @config.subscription_enabled
111
- @subscriber = Subscriber.new(@config, @nats_client, @middleware_stack)
606
+ if @config.subscription_enabled && connected?
607
+ @subscriber = Subscriber.new(@config, @client, @middleware_stack)
608
+ NatsWave.logger.debug "Subscriber initialized"
112
609
  end
113
610
  end
114
611
 
612
+ # def establish_nats_connection
613
+ # NatsWave.logger.info "Attempting to connect to NATS at #{@config.nats_url}"
614
+ #
615
+ # @client = NATS.connect(
616
+ # @config.nats_url,
617
+ # reconnect_time_wait: @config.retry_delay,
618
+ # max_reconnect_attempts: @config.reconnect_attempts
619
+ # )
620
+ #
621
+ # # Verify the connection actually worked
622
+ # if @client&.connected?
623
+ # NatsWave.logger.info "✅ Successfully connected to NATS at #{@config.nats_url}"
624
+ # else
625
+ # NatsWave.logger.error "❌ NATS client created but not connected"
626
+ # @client = nil
627
+ # end
628
+ #
629
+ # rescue => e
630
+ # NatsWave.logger.error "❌ Failed to connect to NATS: #{e.message}"
631
+ # NatsWave.logger.error "NATS URL: #{@config.nats_url}"
632
+ # NatsWave.logger.error "Error class: #{e.class}"
633
+ #
634
+ # @client = nil
635
+ #
636
+ # # Re-raise in development to catch issues early
637
+ # if defined?(Rails) && Rails.env.development?
638
+ # raise ConnectionError, "Failed to connect to NATS: #{e.message}"
639
+ # end
640
+ # end
641
+
115
642
  def establish_nats_connection
116
- @nats_client = NATS.connect(
117
- @config.nats_url,
643
+ NatsWave.logger.info "Attempting to connect to NATS at #{@config.nats_url}"
644
+
645
+ # Add more detailed connection options for debugging
646
+ connection_options = {
118
647
  reconnect_time_wait: @config.retry_delay,
119
- max_reconnect_attempts: @config.reconnect_attempts
120
- )
121
- NatsWave.logger.info("Connected to NATS at #{@config.nats_url}")
648
+ max_reconnect_attempts: @config.reconnect_attempts,
649
+ dont_randomize_servers: true,
650
+ verbose: true,
651
+ pedantic: false
652
+ }
653
+
654
+ NatsWave.logger.info "Connection options: #{connection_options}"
655
+
656
+ @client = NATS.connect(@config.nats_url, connection_options)
657
+
658
+ # Wait a moment for connection to establish
659
+ sleep 0.5
660
+
661
+ # Check connection status with more detail
662
+ if @client
663
+ NatsWave.logger.info "NATS client created: #{@client.class}"
664
+ NatsWave.logger.info "NATS client connected?: #{@client.connected?}"
665
+ NatsWave.logger.info "NATS client status: #{@client.status rescue 'unknown'}"
666
+
667
+ if @client.connected?
668
+ NatsWave.logger.info "✅ Successfully connected to NATS at #{@config.nats_url}"
669
+ else
670
+ NatsWave.logger.error "❌ NATS client created but not connected"
671
+ NatsWave.logger.error "Client last error: #{@client.last_error rescue 'none'}"
672
+ @client = nil
673
+ end
674
+ else
675
+ NatsWave.logger.error "❌ NATS client is nil after connection attempt"
676
+ end
677
+
122
678
  rescue => e
123
- NatsWave.logger.error("Failed to connect to NATS: #{e.message}")
124
- raise ConnectionError, "Failed to connect to NATS: #{e.message}"
679
+ NatsWave.logger.error "Failed to connect to NATS: #{e.message}"
680
+ NatsWave.logger.error "Error class: #{e.class}"
681
+ NatsWave.logger.error "Error backtrace: #{e.backtrace.first(5).join('\n')}"
682
+ NatsWave.logger.error "NATS URL: #{@config.nats_url}"
683
+ NatsWave.logger.error "Retry delay: #{@config.retry_delay}"
684
+ NatsWave.logger.error "Max reconnect attempts: #{@config.reconnect_attempts}"
685
+
686
+ @client = nil
687
+
688
+ # Re-raise in development to catch issues early
689
+ if defined?(Rails) && Rails.env.development?
690
+ raise ConnectionError, "Failed to connect to NATS: #{e.message}"
691
+ end
125
692
  end
126
693
 
127
694
  def ensure_connected!
128
- raise ConnectionError, "Not connected to NATS" unless connected?
695
+ unless connected?
696
+ error_msg = if @client.nil?
697
+ "NATS client is nil - connection failed during initialization"
698
+ else
699
+ "NATS client exists but not connected (#{@client.class})"
700
+ end
701
+
702
+ NatsWave.logger.error "🔴 Connection check failed: #{error_msg}"
703
+ NatsWave.logger.error "🔧 NATS URL: #{@config.nats_url}"
704
+ NatsWave.logger.error "🔧 Publishing enabled: #{@config.publishing_enabled}"
705
+ NatsWave.logger.error "🔧 Subscription enabled: #{@config.subscription_enabled}"
706
+
707
+ raise ConnectionError, error_msg
708
+ end
129
709
  end
130
710
 
131
711
  def database_connected?
@@ -134,9 +714,38 @@ module NatsWave
134
714
  end
135
715
 
136
716
  def nats_available?
717
+ # First, try to require the NATS gem if it's not already loaded
718
+ begin
719
+ require 'nats/client' unless defined?(NATS)
720
+ rescue LoadError
721
+ NatsWave.logger.error "❌ NATS gem not found. Add 'gem \"nats\"' to your Gemfile"
722
+ return false
723
+ end
724
+
725
+ # Then check if the NATS constant is defined
137
726
  defined?(NATS)
138
- rescue LoadError
139
- false
727
+ end
728
+
729
+ def test_nats_connectivity
730
+ require 'socket'
731
+ require 'uri'
732
+
733
+ begin
734
+ uri = URI.parse(@config.nats_url)
735
+ host = uri.host || 'localhost'
736
+ port = uri.port || 4222
737
+
738
+ NatsWave.logger.info "Testing TCP connectivity to #{host}:#{port}"
739
+
740
+ socket = TCPSocket.new(host, port)
741
+ socket.close
742
+
743
+ NatsWave.logger.info "✅ TCP connection to NATS server successful"
744
+ true
745
+ rescue => e
746
+ NatsWave.logger.error "❌ Cannot reach NATS server: #{e.message}"
747
+ false
748
+ end
140
749
  end
141
750
  end
142
751
  end