jetstream_bridge 2.9.0 → 3.0.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 +4 -4
- data/CHANGELOG.md +164 -0
- data/LICENSE +21 -0
- data/README.md +379 -0
- data/lib/generators/jetstream_bridge/health_check/health_check_generator.rb +65 -0
- data/lib/generators/jetstream_bridge/health_check/templates/health_controller.rb +38 -0
- data/lib/generators/jetstream_bridge/initializer/templates/jetstream_bridge.rb +61 -13
- data/lib/generators/jetstream_bridge/install/install_generator.rb +4 -2
- data/lib/generators/jetstream_bridge/migrations/migrations_generator.rb +1 -0
- data/lib/jetstream_bridge/consumer/consumer.rb +50 -9
- data/lib/jetstream_bridge/consumer/dlq_publisher.rb +4 -1
- data/lib/jetstream_bridge/consumer/inbox/inbox_message.rb +8 -2
- data/lib/jetstream_bridge/consumer/inbox/inbox_repository.rb +37 -61
- data/lib/jetstream_bridge/consumer/message_processor.rb +105 -33
- data/lib/jetstream_bridge/consumer/subscription_manager.rb +13 -2
- data/lib/jetstream_bridge/core/config.rb +37 -1
- data/lib/jetstream_bridge/core/connection.rb +80 -3
- data/lib/jetstream_bridge/core/connection_factory.rb +102 -0
- data/lib/jetstream_bridge/core/debug_helper.rb +107 -0
- data/lib/jetstream_bridge/core/duration.rb +8 -1
- data/lib/jetstream_bridge/core/logging.rb +20 -7
- data/lib/jetstream_bridge/core/model_utils.rb +4 -3
- data/lib/jetstream_bridge/core/retry_strategy.rb +135 -0
- data/lib/jetstream_bridge/errors.rb +39 -0
- data/lib/jetstream_bridge/inbox_event.rb +4 -4
- data/lib/jetstream_bridge/models/event_envelope.rb +133 -0
- data/lib/jetstream_bridge/models/subject.rb +94 -0
- data/lib/jetstream_bridge/outbox_event.rb +3 -1
- data/lib/jetstream_bridge/publisher/outbox_repository.rb +47 -28
- data/lib/jetstream_bridge/publisher/publisher.rb +12 -35
- data/lib/jetstream_bridge/railtie.rb +35 -1
- data/lib/jetstream_bridge/tasks/install.rake +99 -0
- data/lib/jetstream_bridge/topology/overlap_guard.rb +15 -1
- data/lib/jetstream_bridge/topology/stream.rb +16 -8
- data/lib/jetstream_bridge/topology/subject_matcher.rb +17 -7
- data/lib/jetstream_bridge/topology/topology.rb +1 -1
- data/lib/jetstream_bridge/version.rb +1 -1
- data/lib/jetstream_bridge.rb +63 -6
- metadata +51 -10
- data/lib/jetstream_bridge/consumer/backoff_strategy.rb +0 -24
- data/lib/jetstream_bridge/consumer/consumer_config.rb +0 -26
- data/lib/jetstream_bridge/consumer/message_context.rb +0 -22
|
@@ -96,17 +96,27 @@ module JetstreamBridge
|
|
|
96
96
|
raise ArgumentError, 'subjects must not be empty' if desired.empty?
|
|
97
97
|
|
|
98
98
|
attempts = 0
|
|
99
|
+
max_attempts = 3
|
|
100
|
+
backoffs = [0.05, 0.2, 0.5]
|
|
101
|
+
|
|
99
102
|
begin
|
|
100
103
|
info = safe_stream_info(jts, name)
|
|
101
104
|
info ? ensure_update(jts, name, info, desired) : ensure_create(jts, name, desired)
|
|
102
105
|
rescue NATS::JetStream::Error => e
|
|
103
|
-
if StreamSupport.overlap_error?(e) && (attempts += 1) <=
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
if StreamSupport.overlap_error?(e) && (attempts += 1) <= max_attempts
|
|
107
|
+
backoff = backoffs[attempts - 1] || backoffs.last
|
|
108
|
+
Logging.warn(
|
|
109
|
+
"Overlap race while ensuring #{name}; retry #{attempts}/#{max_attempts} after #{backoff}s...",
|
|
110
|
+
tag: 'JetstreamBridge::Stream'
|
|
111
|
+
)
|
|
112
|
+
sleep(backoff)
|
|
106
113
|
retry
|
|
107
114
|
elsif StreamSupport.overlap_error?(e)
|
|
108
|
-
Logging.warn(
|
|
109
|
-
|
|
115
|
+
Logging.warn(
|
|
116
|
+
"Overlap persists ensuring #{name} after #{attempts} attempts; leaving unchanged. " \
|
|
117
|
+
"err=#{e.message.inspect}",
|
|
118
|
+
tag: 'JetstreamBridge::Stream'
|
|
119
|
+
)
|
|
110
120
|
nil
|
|
111
121
|
else
|
|
112
122
|
raise
|
|
@@ -123,9 +133,7 @@ module JetstreamBridge
|
|
|
123
133
|
|
|
124
134
|
# Retention is immutable; warn if different and do not include on update.
|
|
125
135
|
have_ret = info.config.retention.to_s.downcase
|
|
126
|
-
if have_ret != RETENTION
|
|
127
|
-
StreamSupport.log_retention_mismatch(name, have: have_ret, want: RETENTION)
|
|
128
|
-
end
|
|
136
|
+
StreamSupport.log_retention_mismatch(name, have: have_ret, want: RETENTION) if have_ret != RETENTION
|
|
129
137
|
|
|
130
138
|
# Storage can be updated; do it without passing retention.
|
|
131
139
|
have_storage = info.config.storage.to_s.downcase
|
|
@@ -48,20 +48,30 @@ module JetstreamBridge
|
|
|
48
48
|
while ai < a_parts.length && bi < b_parts.length
|
|
49
49
|
at = a_parts[ai]
|
|
50
50
|
bt = b_parts[bi]
|
|
51
|
-
return true if at
|
|
52
|
-
return false unless at
|
|
51
|
+
return true if tail?(at, bt)
|
|
52
|
+
return false unless token_match?(at, bt)
|
|
53
53
|
|
|
54
54
|
ai += 1
|
|
55
55
|
bi += 1
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
tail_overlap?(a_parts[ai..], b_parts[bi..])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def tail?(a_token, b_token)
|
|
62
|
+
a_token == '>' || b_token == '>'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def token_match?(a_token, b_token)
|
|
66
|
+
a_token == b_token || a_token == '*' || b_token == '*'
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def tail_overlap?(a_tail, b_tail)
|
|
70
|
+
a_tail ||= []
|
|
71
|
+
b_tail ||= []
|
|
61
72
|
return true if a_tail.include?('>') || b_tail.include?('>')
|
|
62
73
|
|
|
63
|
-
|
|
64
|
-
ai == a_parts.length && bi == b_parts.length
|
|
74
|
+
a_tail.empty? && b_tail.empty?
|
|
65
75
|
end
|
|
66
76
|
end
|
|
67
77
|
end
|
|
@@ -14,7 +14,7 @@ module JetstreamBridge
|
|
|
14
14
|
|
|
15
15
|
Logging.info(
|
|
16
16
|
"Subjects ready: producer=#{cfg.source_subject}, consumer=#{cfg.destination_subject}. " \
|
|
17
|
-
|
|
17
|
+
"Counterpart publishes on #{cfg.destination_subject} and subscribes on #{cfg.source_subject}.",
|
|
18
18
|
tag: 'JetstreamBridge::Topology'
|
|
19
19
|
)
|
|
20
20
|
end
|
data/lib/jetstream_bridge.rb
CHANGED
|
@@ -15,7 +15,6 @@ require_relative 'jetstream_bridge/railtie' if defined?(Rails::Railtie)
|
|
|
15
15
|
require_relative 'jetstream_bridge/inbox_event'
|
|
16
16
|
require_relative 'jetstream_bridge/outbox_event'
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
# JetstreamBridge main module.
|
|
20
19
|
module JetstreamBridge
|
|
21
20
|
class << self
|
|
@@ -54,15 +53,73 @@ module JetstreamBridge
|
|
|
54
53
|
Connection.jetstream
|
|
55
54
|
end
|
|
56
55
|
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
# Health check for monitoring and readiness probes
|
|
57
|
+
#
|
|
58
|
+
# @return [Hash] Health status including NATS connection, stream, and version
|
|
59
|
+
def health_check
|
|
60
|
+
conn_instance = Connection.instance
|
|
61
|
+
connected = conn_instance.connected?
|
|
62
|
+
connected_at = conn_instance.connected_at
|
|
63
|
+
|
|
64
|
+
stream_info = fetch_stream_info if connected
|
|
65
|
+
|
|
66
|
+
{
|
|
67
|
+
healthy: connected && stream_info[:exists],
|
|
68
|
+
nats_connected: connected,
|
|
69
|
+
connected_at: connected_at&.iso8601,
|
|
70
|
+
stream: stream_info,
|
|
71
|
+
config: {
|
|
72
|
+
env: config.env,
|
|
73
|
+
app_name: config.app_name,
|
|
74
|
+
destination_app: config.destination_app,
|
|
75
|
+
use_outbox: config.use_outbox,
|
|
76
|
+
use_inbox: config.use_inbox,
|
|
77
|
+
use_dlq: config.use_dlq
|
|
78
|
+
},
|
|
79
|
+
version: JetstreamBridge::VERSION
|
|
80
|
+
}
|
|
81
|
+
rescue StandardError => e
|
|
82
|
+
{
|
|
83
|
+
healthy: false,
|
|
84
|
+
error: "#{e.class}: #{e.message}"
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Check if connected to NATS
|
|
89
|
+
#
|
|
90
|
+
# @return [Boolean] true if connected and healthy
|
|
91
|
+
def connected?
|
|
92
|
+
Connection.instance.connected?
|
|
93
|
+
rescue StandardError
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Get stream information for the configured stream
|
|
98
|
+
#
|
|
99
|
+
# @return [Hash] Stream information including subjects and message count
|
|
100
|
+
def stream_info
|
|
101
|
+
fetch_stream_info
|
|
62
102
|
end
|
|
63
103
|
|
|
64
104
|
private
|
|
65
105
|
|
|
106
|
+
def fetch_stream_info
|
|
107
|
+
jts = Connection.jetstream
|
|
108
|
+
info = jts.stream_info(config.stream_name)
|
|
109
|
+
{
|
|
110
|
+
exists: true,
|
|
111
|
+
name: config.stream_name,
|
|
112
|
+
subjects: info.config.subjects,
|
|
113
|
+
messages: info.state.messages
|
|
114
|
+
}
|
|
115
|
+
rescue StandardError => e
|
|
116
|
+
{
|
|
117
|
+
exists: false,
|
|
118
|
+
name: config.stream_name,
|
|
119
|
+
error: "#{e.class}: #{e.message}"
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
|
|
66
123
|
def assign!(cfg, key, val)
|
|
67
124
|
setter = :"#{key}="
|
|
68
125
|
raise ArgumentError, "Unknown configuration option: #{key}" unless cfg.respond_to?(setter)
|
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:
|
|
4
|
+
version: 3.0.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: 2025-
|
|
11
|
+
date: 2025-11-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -17,6 +17,9 @@ dependencies:
|
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '6.0'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '8.0'
|
|
20
23
|
type: :runtime
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -24,6 +27,9 @@ dependencies:
|
|
|
24
27
|
- - ">="
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
29
|
version: '6.0'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.0'
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
34
|
name: activesupport
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -31,6 +37,9 @@ dependencies:
|
|
|
31
37
|
- - ">="
|
|
32
38
|
- !ruby/object:Gem::Version
|
|
33
39
|
version: '6.0'
|
|
40
|
+
- - "<"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '8.0'
|
|
34
43
|
type: :runtime
|
|
35
44
|
prerelease: false
|
|
36
45
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -38,6 +47,9 @@ dependencies:
|
|
|
38
47
|
- - ">="
|
|
39
48
|
- !ruby/object:Gem::Version
|
|
40
49
|
version: '6.0'
|
|
50
|
+
- - "<"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '8.0'
|
|
41
53
|
- !ruby/object:Gem::Dependency
|
|
42
54
|
name: nats-pure
|
|
43
55
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -59,6 +71,9 @@ dependencies:
|
|
|
59
71
|
- - ">="
|
|
60
72
|
- !ruby/object:Gem::Version
|
|
61
73
|
version: '3.16'
|
|
74
|
+
- - "<"
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '4.0'
|
|
62
77
|
type: :runtime
|
|
63
78
|
prerelease: false
|
|
64
79
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -66,16 +81,39 @@ dependencies:
|
|
|
66
81
|
- - ">="
|
|
67
82
|
- !ruby/object:Gem::Version
|
|
68
83
|
version: '3.16'
|
|
84
|
+
- - "<"
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '4.0'
|
|
87
|
+
- !ruby/object:Gem::Dependency
|
|
88
|
+
name: mutex_m
|
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '0'
|
|
94
|
+
type: :runtime
|
|
95
|
+
prerelease: false
|
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - ">="
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: '0'
|
|
69
101
|
description: |-
|
|
70
102
|
Publisher/Consumer utilities for NATS JetStream with environment-scoped subjects,
|
|
71
103
|
overlap guards, DLQ routing, retries/backoff, and optional Inbox/Outbox patterns.
|
|
72
|
-
Includes
|
|
104
|
+
Includes health checks, auto-reconnection, graceful shutdown, and topology setup
|
|
105
|
+
helpers for production-safe operation.
|
|
73
106
|
email:
|
|
74
107
|
- mpyebattara@gmail.com
|
|
75
108
|
executables: []
|
|
76
109
|
extensions: []
|
|
77
110
|
extra_rdoc_files: []
|
|
78
111
|
files:
|
|
112
|
+
- CHANGELOG.md
|
|
113
|
+
- LICENSE
|
|
114
|
+
- README.md
|
|
115
|
+
- lib/generators/jetstream_bridge/health_check/health_check_generator.rb
|
|
116
|
+
- lib/generators/jetstream_bridge/health_check/templates/health_controller.rb
|
|
79
117
|
- lib/generators/jetstream_bridge/initializer/initializer_generator.rb
|
|
80
118
|
- lib/generators/jetstream_bridge/initializer/templates/jetstream_bridge.rb
|
|
81
119
|
- lib/generators/jetstream_bridge/install/install_generator.rb
|
|
@@ -83,23 +121,26 @@ files:
|
|
|
83
121
|
- lib/generators/jetstream_bridge/migrations/templates/create_jetstream_inbox_events.rb.erb
|
|
84
122
|
- lib/generators/jetstream_bridge/migrations/templates/create_jetstream_outbox_events.rb.erb
|
|
85
123
|
- lib/jetstream_bridge.rb
|
|
86
|
-
- lib/jetstream_bridge/consumer/backoff_strategy.rb
|
|
87
124
|
- lib/jetstream_bridge/consumer/consumer.rb
|
|
88
|
-
- lib/jetstream_bridge/consumer/consumer_config.rb
|
|
89
125
|
- lib/jetstream_bridge/consumer/dlq_publisher.rb
|
|
90
126
|
- lib/jetstream_bridge/consumer/inbox/inbox_message.rb
|
|
91
127
|
- lib/jetstream_bridge/consumer/inbox/inbox_processor.rb
|
|
92
128
|
- lib/jetstream_bridge/consumer/inbox/inbox_repository.rb
|
|
93
|
-
- lib/jetstream_bridge/consumer/message_context.rb
|
|
94
129
|
- lib/jetstream_bridge/consumer/message_processor.rb
|
|
95
130
|
- lib/jetstream_bridge/consumer/subscription_manager.rb
|
|
96
131
|
- lib/jetstream_bridge/core/config.rb
|
|
97
132
|
- lib/jetstream_bridge/core/connection.rb
|
|
133
|
+
- lib/jetstream_bridge/core/connection_factory.rb
|
|
134
|
+
- lib/jetstream_bridge/core/debug_helper.rb
|
|
98
135
|
- lib/jetstream_bridge/core/duration.rb
|
|
99
136
|
- lib/jetstream_bridge/core/logging.rb
|
|
100
137
|
- lib/jetstream_bridge/core/model_codec_setup.rb
|
|
101
138
|
- lib/jetstream_bridge/core/model_utils.rb
|
|
139
|
+
- lib/jetstream_bridge/core/retry_strategy.rb
|
|
140
|
+
- lib/jetstream_bridge/errors.rb
|
|
102
141
|
- lib/jetstream_bridge/inbox_event.rb
|
|
142
|
+
- lib/jetstream_bridge/models/event_envelope.rb
|
|
143
|
+
- lib/jetstream_bridge/models/subject.rb
|
|
103
144
|
- lib/jetstream_bridge/outbox_event.rb
|
|
104
145
|
- lib/jetstream_bridge/publisher/outbox_repository.rb
|
|
105
146
|
- lib/jetstream_bridge/publisher/publisher.rb
|
|
@@ -119,8 +160,8 @@ metadata:
|
|
|
119
160
|
changelog_uri: https://github.com/attaradev/jetstream_bridge/blob/main/CHANGELOG.md
|
|
120
161
|
documentation_uri: https://github.com/attaradev/jetstream_bridge#readme
|
|
121
162
|
bug_tracker_uri: https://github.com/attaradev/jetstream_bridge/issues
|
|
122
|
-
github_repo: ssh://github.com/attaradev/jetstream_bridge
|
|
123
163
|
rubygems_mfa_required: 'true'
|
|
164
|
+
allowed_push_host: https://rubygems.org
|
|
124
165
|
post_install_message:
|
|
125
166
|
rdoc_options: []
|
|
126
167
|
require_paths:
|
|
@@ -129,15 +170,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
129
170
|
requirements:
|
|
130
171
|
- - ">="
|
|
131
172
|
- !ruby/object:Gem::Version
|
|
132
|
-
version: 2.
|
|
173
|
+
version: 3.2.0
|
|
133
174
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
175
|
requirements:
|
|
135
176
|
- - ">="
|
|
136
177
|
- !ruby/object:Gem::Version
|
|
137
|
-
version:
|
|
178
|
+
version: '0'
|
|
138
179
|
requirements: []
|
|
139
180
|
rubygems_version: 3.4.19
|
|
140
181
|
signing_key:
|
|
141
182
|
specification_version: 4
|
|
142
|
-
summary:
|
|
183
|
+
summary: Production-safe realtime data bridge using NATS JetStream
|
|
143
184
|
test_files: []
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JetstreamBridge
|
|
4
|
-
class BackoffStrategy
|
|
5
|
-
TRANSIENT_ERRORS = [Timeout::Error, IOError].freeze
|
|
6
|
-
MAX_EXPONENT = 6
|
|
7
|
-
MAX_DELAY = 60
|
|
8
|
-
MIN_DELAY = 1
|
|
9
|
-
|
|
10
|
-
# Returns a bounded delay in seconds
|
|
11
|
-
def delay(deliveries, error)
|
|
12
|
-
base = transient?(error) ? 0.5 : 2.0
|
|
13
|
-
power = [deliveries - 1, MAX_EXPONENT].min
|
|
14
|
-
raw = (base * (2**power)).to_i
|
|
15
|
-
raw.clamp(MIN_DELAY, MAX_DELAY)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
def transient?(error)
|
|
21
|
-
TRANSIENT_ERRORS.any? { |k| error.is_a?(k) }
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../core/duration'
|
|
4
|
-
require_relative '../core/config'
|
|
5
|
-
require_relative '../core/logging'
|
|
6
|
-
|
|
7
|
-
module JetstreamBridge
|
|
8
|
-
# Consumer configuration helpers.
|
|
9
|
-
module ConsumerConfig
|
|
10
|
-
module_function
|
|
11
|
-
|
|
12
|
-
# Complete consumer config (pre-provisioned durable, pull mode).
|
|
13
|
-
def consumer_config(durable, filter_subject)
|
|
14
|
-
{
|
|
15
|
-
durable_name: durable,
|
|
16
|
-
filter_subject: filter_subject,
|
|
17
|
-
ack_policy: 'explicit',
|
|
18
|
-
deliver_policy: 'all',
|
|
19
|
-
max_deliver: JetstreamBridge.config.max_deliver,
|
|
20
|
-
ack_wait: Duration.to_millis(JetstreamBridge.config.ack_wait),
|
|
21
|
-
backoff: Array(JetstreamBridge.config.backoff)
|
|
22
|
-
.map { |d| Duration.to_millis(d) }
|
|
23
|
-
}
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'securerandom'
|
|
4
|
-
|
|
5
|
-
module JetstreamBridge
|
|
6
|
-
# Immutable per-message metadata
|
|
7
|
-
MessageContext = Struct.new(
|
|
8
|
-
:event_id, :deliveries, :subject, :seq, :consumer, :stream,
|
|
9
|
-
keyword_init: true
|
|
10
|
-
) do
|
|
11
|
-
def self.build(msg)
|
|
12
|
-
new(
|
|
13
|
-
event_id: msg.header&.[]('nats-msg-id') || SecureRandom.uuid,
|
|
14
|
-
deliveries: msg.metadata&.num_delivered.to_i,
|
|
15
|
-
subject: msg.subject,
|
|
16
|
-
seq: msg.metadata&.sequence,
|
|
17
|
-
consumer: msg.metadata&.consumer,
|
|
18
|
-
stream: msg.metadata&.stream
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|