jetstream_bridge 2.0.0 β†’ 2.2.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: e107d86ffe1dfa7ee9362c82a6f827ad5e5501d0c9eedff4b2da359c360465cc
4
- data.tar.gz: 33e9522da1b3a13161858c46b125978a3760d5263996ff4a12ad4b7d5ab2a8cd
3
+ metadata.gz: 4d4dcab294bcdbc3c618eef156debf4d7d5e567a105c7795b509cc68543cb592
4
+ data.tar.gz: de590eb069ca8f09a17b69c53cdc01c4730563ce51f9f43f937cff66d41df700
5
5
  SHA512:
6
- metadata.gz: 4c276cc1a4c2bfa086b39051d60a8cae9b3668c70a556ffd08c6235bf2031f74649872ec265921604c36589b1fa4776cd74094ecc5977aad3c09abe4d989e8f7
7
- data.tar.gz: 571d9ca427e2ea0f06a428ec197242bd437f54711fefbcfa23f13450a39ad7c70bc70720af8ccad2b4039579246e2d056daac9bd48b11ea232e0fbb1a4369adc
6
+ metadata.gz: '09c98872c6174c4c5a71d1d94d049ce0e554a0667e9a0db864a0a245a6d1cb5865db9b9a979de3d39a2948a7943da260e8f2c9d90fcd74f3eee65490e2d63968'
7
+ data.tar.gz: 4405598ee5e9050c1f344ed00e388974da0959775a9cd07460ec2f6dbf5c730f562210c2c35e16e5b0b2e3eac61bc3905e294dd3066c61f7bce12a4108000ff7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jetstream_bridge (2.0.0)
4
+ jetstream_bridge (2.2.0)
5
5
  activerecord (>= 6.0)
6
6
  activesupport (>= 6.0)
7
7
  nats-pure (~> 2.4)
@@ -142,8 +142,6 @@ GEM
142
142
  racc (~> 1.4)
143
143
  nokogiri (1.18.7-arm64-darwin)
144
144
  racc (~> 1.4)
145
- nokogiri (1.18.7-x86_64-linux-gnu)
146
- racc (~> 1.4)
147
145
  parallel (1.27.0)
148
146
  parser (3.3.9.0)
149
147
  ast (~> 2.4.1)
data/README.md CHANGED
@@ -98,11 +98,11 @@ end
98
98
 
99
99
  ## πŸ“‘ Subject Conventions
100
100
 
101
- | Direction | Subject Pattern |
102
- |---------------|--------------------------------|
103
- | **Publish** | `{env}.data.sync.{app}.{dest}` |
104
- | **Subscribe** | `{env}.data.sync.{dest}.{app}` |
105
- | **DLQ** | `{env}.data.sync.dlq` |
101
+ | Direction | Subject Pattern |
102
+ |---------------|---------------------------|
103
+ | **Publish** | `{env}.{app}.sync.{dest}` |
104
+ | **Subscribe** | `{env}.{dest}.sync.{app}` |
105
+ | **DLQ** | `{env}.sync.dlq` |
106
106
 
107
107
  * `{app}`: `app_name`
108
108
  * `{dest}`: `destination_app`
@@ -110,12 +110,12 @@ end
110
110
 
111
111
  ---
112
112
 
113
- ## 🧱 Stream Topology (auto-ensure & overlap-safe)
113
+ ## 🧱 Stream Topology (auto-ensure and overlap-safe)
114
114
 
115
115
  On first connection, Jetstream Bridge **ensures** a single stream exists for your `env` and that it covers:
116
116
 
117
- * `source_subject` (`{env}.data.sync.{app}.{dest}`)
118
- * `destination_subject` (`{env}.data.sync.{dest}.{app}`)
117
+ * `source_subject` (`{env}.{app}.sync.{dest}`)
118
+ * `destination_subject` (`{env}.{dest}.sync.{app}`)
119
119
  * `dlq_subject` (if enabled)
120
120
 
121
121
  It’s **overlap-safe**:
@@ -200,7 +200,7 @@ If **Outbox** is enabled, the publish call:
200
200
 
