jetstream_bridge 4.0.3 → 4.0.4
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 +11 -0
- data/lib/jetstream_bridge/core/debug_helper.rb +24 -12
- data/lib/jetstream_bridge/topology/overlap_guard.rb +4 -1
- data/lib/jetstream_bridge/topology/stream.rb +8 -3
- data/lib/jetstream_bridge/version.rb +1 -1
- data/lib/jetstream_bridge.rb +42 -3
- 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: 3d2af28b3492eb97a04b9337390f71f90786321baf7c50f3f49de3a2cbe1b5c3
|
|
4
|
+
data.tar.gz: 3746cb676e9739a087c295488d85b51a6acec82c4ffca81027726cf33a9ada53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 91cff40e1f076eaee83bf937d10cbbcaaaf0e311fabb347e3ae757d7e7bd0645bcea338cd222cbfcb9d42c37deefd88076d618af2185572ca4cdeb4fa85dea27
|
|
7
|
+
data.tar.gz: 5fc680881fb91c550b8f6561babfd2227a3a9e263d3c07f1bd89a0de61fd835a8bf74b0731603384a16f078b401f22afbdbf31e94d62bac88ed1473f8c0aa15b
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.0.4] - 2025-11-23
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **NATS Compatibility** - Fix connection failure with nats-pure 2.5.0
|
|
13
|
+
- Handle both object-style and hash-style access for stream_info responses
|
|
14
|
+
- Fixes "undefined method 'streams' for Hash" error during connection establishment
|
|
15
|
+
- Adds compatibility checks using `respond_to?` for config and state attributes
|
|
16
|
+
- Updated 4 files: jetstream_bridge.rb, topology/stream.rb, topology/overlap_guard.rb, debug_helper.rb
|
|
17
|
+
- Maintains backward compatibility with older nats-pure versions
|
|
18
|
+
|
|
8
19
|
## [4.0.3] - 2025-11-23
|
|
9
20
|
|
|
10
21
|
### Added
|
|
@@ -81,18 +81,7 @@ module JetstreamBridge
|
|
|
81
81
|
cfg = JetstreamBridge.config
|
|
82
82
|
info = jts.stream_info(cfg.stream_name)
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
name: cfg.stream_name,
|
|
86
|
-
exists: true,
|
|
87
|
-
subjects: info.config.subjects,
|
|
88
|
-
retention: info.config.retention,
|
|
89
|
-
storage: info.config.storage,
|
|
90
|
-
max_consumers: info.config.max_consumers,
|
|
91
|
-
messages: info.state.messages,
|
|
92
|
-
bytes: info.state.bytes,
|
|
93
|
-
first_seq: info.state.first_seq,
|
|
94
|
-
last_seq: info.state.last_seq
|
|
95
|
-
}
|
|
84
|
+
build_stream_info(cfg, info)
|
|
96
85
|
rescue StandardError => e
|
|
97
86
|
{
|
|
98
87
|
name: JetstreamBridge.config.stream_name,
|
|
@@ -101,6 +90,29 @@ module JetstreamBridge
|
|
|
101
90
|
}
|
|
102
91
|
end
|
|
103
92
|
|
|
93
|
+
def build_stream_info(cfg, info)
|
|
94
|
+
# Handle both object-style and hash-style access for compatibility
|
|
95
|
+
config_data = info.config
|
|
96
|
+
state_data = info.state
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
name: cfg.stream_name,
|
|
100
|
+
exists: true,
|
|
101
|
+
subjects: safe_attr(config_data, :subjects),
|
|
102
|
+
retention: safe_attr(config_data, :retention),
|
|
103
|
+
storage: safe_attr(config_data, :storage),
|
|
104
|
+
max_consumers: safe_attr(config_data, :max_consumers),
|
|
105
|
+
messages: safe_attr(state_data, :messages),
|
|
106
|
+
bytes: safe_attr(state_data, :bytes),
|
|
107
|
+
first_seq: safe_attr(state_data, :first_seq),
|
|
108
|
+
last_seq: safe_attr(state_data, :last_seq)
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def safe_attr(obj, attr)
|
|
113
|
+
obj.respond_to?(attr) ? obj.public_send(attr) : obj[attr]
|
|
114
|
+
end
|
|
115
|
+
|
|
104
116
|
def log_hash(hash, indent: 0)
|
|
105
117
|
prefix = ' ' * indent
|
|
106
118
|
hash.each do |key, value|
|
|
@@ -48,7 +48,10 @@ module JetstreamBridge
|
|
|
48
48
|
def list_streams_with_subjects(jts)
|
|
49
49
|
list_stream_names(jts).map do |name|
|
|
50
50
|
info = jts.stream_info(name)
|
|
51
|
-
|
|
51
|
+
# Handle both object-style and hash-style access for compatibility
|
|
52
|
+
config_data = info.config
|
|
53
|
+
subjects = config_data.respond_to?(:subjects) ? config_data.subjects : config_data[:subjects]
|
|
54
|
+
{ name: name, subjects: Array(subjects || []) }
|
|
52
55
|
end
|
|
53
56
|
end
|
|
54
57
|
|
|
@@ -127,11 +127,15 @@ module JetstreamBridge
|
|
|
127
127
|
private
|
|
128
128
|
|
|
129
129
|
def ensure_update(jts, name, info, desired_subjects)
|
|
130
|
-
|
|
130
|
+
# Handle both object-style and hash-style access for compatibility
|
|
131
|
+
config_data = info.config
|
|
132
|
+
subjects = config_data.respond_to?(:subjects) ? config_data.subjects : config_data[:subjects]
|
|
133
|
+
existing = StreamSupport.normalize_subjects(subjects || [])
|
|
131
134
|
to_add = StreamSupport.missing_subjects(existing, desired_subjects)
|
|
132
135
|
|
|
133
136
|
# Retention is immutable; if different, skip all updates to avoid 10052 error.
|
|
134
|
-
|
|
137
|
+
retention = config_data.respond_to?(:retention) ? config_data.retention : config_data[:retention]
|
|
138
|
+
have_ret = retention.to_s.downcase
|
|
135
139
|
if have_ret != RETENTION
|
|
136
140
|
StreamSupport.log_retention_mismatch(name, have: have_ret, want: RETENTION)
|
|
137
141
|
return
|
|
@@ -140,7 +144,8 @@ module JetstreamBridge
|
|
|
140
144
|
add_subjects(jts, name, existing, to_add) if to_add.any?
|
|
141
145
|
|
|
142
146
|
# Storage can be updated; do it without passing retention.
|
|
143
|
-
|
|
147
|
+
storage = config_data.respond_to?(:storage) ? config_data.storage : config_data[:storage]
|
|
148
|
+
have_storage = storage.to_s.downcase
|
|
144
149
|
if have_storage != STORAGE
|
|
145
150
|
apply_update(jts, name, existing, storage: STORAGE)
|
|
146
151
|
StreamSupport.log_config_updated(name, storage: STORAGE)
|
data/lib/jetstream_bridge.rb
CHANGED
|
@@ -115,21 +115,39 @@ module JetstreamBridge
|
|
|
115
115
|
Connection.jetstream
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
-
#
|
|
118
|
+
# Active health check for monitoring and readiness probes
|
|
119
|
+
#
|
|
120
|
+
# Performs actual operations to verify system health:
|
|
121
|
+
# - Checks NATS connection (active: calls account_info API)
|
|
122
|
+
# - Verifies stream exists and is accessible (active: queries stream info)
|
|
123
|
+
# - Tests NATS round-trip communication (active: RTT measurement)
|
|
119
124
|
#
|
|
120
125
|
# @return [Hash] Health status including NATS connection, stream, and version
|
|
121
126
|
def health_check
|
|
127
|
+
start_time = Time.now
|
|
122
128
|
conn_instance = Connection.instance
|
|
129
|
+
|
|
130
|
+
# Active check: calls @jts.account_info internally
|
|
123
131
|
connected = conn_instance.connected?
|
|
124
132
|
connected_at = conn_instance.connected_at
|
|
125
133
|
|
|
134
|
+
# Active check: queries actual stream from NATS server
|
|
126
135
|
stream_info = fetch_stream_info if connected
|
|
127
136
|
|
|
137
|
+
# Active check: measure NATS round-trip time
|
|
138
|
+
rtt_ms = measure_nats_rtt if connected
|
|
139
|
+
|
|
140
|
+
health_check_duration_ms = ((Time.now - start_time) * 1000).round(2)
|
|
141
|
+
|
|
128
142
|
{
|
|
129
143
|
healthy: connected && stream_info&.fetch(:exists, false),
|
|
130
144
|
nats_connected: connected,
|
|
131
145
|
connected_at: connected_at&.iso8601,
|
|
132
146
|
stream: stream_info,
|
|
147
|
+
performance: {
|
|
148
|
+
nats_rtt_ms: rtt_ms,
|
|
149
|
+
health_check_duration_ms: health_check_duration_ms
|
|
150
|
+
},
|
|
133
151
|
config: {
|
|
134
152
|
env: config.env,
|
|
135
153
|
app_name: config.app_name,
|
|
@@ -281,11 +299,18 @@ module JetstreamBridge
|
|
|
281
299
|
def fetch_stream_info
|
|
282
300
|
jts = Connection.jetstream
|
|
283
301
|
info = jts.stream_info(config.stream_name)
|
|
302
|
+
|
|
303
|
+
# Handle both object-style and hash-style access for compatibility
|
|
304
|
+
config_data = info.config
|
|
305
|
+
state_data = info.state
|
|
306
|
+
subjects = config_data.respond_to?(:subjects) ? config_data.subjects : config_data[:subjects]
|
|
307
|
+
messages = state_data.respond_to?(:messages) ? state_data.messages : state_data[:messages]
|
|
308
|
+
|
|
284
309
|
{
|
|
285
310
|
exists: true,
|
|
286
311
|
name: config.stream_name,
|
|
287
|
-
subjects:
|
|
288
|
-
messages:
|
|
312
|
+
subjects: subjects,
|
|
313
|
+
messages: messages
|
|
289
314
|
}
|
|
290
315
|
rescue StandardError => e
|
|
291
316
|
{
|
|
@@ -295,6 +320,20 @@ module JetstreamBridge
|
|
|
295
320
|
}
|
|
296
321
|
end
|
|
297
322
|
|
|
323
|
+
def measure_nats_rtt
|
|
324
|
+
# Measure round-trip time using NATS RTT method
|
|
325
|
+
nc = Connection.nc
|
|
326
|
+
start = Time.now
|
|
327
|
+
nc.rtt
|
|
328
|
+
((Time.now - start) * 1000).round(2)
|
|
329
|
+
rescue StandardError => e
|
|
330
|
+
Logging.warn(
|
|
331
|
+
"Failed to measure NATS RTT: #{e.class} #{e.message}",
|
|
332
|
+
tag: 'JetstreamBridge'
|
|
333
|
+
)
|
|
334
|
+
nil
|
|
335
|
+
end
|
|
336
|
+
|
|
298
337
|
def assign!(cfg, key, val)
|
|
299
338
|
setter = :"#{key}="
|
|
300
339
|
raise ArgumentError, "Unknown configuration option: #{key}" unless cfg.respond_to?(setter)
|