jetstream_bridge 2.4.0 → 2.5.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: 3609786f5181b8d4af9197952b7ff8d2878123c1c248bd6345454b99b43e0803
4
- data.tar.gz: e0f521d0962785ca1c8e3cb271c7961d8aa9c55ac58b441ae04c39d150de4aae
3
+ metadata.gz: 0defcef0e3c705c2a62268116e62d93076b4f5ba405de6fcfc1b3ea2b0122101
4
+ data.tar.gz: 3eaede87e4904d755a8370042f4405011a5dd3996704a21f8f4329676a81dffd
5
5
  SHA512:
6
- metadata.gz: cd6d5b0380a0ff25c60f093c7d64b80bd5733666d7621f07462817112b5fdeb4b6f63396e1be09ce7f5ad4cb9368d185ecd4a8c5674fae3e677def970fd2f1ed
7
- data.tar.gz: 7d595ae9091f19e2c35168ed3422a2b5cf424d183f5d4c3942c97b423b8a9f9c70cd560e08c26979ec818929975dc590143e7fa30f5706068d594e4c59ec2598
6
+ metadata.gz: 58b9210d52525cc66332c68ba3c22f03b320941178635a99e489aaf62ad74a90e2bccaaab0e9851aa23397247bb0bdf4c66e2da86924249823039af66a428efd
7
+ data.tar.gz: d96886372c8067e1147b879f794a772df9052d12966ce20e585ee19d950876c952f76af88400b46da186a35583314e09e0b3f84e9598843696e38a8a3e96a3d5
@@ -37,8 +37,7 @@ module JetstreamBridge
37
37
  def log_all_blocked(name, blocked)
38
38
  if blocked.any?
39
39
  Logging.warn(
40
- "Stream #{name}: all missing subjects belong to other streams; unchanged. " \
41
- "blocked=#{blocked.inspect}",
40
+ "Stream #{name}: all missing subjects belong to other streams; unchanged. blocked=#{blocked.inspect}",
42
41
  tag: 'JetstreamBridge::Stream'
43
42
  )
44
43
  else
@@ -54,8 +53,7 @@ module JetstreamBridge
54
53
 
55
54
  def log_not_created(name, blocked)
56
55
  Logging.warn(
57
- "Not creating stream #{name}: all desired subjects belong to other streams. " \
58
- "blocked=#{blocked.inspect}",
56
+ "Not creating stream #{name}: all desired subjects belong to other streams. blocked=#{blocked.inspect}",
59
57
  tag: 'JetstreamBridge::Stream'
60
58
  )
61
59
  end
@@ -70,10 +68,24 @@ module JetstreamBridge
70
68
  msg += " (skipped overlapped=#{blocked.inspect})" if blocked.any?
71
69
  Logging.info(msg, tag: 'JetstreamBridge::Stream')
72
70
  end
71
+
72
+ def log_config_updated(name, storage:)
73
+ Logging.info(
74
+ "Updated stream #{name} config; storage=#{storage.inspect}",
75
+ tag: 'JetstreamBridge::Stream'
76
+ )
77
+ end
78
+
79
+ def log_retention_mismatch(name, have:, want:)
80
+ Logging.warn(
81
+ "Stream #{name} retention mismatch (have=#{have.inspect}, want=#{want.inspect}). " \
82
+ "Retention is immutable; skipping retention change.",
83
+ tag: 'JetstreamBridge::Stream'
84
+ )
85
+ end
73
86
  end
74
87
 
75
- # Ensures a stream exists and updates only uncovered subjects, using work-queue semantics:
76
- # Persist with zero consumers; delete after the first ack (retention: 'workqueue').
88
+ # Ensures a stream exists and updates only uncovered subjects, using work-queue semantics.
77
89
  class Stream
78
90
  RETENTION = 'workqueue'
79
91
  STORAGE = 'file'
@@ -89,17 +101,12 @@ module JetstreamBridge
89
101
  info ? ensure_update(jts, name, info, desired) : ensure_create(jts, name, desired)
90
102
  rescue NATS::JetStream::Error => e
91
103
  if StreamSupport.overlap_error?(e) && (attempts += 1) <= 1
