nats_wave 1.1.7 → 1.1.9
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/Gemfile.lock +1 -1
- data/lib/nats_wave/active_record_extension.rb +93 -0
- data/lib/nats_wave/adapters/active_record.rb +207 -0
- data/lib/nats_wave/adapters/datadog_metrics.rb +1 -1
- data/lib/nats_wave/client.rb +426 -158
- data/lib/nats_wave/concerns/mappable.rb +481 -117
- data/lib/nats_wave/configuration.rb +1 -1
- data/lib/nats_wave/database_connector.rb +51 -0
- data/lib/nats_wave/publisher.rb +142 -39
- data/lib/nats_wave/railtie.rb +126 -6
- data/lib/nats_wave/subscriber.rb +588 -50
- data/lib/nats_wave/version.rb +1 -1
- data/lib/nats_wave.rb +99 -0
- metadata +3 -3
- data/lib/nats_wave/concerns/publishable.rb +0 -216
data/lib/nats_wave/client.rb
CHANGED
@@ -1,3 +1,154 @@
|
|
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
|
+
# # #
|
1
152
|
# # # # frozen_string_literal: true
|
2
153
|
# # #
|
3
154
|
# # # module NatsWave
|
@@ -10,76 +161,64 @@
|
|
10
161
|
# # # @publisher = nil
|
11
162
|
# # # @subscriber = nil
|
12
163
|
# # # @middleware_stack = []
|
164
|
+
# # # @shutdown = false
|
13
165
|
# # #
|
14
166
|
# # # setup_connection_pool
|
15
167
|
# # # setup_middleware
|
16
168
|
# # # establish_connections
|
169
|
+
# # # setup_signal_handlers
|
17
170
|
# # # end
|
18
171
|
# # #
|
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
172
|
# # # def start_subscriber
|
52
173
|
# # # ensure_connected!
|
53
|
-
# # #
|
54
|
-
# # # end
|
174
|
+
# # # return unless @subscriber
|
55
175
|
# # #
|
56
|
-
# # #
|
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
|
176
|
+
# # # @subscriber.begin
|
69
177
|
# # #
|
70
|
-
# # #
|
71
|
-
# # #
|
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
|
72
182
|
# # # end
|
73
183
|
# # #
|
74
184
|
# # # def disconnect!
|
185
|
+
# # # @shutdown = true
|
186
|
+
# # #
|
187
|
+
# # # NatsWave.logger.info "🛑 Shutting down NatsWave client..."
|
188
|
+
# # #
|
75
189
|
# # # @subscriber&.reset
|
76
|
-
# # # @client&.close
|
190
|
+
# # # @client&.close if @client&.connected?
|
77
191
|
# # # @connection_pool&.shutdown
|
78
192
|
# # # @connection_pool&.wait_for_termination(5)
|
193
|
+
# # #
|
194
|
+
# # # NatsWave.logger.info "✅ NatsWave client shutdown complete"
|
79
195
|
# # # end
|
80
196
|
# # #
|
81
197
|
# # # private
|
82
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
|
+
# # #
|
83
222
|
# # # def setup_connection_pool
|
84
223
|
# # # return unless defined?(Concurrent)
|
85
224
|
# # #
|
@@ -148,7 +287,6 @@
|
|
148
287
|
# # # end
|
149
288
|
# # # end
|
150
289
|
# #
|
151
|
-
# #
|
152
290
|
# # # frozen_string_literal: true
|
153
291
|
# #
|
154
292
|
# # module NatsWave
|
@@ -166,19 +304,61 @@
|
|
166
304
|
# # setup_connection_pool
|
167
305
|
# # setup_middleware
|
168
306
|
# # establish_connections
|
169
|
-
# #
|
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
|
+
# # )
|
170
337
|
# # end
|
171
338
|
# #
|
172
339
|
# # def start_subscriber
|
173
340
|
# # ensure_connected!
|
174
|
-
# #
|
341
|
+
# # @subscriber.begin if @subscriber
|
342
|
+
# # end
|
175
343
|
# #
|
176
|
-
# #
|
344
|
+
# # # ✅ ADD THIS METHOD
|
345
|
+
# # def connected?
|
346
|
+
# # @client && @client.respond_to?(:connected?) && @client.connected?
|
347
|
+
# # end
|
177
348
|
# #
|
178
|
-
# #
|
179
|
-
# #
|
180
|
-
# #
|
181
|
-
# #
|
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
|
+
# # }
|
182
362
|
# # end
|
183
363
|
# #
|
184
364
|
# # def disconnect!
|
@@ -187,7 +367,13 @@
|
|
187
367
|
# # NatsWave.logger.info "🛑 Shutting down NatsWave client..."
|
188
368
|
# #
|
189
369
|
# # @subscriber&.reset
|
190
|
-
# #
|
370
|
+
# #
|
371
|
+
# # if @client&.respond_to?(:close)
|
372
|
+
# # @client.close
|
373
|
+
# # elsif @client&.respond_to?(:disconnect)
|
374
|
+
# # @client.disconnect
|
375
|
+
# # end
|
376
|
+
# #
|
191
377
|
# # @connection_pool&.shutdown
|
192
378
|
# # @connection_pool&.wait_for_termination(5)
|
193
379
|
# #
|
@@ -196,29 +382,6 @@
|
|
196
382
|
# #
|
197
383
|
# # private
|
198
384
|
# #
|
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
385
|
# # def setup_connection_pool
|
223
386
|
# # return unless defined?(Concurrent)
|
224
387
|
# #
|
@@ -270,8 +433,11 @@
|
|
270
433
|
# # raise ConnectionError, "Failed to connect to NATS: #{e.message}"
|
271
434
|
# # end
|
272
435
|
# #
|
436
|
+
# # # ✅ UPDATE THIS METHOD
|
273
437
|
# # def ensure_connected!
|
274
|
-
# #
|
438
|
+
# # unless connected?
|
439
|
+
# # raise ConnectionError, "Not connected to NATS. NATS client: #{@client.class rescue 'nil'}"
|
440
|
+
# # end
|
275
441
|
# # end
|
276
442
|
# #
|
277
443
|
# # def database_connected?
|
@@ -289,6 +455,13 @@
|
|
289
455
|
#
|
290
456
|
# # frozen_string_literal: true
|
291
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
|
+
#
|
292
465
|
# module NatsWave
|
293
466
|
# class Client
|
294
467
|
# attr_reader :config, :publisher, :subscriber, :client
|
@@ -341,7 +514,6 @@
|
|
341
514
|
# @subscriber.begin if @subscriber
|
342
515
|
# end
|
343
516
|
#
|
344
|
-
# # ✅ ADD THIS METHOD
|
345
517
|
# def connected?
|
346
518
|
# @client && @client.respond_to?(:connected?) && @client.connected?
|
347
519
|
# end
|
@@ -351,11 +523,9 @@
|
|
351
523
|
# nats_connected: connected?,
|
352
524
|
# database_connected: database_connected?,
|
353
525
|
# nats_url: @config.nats_url,
|
354
|
-
# nats_server_url: @config.nats_server_url,
|
355
526
|
# service_name: @config.service_name,
|
356
527
|
# version: @config.version,
|
357
528
|
# instance_id: @config.instance_id,
|
358
|
-
# published_subjects: @config.subject_patterns,
|
359
529
|
# timestamp: Time.current.iso8601,
|
360
530
|
# model_registry_stats: NatsWave::ModelRegistry.subscription_stats
|
361
531
|
# }
|
@@ -408,35 +578,144 @@
|
|
408
578
|
# end
|
409
579
|
#
|
410
580
|
# def establish_connections
|
411
|
-
#
|
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
|
412
590
|
#
|
591
|
+
# # Always try to establish NATS connection
|
413
592
|
# establish_nats_connection
|
414
593
|
#
|
415
|
-
#
|
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?
|
416
602
|
# @publisher = Publisher.new(@config, @client, @middleware_stack)
|
603
|
+
# NatsWave.logger.debug "Publisher initialized"
|
417
604
|
# end
|
418
605
|
#
|
419
|
-
# if @config.subscription_enabled
|
606
|
+
# if @config.subscription_enabled && connected?
|
420
607
|
# @subscriber = Subscriber.new(@config, @client, @middleware_stack)
|
608
|
+
# NatsWave.logger.debug "Subscriber initialized"
|
421
609
|
# end
|
422
610
|
# end
|
423
611
|
#
|
424
612
|
# def establish_nats_connection
|
613
|
+
# NatsWave.logger.info "Attempting to connect to NATS at #{@config.nats_url}"
|
614
|
+
#
|
425
615
|
# @client = NATS.connect(
|
426
616
|
# @config.nats_url,
|
427
617
|
# reconnect_time_wait: @config.retry_delay,
|
428
|
-
# max_reconnect_attempts: @config.reconnect_attempts
|
618
|
+
# max_reconnect_attempts: @config.reconnect_attempts,
|
619
|
+
# ping_interval: 20,
|
620
|
+
# max_outstanding_pings: 2,
|
621
|
+
# disconnected_cb: proc do |reason|
|
622
|
+
# NatsWave.logger.warn "🔌 NATS disconnected: #{reason}"
|
623
|
+
# end,
|
624
|
+
# reconnected_cb: proc do
|
625
|
+
# NatsWave.logger.info "🔌 NATS auto-reconnected"
|
626
|
+
# end,
|
627
|
+
# error_cb: proc do |error|
|
628
|
+
# NatsWave.logger.error "🔌 NATS error: #{error}"
|
629
|
+
# end
|
429
630
|
# )
|
430
|
-
#
|
631
|
+
#
|
632
|
+
# # Verify the connection actually worked
|
633
|
+
# if @client&.connected?
|
634
|
+
# NatsWave.logger.info "✅ Successfully connected to NATS at #{@config.nats_url}"
|
635
|
+
# else
|
636
|
+
# NatsWave.logger.error "❌ NATS client created but not connected"
|
637
|
+
# @client = nil
|
638
|
+
# end
|
639
|
+
#
|
431
640
|
# rescue => e
|
432
|
-
# NatsWave.logger.error
|
433
|
-
#
|
641
|
+
# NatsWave.logger.error "❌ Failed to connect to NATS: #{e.message}"
|
642
|
+
# NatsWave.logger.error "NATS URL: #{@config.nats_url}"
|
643
|
+
# NatsWave.logger.error "Error class: #{e.class}"
|
644
|
+
#
|
645
|
+
# @client = nil
|
646
|
+
#
|
647
|
+
# # Re-raise in development to catch issues early
|
648
|
+
# if defined?(Rails) && Rails.env.development?
|
649
|
+
# raise ConnectionError, "Failed to connect to NATS: #{e.message}"
|
650
|
+
# end
|
434
651
|
# end
|
435
652
|
#
|
436
|
-
# #
|
653
|
+
# # def establish_nats_connection
|
654
|
+
# # NatsWave.logger.info "Attempting to connect to NATS at #{@config.nats_url}"
|
655
|
+
# #
|
656
|
+
# # # Add more detailed connection options for debugging
|
657
|
+
# # connection_options = {
|
658
|
+
# # reconnect_time_wait: @config.retry_delay,
|
659
|
+
# # max_reconnect_attempts: @config.reconnect_attempts,
|
660
|
+
# # dont_randomize_servers: true,
|
661
|
+
# # verbose: true,
|
662
|
+
# # pedantic: false
|
663
|
+
# # }
|
664
|
+
# #
|
665
|
+
# # NatsWave.logger.info "Connection options: #{connection_options}"
|
666
|
+
# #
|
667
|
+
# # @client = NATS.connect(@config.nats_url, connection_options)
|
668
|
+
# #
|
669
|
+
# # # Wait a moment for connection to establish
|
670
|
+
# # sleep 0.5
|
671
|
+
# #
|
672
|
+
# # # Check connection status with more detail
|
673
|
+
# # if @client
|
674
|
+
# # NatsWave.logger.info "NATS client created: #{@client.class}"
|
675
|
+
# # NatsWave.logger.info "NATS client connected?: #{@client.connected?}"
|
676
|
+
# # NatsWave.logger.info "NATS client status: #{@client.status rescue 'unknown'}"
|
677
|
+
# #
|
678
|
+
# # if @client.connected?
|
679
|
+
# # NatsWave.logger.info "✅ Successfully connected to NATS at #{@config.nats_url}"
|
680
|
+
# # else
|
681
|
+
# # NatsWave.logger.error "❌ NATS client created but not connected"
|
682
|
+
# # NatsWave.logger.error "Client last error: #{@client.last_error rescue 'none'}"
|
683
|
+
# # @client = nil
|
684
|
+
# # end
|
685
|
+
# # else
|
686
|
+
# # NatsWave.logger.error "❌ NATS client is nil after connection attempt"
|
687
|
+
# # end
|
688
|
+
# #
|
689
|
+
# # rescue => e
|
690
|
+
# # NatsWave.logger.error "❌ Failed to connect to NATS: #{e.message}"
|
691
|
+
# # NatsWave.logger.error "Error class: #{e.class}"
|
692
|
+
# # NatsWave.logger.error "Error backtrace: #{e.backtrace.first(5).join('\n')}"
|
693
|
+
# # NatsWave.logger.error "NATS URL: #{@config.nats_url}"
|
694
|
+
# # NatsWave.logger.error "Retry delay: #{@config.retry_delay}"
|
695
|
+
# # NatsWave.logger.error "Max reconnect attempts: #{@config.reconnect_attempts}"
|
696
|
+
# #
|
697
|
+
# # @client = nil
|
698
|
+
# #
|
699
|
+
# # # Re-raise in development to catch issues early
|
700
|
+
# # if defined?(Rails) && Rails.env.development?
|
701
|
+
# # raise ConnectionError, "Failed to connect to NATS: #{e.message}"
|
702
|
+
# # end
|
703
|
+
# # end
|
704
|
+
#
|
437
705
|
# def ensure_connected!
|
438
706
|
# unless connected?
|
439
|
-
#
|
707
|
+
# error_msg = if @client.nil?
|
708
|
+
# "NATS client is nil - connection failed during initialization"
|
709
|
+
# else
|
710
|
+
# "NATS client exists but not connected (#{@client.class})"
|
711
|
+
# end
|
712
|
+
#
|
713
|
+
# NatsWave.logger.error "🔴 Connection check failed: #{error_msg}"
|
714
|
+
# NatsWave.logger.error "🔧 NATS URL: #{@config.nats_url}"
|
715
|
+
# NatsWave.logger.error "🔧 Publishing enabled: #{@config.publishing_enabled}"
|
716
|
+
# NatsWave.logger.error "🔧 Subscription enabled: #{@config.subscription_enabled}"
|
717
|
+
#
|
718
|
+
# raise ConnectionError, error_msg
|
440
719
|
# end
|
441
720
|
# end
|
442
721
|
#
|
@@ -446,13 +725,43 @@
|
|
446
725
|
# end
|
447
726
|
#
|
448
727
|
# def nats_available?
|
728
|
+
# # First, try to require the NATS gem if it's not already loaded
|
729
|
+
# begin
|
730
|
+
# require 'nats/client' unless defined?(NATS)
|
731
|
+
# rescue LoadError
|
732
|
+
# NatsWave.logger.error "❌ NATS gem not found. Add 'gem \"nats\"' to your Gemfile"
|
733
|
+
# return false
|
734
|
+
# end
|
735
|
+
#
|
736
|
+
# # Then check if the NATS constant is defined
|
449
737
|
# defined?(NATS)
|
450
|
-
#
|
451
|
-
#
|
738
|
+
# end
|
739
|
+
#
|
740
|
+
# def test_nats_connectivity
|
741
|
+
# require 'socket'
|
742
|
+
# require 'uri'
|
743
|
+
#
|
744
|
+
# begin
|
745
|
+
# uri = URI.parse(@config.nats_url)
|
746
|
+
# host = uri.host || 'localhost'
|
747
|
+
# port = uri.port || 4222
|
748
|
+
#
|
749
|
+
# NatsWave.logger.info "Testing TCP connectivity to #{host}:#{port}"
|
750
|
+
#
|
751
|
+
# socket = TCPSocket.new(host, port)
|
752
|
+
# socket.close
|
753
|
+
#
|
754
|
+
# NatsWave.logger.info "✅ TCP connection to NATS server successful"
|
755
|
+
# true
|
756
|
+
# rescue => e
|
757
|
+
# NatsWave.logger.error "❌ Cannot reach NATS server: #{e.message}"
|
758
|
+
# false
|
759
|
+
# end
|
452
760
|
# end
|
453
761
|
# end
|
454
762
|
# end
|
455
763
|
|
764
|
+
|
456
765
|
# frozen_string_literal: true
|
457
766
|
|
458
767
|
begin
|
@@ -609,79 +918,38 @@ module NatsWave
|
|
609
918
|
end
|
610
919
|
end
|
611
920
|
|
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
|
-
|
642
921
|
def establish_nats_connection
|
643
922
|
NatsWave.logger.info "Attempting to connect to NATS at #{@config.nats_url}"
|
644
923
|
|
645
|
-
|
646
|
-
|
924
|
+
@client = NATS.connect(
|
925
|
+
@config.nats_url,
|
647
926
|
reconnect_time_wait: @config.retry_delay,
|
648
927
|
max_reconnect_attempts: @config.reconnect_attempts,
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
928
|
+
ping_interval: 20,
|
929
|
+
max_outstanding_pings: 2,
|
930
|
+
disconnected_cb: proc do |reason|
|
931
|
+
NatsWave.logger.warn "🔌 NATS disconnected: #{reason}"
|
932
|
+
end,
|
933
|
+
reconnected_cb: proc do
|
934
|
+
NatsWave.logger.info "🔌 NATS auto-reconnected"
|
935
|
+
end,
|
936
|
+
error_cb: proc do |error|
|
937
|
+
NatsWave.logger.error "🔌 NATS error: #{error}"
|
938
|
+
end
|
939
|
+
)
|
660
940
|
|
661
|
-
#
|
662
|
-
if @client
|
663
|
-
NatsWave.logger.info "NATS
|
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
|
941
|
+
# Verify the connection actually worked
|
942
|
+
if @client&.connected?
|
943
|
+
NatsWave.logger.info "✅ Successfully connected to NATS at #{@config.nats_url}"
|
674
944
|
else
|
675
|
-
NatsWave.logger.error "❌ NATS client
|
945
|
+
NatsWave.logger.error "❌ NATS client created but not connected"
|
946
|
+
@client = nil
|
676
947
|
end
|
677
948
|
|
678
949
|
rescue => e
|
679
950
|
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
951
|
NatsWave.logger.error "NATS URL: #{@config.nats_url}"
|
683
|
-
NatsWave.logger.error "
|
684
|
-
NatsWave.logger.error "Max reconnect attempts: #{@config.reconnect_attempts}"
|
952
|
+
NatsWave.logger.error "Error class: #{e.class}"
|
685
953
|
|
686
954
|
@client = nil
|
687
955
|
|