201
201
  ```ruby
202
202
  JetstreamBridge::Consumer.new(
203
- durable_name: "#{Rails.env}-peerapp-events",
203
+ durable_name: "#{Rails.env}-#{app_name}-workers",
204
204
  batch_size: 25
205
205
  ) do |event, subject, deliveries|
206
206
  # Your idempotent domain logic here
@@ -13,7 +13,7 @@ require_relative 'subscription_manager'
13
13
  require_relative 'inbox/inbox_processor'
14
14
 
15
15
  module JetstreamBridge
16
- # Subscribes to "{env}.data.sync.{dest}.{app}" and processes messages.
16
+ # Subscribes to "{env}.{dest}.sync.{app}" and processes messages.
17
17
  class Consumer
18
18
  DEFAULT_BATCH_SIZE = 25
19
19
  FETCH_TIMEOUT_SECS = 5
@@ -30,19 +30,19 @@ module JetstreamBridge
30
30
  end
31
31
 
32
32
  # Base subjects
33
- # Producer publishes to: {env}.data.sync.{app}.{dest}
34
- # Consumer subscribes to: {env}.data.sync.{dest}.{app}
33
+ # Producer publishes to: {env}.{app}.sync.{dest}
34
+ # Consumer subscribes to: {env}.{dest}.sync.{app}
35
35
  def source_subject
36
- "#{env}.data.sync.#{app_name}.#{destination_app}"
36
+ "#{env}.#{app_name}.sync.#{destination_app}"
37
37
  end
38
38
 
39
39
  def destination_subject
40
- "#{env}.data.sync.#{destination_app}.#{app_name}"
40
+ "#{env}.#{destination_app}.sync.#{app_name}"
41
41
  end
42
42
 
43
43
  # DLQ
44
44
  def dlq_subject
45
- "#{env}.data.sync.dlq"
45
+ "#{env}.sync.dlq"
46
46
  end
47
47
 
48
48
  def durable_name
@@ -21,11 +21,21 @@ module JetstreamBridge
21
21
  }.freeze
22
22
 
23
23
  class << self
24
- # Thread-safe delegator to the singleton instance
24
+ # Thread-safe delegator to the singleton instance.
25
+ # Returns a live JetStream context.
25
26
  def connect!
26
27
  @__mutex ||= Mutex.new
27
28
  @__mutex.synchronize { instance.connect! }
28
29
  end
30
+
31
+ # Optional accessors if callers need raw handles
32
+ def nc
33
+ instance.__send__(:nc)
34
+ end
35
+
36
+ def jetstream
37
+ instance.__send__(:jetstream)
38
+ end
29
39
  end
30
40
 
31
41
  # Idempotent: returns an existing, healthy JetStream context or establishes one.
@@ -36,12 +46,17 @@ module JetstreamBridge
36
46
  raise 'No NATS URLs configured' if servers.empty?
37
47
 
38
48
  establish_connection(servers)
49
+
39
50
  Logging.info(
40
- "Connected to NATS (#{servers.size} server#{servers.size == 1 ? '' : 's'}): #{sanitize_urls(servers).join(',')}",
51
+ "Connected to NATS (#{servers.size} server#{unless servers.size == 1
52
+ 's'
53
+ end}): #{sanitize_urls(servers).join(', ')}",
41
54
  tag: 'JetstreamBridge::Connection'
42
55
  )
43
56
 
57
+ # Ensure topology (streams, subjects, overlap guard, etc.)
44
58
  Topology.ensure!(@jts)
59
+
45
60
  @jts
46
61
  end
47
62
 
@@ -62,7 +77,26 @@ module JetstreamBridge
62
77
  def establish_connection(servers)
63
78
  @nc = NATS::IO::Client.new
64
79
  @nc.connect({ servers: servers }.merge(DEFAULT_CONN_OPTS))
80
+
81
+ # Create JetStream context
65
82
  @jts = @nc.jetstream
83
+
84
+ # --- Compatibility shim: ensure JetStream responds to #nc for older/newer clients ---
85
+ # Some versions of the NATS Ruby client don't expose nc on the JetStream object.
86
+ # We attach a singleton method, so code expecting `js.nc` continues to work.
87
+ return if @jts.respond_to?(:nc)
88
+
89
+ nc_ref = @nc
90
+ @jts.define_singleton_method(:nc) { nc_ref }
91
+
92
+ # ------------------------------------------------------------------------------------
93
+ end
94
+
95
+ # Expose for class-level helpers (not part of public API)
96
+ attr_reader :nc
97
+
98
+ def jetstream
99
+ @jts
66
100
  end
67
101
 
68
102
  # Mask credentials in NATS URLs:
@@ -9,7 +9,7 @@ require_relative '../core/model_utils'
9
9
  require_relative 'outbox_repository'
10
10
 
11
11
  module JetstreamBridge
12
- # Publishes to "{env}.data.sync.{app}.{dest}".
12
+ # Publishes to "{env}.{app}.sync.{dest}".
13
13
  class Publisher
14
14
  DEFAULT_RETRIES = 2
15
15
  RETRY_BACKOFFS = [0.25, 1.0].freeze
@@ -33,7 +33,7 @@ module JetstreamBridge
33
33
  if JetstreamBridge.config.use_outbox
34
34
  publish_via_outbox(subject, envelope)
35
35
  else
36
- with_retries { do_publish(subject, envelope) }
36
+ with_retries { do_publish?(subject, envelope) }
37
37
  end
38
38
  rescue StandardError => e
39
39
  log_error(false, e)
@@ -47,8 +47,8 @@ module JetstreamBridge
47
47
  raise ArgumentError, 'destination_app must be configured'
48
48
  end
49
49
 
50
- def do_publish(subject, envelope)
51
- headers = { 'Nats-Msg-Id' => envelope['event_id'] }
50
+ def do_publish?(subject, envelope)
51
+ headers = { 'nats-msg-id' => envelope['event_id'] }
52
52
  @jts.publish(subject, JSON.generate(envelope), header: headers)
53
53
  Logging.info("Published #{subject} event_id=#{envelope['event_id']}",
54
54
  tag: 'JetstreamBridge::Publisher')
@@ -62,7 +62,7 @@ module JetstreamBridge
62
62
  unless ModelUtils.ar_class?(klass)
63
63
  Logging.warn("Outbox model #{klass} is not an ActiveRecord model; publishing directly.",
64
64
  tag: 'JetstreamBridge::Publisher')
65
- return with_retries { do_publish(subject, envelope) }
65
+ return with_retries { do_publish?(subject, envelope) }
66
66
  end
67
67
 
68
68
  repo = OutboxRepository.new(klass)
@@ -77,7 +77,7 @@ module JetstreamBridge
77
77
 
78
78
  repo.persist_pre(record, subject, envelope)
79
79
 
80
- ok = with_retries { do_publish(subject, envelope) }
80
+ ok = with_retries { do_publish?(subject, envelope) }
81
81
  ok ? repo.persist_success(record) : repo.persist_failure(record, 'Publish returned false')
82
82
  ok
83
83
  rescue StandardError => e
@@ -4,5 +4,5 @@
4
4
  #
5
5
  # Version constant for the gem.
6
6
  module JetstreamBridge
7
- VERSION = '2.0.0'
7
+ VERSION = '2.2.0'
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jetstream_bridge
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Attara