92
- Logging.warn(
93
- "Overlap race while ensuring #{name}; retrying once...",
94
- tag: 'JetstreamBridge::Stream'
95
- )
104
+ Logging.warn("Overlap race while ensuring #{name}; retrying once...", tag: 'JetstreamBridge::Stream')
96
105
  sleep(0.05)
97
106
  retry
98
107
  elsif StreamSupport.overlap_error?(e)
99
- Logging.warn(
100
- "Overlap persists ensuring #{name}; leaving unchanged. err=#{e.message.inspect}",
101
- tag: 'JetstreamBridge::Stream'
102
- )
108
+ Logging.warn("Overlap persists ensuring #{name}; leaving unchanged. err=#{e.message.inspect}",
109
+ tag: 'JetstreamBridge::Stream')
103
110
  nil
104
111
  else
105
112
  raise
@@ -109,18 +116,27 @@ module JetstreamBridge
109
116
 
110
117
  private
111
118
 
112
- # ---- keep ensure_update small (<=20 lines, lower ABC) ----
113
119
  def ensure_update(jts, name, info, desired_subjects)
114
120
  existing = StreamSupport.normalize_subjects(info.config.subjects || [])
115
121
  to_add = StreamSupport.missing_subjects(existing, desired_subjects)
122
+ add_subjects(jts, name, existing, to_add) if to_add.any?
116
123
 
117
- return add_subjects(jts, name, existing, to_add) if to_add.any?
124
+ # Retention is immutable; warn if different and do not include on update.
125
+ 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
118
129
 
119
- if config_needs_update?(info)
120
- apply_update(jts, name, existing)
121
- return log_config_updated(name)
130
+ # Storage can be updated; do it without passing retention.
131
+ have_storage = info.config.storage.to_s.downcase
132
+ if have_storage != STORAGE
133
+ apply_update(jts, name, existing, storage: STORAGE)
134
+ StreamSupport.log_config_updated(name, storage: STORAGE)
135
+ return
122
136
  end
123
137
 
138
+ return if to_add.any?
139
+
124
140
  StreamSupport.log_already_covered(name)
125
141
  end
126
142
 
@@ -129,36 +145,18 @@ module JetstreamBridge
129
145
  allowed, blocked = OverlapGuard.partition_allowed(jts, name, to_add)
130
146
  return StreamSupport.log_all_blocked(name, blocked) if allowed.empty?
131
147
 
132
- target = merge_subjects(existing, allowed)
148
+ target = (existing + allowed).uniq
133
149
  OverlapGuard.check!(jts, name, target)
150
+ # Do not pass retention on update to avoid 10052.
134
151
  apply_update(jts, name, target)
135
152
  StreamSupport.log_updated(name, allowed, blocked)
136
153
  end
137
154
 
138
- def merge_subjects(existing, allowed)
139
- (existing + allowed).uniq
140
- end
141
-
142
- def config_needs_update?(info)
143
- info.config.retention.to_s.downcase != RETENTION ||
144
- info.config.storage.to_s.downcase != STORAGE
145
- end
146
-
147
- def apply_update(jts, name, subjects)
148
- jts.update_stream(
149
- name: name,
150
- subjects: subjects,
151
- retention: RETENTION,
152
- storage: STORAGE
153
- )
154
- end
155
-
156
- def log_config_updated(name)
157
- Logging.info(
158
- "Updated stream #{name} config; retention=#{RETENTION.inspect} " \
159
- "storage=#{STORAGE.inspect}",
160
- tag: 'JetstreamBridge::Stream'
161
- )
155
+ # Only include mutable fields on update (subjects, storage). Never retention.
156
+ def apply_update(jts, name, subjects, storage: nil)
157
+ params = { name: name, subjects: subjects }
158
+ params[:storage] = storage if storage
159
+ jts.update_stream(**params)
162
160
  end
163
161
 
164
162
  def ensure_create(jts, name, desired_subjects)
@@ -4,5 +4,5 @@
4
4
  #
5
5
  # Version constant for the gem.
6
6
  module JetstreamBridge
7
- VERSION = '2.4.0'
7
+ VERSION = '2.5.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.4.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Attara