nats-pure 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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +10 -3
  4. data/lib/nats/client.rb +7 -3
  5. data/lib/nats/io/client.rb +303 -280
  6. data/lib/nats/io/errors.rb +2 -0
  7. data/lib/nats/io/jetstream/api.rb +53 -50
  8. data/lib/nats/io/jetstream/errors.rb +30 -14
  9. data/lib/nats/io/jetstream/js/config.rb +9 -3
  10. data/lib/nats/io/jetstream/js/header.rb +15 -9
  11. data/lib/nats/io/jetstream/js/status.rb +11 -5
  12. data/lib/nats/io/jetstream/js/sub.rb +4 -2
  13. data/lib/nats/io/jetstream/js.rb +10 -8
  14. data/lib/nats/io/jetstream/manager.rb +103 -101
  15. data/lib/nats/io/jetstream/msg/ack.rb +15 -9
  16. data/lib/nats/io/jetstream/msg/ack_methods.rb +24 -22
  17. data/lib/nats/io/jetstream/msg/metadata.rb +9 -7
  18. data/lib/nats/io/jetstream/msg.rb +11 -4
  19. data/lib/nats/io/jetstream/pull_subscription.rb +21 -10
  20. data/lib/nats/io/jetstream/push_subscription.rb +3 -1
  21. data/lib/nats/io/jetstream.rb +102 -106
  22. data/lib/nats/io/kv/api.rb +7 -3
  23. data/lib/nats/io/kv/bucket_status.rb +7 -5
  24. data/lib/nats/io/kv/errors.rb +25 -2
  25. data/lib/nats/io/kv/manager.rb +19 -10
  26. data/lib/nats/io/kv.rb +359 -22
  27. data/lib/nats/io/msg.rb +19 -19
  28. data/lib/nats/io/parser.rb +23 -23
  29. data/lib/nats/io/rails.rb +2 -0
  30. data/lib/nats/io/subscription.rb +25 -22
  31. data/lib/nats/io/version.rb +4 -2
  32. data/lib/nats/io/websocket.rb +10 -8
  33. data/lib/nats/nuid.rb +33 -22
  34. data/lib/nats/service/callbacks.rb +22 -0
  35. data/lib/nats/service/endpoint.rb +155 -0
  36. data/lib/nats/service/errors.rb +44 -0
  37. data/lib/nats/service/group.rb +37 -0
  38. data/lib/nats/service/monitoring.rb +108 -0
  39. data/lib/nats/service/stats.rb +52 -0
  40. data/lib/nats/service/status.rb +66 -0
  41. data/lib/nats/service/validator.rb +31 -0
  42. data/lib/nats/service.rb +121 -0
  43. data/lib/nats/utils/list.rb +26 -0
  44. data/lib/nats-pure.rb +5 -0
  45. data/lib/nats.rb +10 -6
  46. metadata +176 -5
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2021 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
@@ -11,17 +13,14 @@
11
13
  # See the License for the specific language governing permissions and
12
14
  # limitations under the License.
13
15
  #
14
- require_relative 'msg'
15
- require_relative 'client'
16
- require_relative 'errors'
17
- require_relative 'kv'
18
- require_relative 'jetstream/api'
19
- require_relative 'jetstream/errors'
20
- require_relative 'jetstream/js'
21
- require_relative 'jetstream/manager'
22
- require_relative 'jetstream/msg'
23
- require_relative 'jetstream/pull_subscription'
24
- require_relative 'jetstream/push_subscription'
16
+ require_relative "kv"
17
+ require_relative "jetstream/api"
18
+ require_relative "jetstream/errors"
19
+ require_relative "jetstream/js"
20
+ require_relative "jetstream/manager"
21
+ require_relative "jetstream/msg"
22
+ require_relative "jetstream/pull_subscription"
23
+ require_relative "jetstream/push_subscription"
25
24
 
26
25
  module NATS
27
26
  # JetStream returns a context with a similar API as the NATS::Client
@@ -33,6 +32,8 @@ module NATS
33
32
  # js = nc.jetstream()
34
33
  #
35
34
  class JetStream
35
+ attr_reader :opts, :prefix, :nc
36
+
36
37
  # Create a new JetStream context for a NATS connection.
37
38
  #
38
39
  # @param conn [NATS::Client]
@@ -40,15 +41,15 @@ module NATS
40
41
  # @option params [String] :prefix JetStream API prefix to use for the requests.
41
42
  # @option params [String] :domain JetStream Domain to use for the requests.
42
43
  # @option params [Float] :timeout Default timeout to use for JS requests.
43
- def initialize(conn, params={})
44
+ def initialize(conn, params = {})
44
45
  @nc = conn
45
46
  @prefix = if params[:prefix]
46
- params[:prefix]
47
- elsif params[:domain]
48
- "$JS.#{params[:domain]}.API"
49
- else
50
- JS::DefaultAPIPrefix
51
- end
47
+ params[:prefix]
48
+ elsif params[:domain]
49
+ "$JS.#{params[:domain]}.API"
50
+ else
51
+ JS::DefaultAPIPrefix
52
+ end
52
53
  @opts = params
53
54
  @opts[:timeout] ||= 5 # seconds
54
55
  params[:prefix] = @prefix
@@ -80,7 +81,7 @@ module NATS
80
81
  # @option params [String] :stream Expected Stream to which the message is being published.
81
82
  # @raise [NATS::Timeout] When it takes too long to receive an ack response.
82
83
  # @return [PubAck] The pub ack response.
83
- def publish(subject, payload="", **params)
84
+ def publish(subject, payload = "", **params)
84
85
  params[:timeout] ||= @opts[:timeout]
85
86
  if params[:stream]
86
87
  params[:header] ||= {}
@@ -89,8 +90,8 @@ module NATS
89
90
 
90
91
  # Send message with headers.
91
92
  msg = NATS::Msg.new(subject: subject,
92
- data: payload,
93
- header: params[:header])
93
+ data: payload,
94
+ header: params[:header])
94
95
 
95
96
  begin
96
97
  resp = @nc.request_msg(msg, **params)
@@ -113,53 +114,49 @@ module NATS
113
114
  # @option params [String] :durable Consumer durable name from where the messages will be fetched.
114
115
  # @option params [Hash] :config Configuration for the consumer.
115
116
  # @return [NATS::JetStream::PushSubscription]
116
- def subscribe(subject, params={}, &cb)
117
+ def subscribe(subject, params = {}, &cb)
117
118
  params[:consumer] ||= params[:durable]
118
119
  params[:consumer] ||= params[:name]
119
- multi_filter = case
120
- when (subject.is_a?(Array) and subject.size == 1)
121
- subject = subject.first
122
- false
123
- when (subject.is_a?(Array) and subject.size > 1)
124
- true
125
- end
120
+ multi_filter = if subject.is_a?(Array) && (subject.size == 1)
121
+ subject = subject.first
122
+ false
123
+ elsif subject.is_a?(Array) && (subject.size > 1)
124
+ true
125
+ end
126
126
 
127
- #
128
127
  stream = if params[:stream].nil?
129
- if multi_filter
130
- # Use the first subject to try to find the stream.
131
- streams = subject.map do |s|
132
- begin
133
- find_stream_name_by_subject(s)
134
- rescue NATS::JetStream::Error::NotFound
135
- raise NATS::JetStream::Error.new("nats: could not find stream matching filter subject '#{s}'")
136
- end
137
- end
128
+ if multi_filter
129
+ # Use the first subject to try to find the stream.
130
+ streams = subject.map do |s|
131
+ find_stream_name_by_subject(s)
132
+ rescue NATS::JetStream::Error::NotFound
133
+ raise NATS::JetStream::Error.new("nats: could not find stream matching filter subject '#{s}'")
134
+ end
138
135
 
139
- # Ensure that the filter subjects are not ambiguous.
140
- streams.uniq!
141
- if streams.count > 1
142
- raise NATS::JetStream::Error.new("nats: multiple streams matched filter subjects: #{streams}")
143
- end
136
+ # Ensure that the filter subjects are not ambiguous.
137
+ streams.uniq!
138
+ if streams.count > 1
139
+ raise NATS::JetStream::Error.new("nats: multiple streams matched filter subjects: #{streams}")
140
+ end
144
141
 
145
- streams.first
146
- else
147
- find_stream_name_by_subject(subject)
148
- end
149
- else
150
- params[:stream]
151
- end
142
+ streams.first
143
+ else
144
+ find_stream_name_by_subject(subject)
145
+ end
146
+ else
147
+ params[:stream]
148
+ end
152
149
 
153
150
  queue = params[:queue]
154
151
  durable = params[:durable]
155
- flow_control = params[:flow_control]
152
+ params[:flow_control]
156
153
  manual_ack = params[:manual_ack]
157
154
  idle_heartbeat = params[:idle_heartbeat]
158
155
  flow_control = params[:flow_control]
159
156
  config = params[:config]
160
157
 
161
158
  if queue
162
- if durable and durable != queue
159
+ if durable && (durable != queue)
163
160
  raise NATS::JetStream::Error.new("nats: cannot create queue subscription '#{queue}' to consumer '#{durable}'")
164
161
  else
165
162
  durable = queue
@@ -170,7 +167,7 @@ module NATS
170
167
  consumer_found = false
171
168
  should_create = false
172
169
 
173
- if not durable
170
+ if !durable
174
171
  should_create = true
175
172
  else
176
173
  begin
@@ -185,18 +182,16 @@ module NATS
185
182
  end
186
183
 
187
184
  if consumer_found
188
- if not config.deliver_group
185
+ if !config.deliver_group
189
186
  if queue
190
187
  raise NATS::JetStream::Error.new("nats: cannot create a queue subscription for a consumer without a deliver group")
191
188
  elsif cinfo.push_bound
192
189
  raise NATS::JetStream::Error.new("nats: consumer is already bound to a subscription")
193
190
  end
194
- else
195
- if not queue
196
- raise NATS::JetStream::Error.new("nats: cannot create a subscription for a consumer with a deliver group #{config.deliver_group}")
197
- elsif queue != config.deliver_group
198
- raise NATS::JetStream::Error.new("nats: cannot create a queue subscription #{queue} for a consumer with a deliver group #{config.deliver_group}")
199
- end
191
+ elsif !queue
192
+ raise NATS::JetStream::Error.new("nats: cannot create a subscription for a consumer with a deliver group #{config.deliver_group}")
193
+ elsif queue != config.deliver_group
194
+ raise NATS::JetStream::Error.new("nats: cannot create a queue subscription #{queue} for a consumer with a deliver group #{config.deliver_group}")
200
195
  end
201
196
  elsif should_create
202
197
  # Auto-create consumer if none found.
@@ -209,8 +204,8 @@ module NATS
209
204
  raise NATS::JetStream::Error.new("nats: invalid ConsumerConfig")
210
205
  end
211
206
 
212
- config.durable_name = durable if not config.durable_name
213
- config.deliver_group = queue if not config.deliver_group
207
+ config.durable_name = durable if !config.durable_name
208
+ config.deliver_group = queue if !config.deliver_group
214
209
 
215
210
  # Create inbox for push consumer.
216
211
  deliver = @nc.new_inbox
@@ -225,9 +220,8 @@ module NATS
225
220
 
226
221
  # Heartbeats / FlowControl
227
222
  config.flow_control = flow_control
228
- if idle_heartbeat or config.idle_heartbeat
223
+ if idle_heartbeat || config.idle_heartbeat
229
224
  idle_heartbeat = config.idle_heartbeat if config.idle_heartbeat
230
- idle_heartbeat = idle_heartbeat * ::NATS::NANOSECONDS
231
225
  config.idle_heartbeat = idle_heartbeat
232
226
  end
233
227
 
@@ -237,11 +231,16 @@ module NATS
237
231
  end
238
232
 
239
233
  # Enable auto acking for async callbacks unless disabled.
240
- if cb and not manual_ack
234
+ # In case ack policy is none then we also do not require to ack.
235
+ if cb && !manual_ack && (config.ack_policy != "none")
241
236
  ocb = cb
242
237
  new_cb = proc do |msg|
243
238
  ocb.call(msg)
244
- msg.ack rescue JetStream::Error::MsgAlreadyAckd
239
+ begin
240
+ msg.ack
241
+ rescue
242
+ JetStream::Error::MsgAlreadyAckd
243
+ end
245
244
  end
246
245
  cb = new_cb
247
246
  end
@@ -250,7 +249,7 @@ module NATS
250
249
  sub.jsi = JS::Sub.new(
251
250
  js: self,
252
251
  stream: stream,
253
- consumer: consumer,
252
+ consumer: consumer
254
253
  )
255
254
  sub
256
255
  end
@@ -265,57 +264,54 @@ module NATS
265
264
  # @option params [String] :name Name of the Consumer to which the PullSubscription will be bound.
266
265
  # @option params [Hash] :config Configuration for the consumer.
267
266
  # @return [NATS::JetStream::PullSubscription]
268
- def pull_subscribe(subject, durable, params={})
269
- if (!durable or durable.empty?) && !(params[:consumer] or params[:name])
267
+ def pull_subscribe(subject, durable, params = {})
268
+ if (!durable || durable.empty?) && !(params[:consumer] || params[:name])
270
269
  raise JetStream::Error::InvalidDurableName.new("nats: invalid durable name")
271
270
  end
272
- multi_filter = case
273
- when (subject.is_a?(Array) and subject.size == 1)
274
- subject = subject.first
275
- false
276
- when (subject.is_a?(Array) and subject.size > 1)
277
- true
278
- end
271
+ multi_filter = if subject.is_a?(Array) && (subject.size == 1)
272
+ subject = subject.first
273
+ false
274
+ elsif subject.is_a?(Array) && (subject.size > 1)
275
+ true
276
+ end
279
277
 
280
278
  params[:consumer] ||= durable
281
279
  params[:consumer] ||= params[:name]
282
280
  stream = if params[:stream].nil?
283
- if multi_filter
284
- # Use the first subject to try to find the stream.
285
- streams = subject.map do |s|
286
- begin
287
- find_stream_name_by_subject(s)
288
- rescue NATS::JetStream::Error::NotFound
289
- raise NATS::JetStream::Error.new("nats: could not find stream matching filter subject '#{s}'")
290
- end
291
- end
281
+ if multi_filter
282
+ # Use the first subject to try to find the stream.
283
+ streams = subject.map do |s|
284
+ find_stream_name_by_subject(s)
285
+ rescue NATS::JetStream::Error::NotFound
286
+ raise NATS::JetStream::Error.new("nats: could not find stream matching filter subject '#{s}'")
287
+ end
292
288
 
293
- # Ensure that the filter subjects are not ambiguous.
294
- streams.uniq!
295
- if streams.count > 1
296
- raise NATS::JetStream::Error.new("nats: multiple streams matched filter subjects: #{streams}")
297
- end
289
+ # Ensure that the filter subjects are not ambiguous.
290
+ streams.uniq!
291
+ if streams.count > 1
292
+ raise NATS::JetStream::Error.new("nats: multiple streams matched filter subjects: #{streams}")
293
+ end
298
294
 
299
- streams.first
300
- else
301
- find_stream_name_by_subject(subject)
302
- end
303
- else
304
- params[:stream]
305
- end
295
+ streams.first
296
+ else
297
+ find_stream_name_by_subject(subject)
298
+ end
299
+ else
300
+ params[:stream]
301
+ end
306
302
  begin
307
303
  consumer_info(stream, params[:consumer])
308
304
  rescue NATS::JetStream::Error::NotFound => e
309
305
  # If attempting to bind, then this is a hard error.
310
- raise e if params[:stream] and !multi_filter
306
+ raise e if params[:stream] && !multi_filter
311
307
 
312
- config = if not params[:config]
313
- JetStream::API::ConsumerConfig.new
314
- elsif params[:config].is_a?(JetStream::API::ConsumerConfig)
315
- params[:config]
316
- else
317
- JetStream::API::ConsumerConfig.new(params[:config])
318
- end
308
+ config = if !(params[:config])
309
+ JetStream::API::ConsumerConfig.new
310
+ elsif params[:config].is_a?(JetStream::API::ConsumerConfig)
311
+ params[:config]
312
+ else
313
+ JetStream::API::ConsumerConfig.new(params[:config])
314
+ end
319
315
  config[:durable_name] = durable
320
316
  config[:ack_policy] ||= JS::Config::AckExplicit
321
317
  if multi_filter
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2021 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
@@ -27,11 +29,13 @@ module NATS
27
29
  :placement,
28
30
  :republish,
29
31
  :direct,
30
- keyword_init: true) do
31
- def initialize(opts={})
32
+ :validate_keys,
33
+ keyword_init: true
34
+ ) do
35
+ def initialize(opts = {})
32
36
  rem = opts.keys - members
33
37
  opts.delete_if { |k| rem.include?(k) }
34
- super(opts)
38
+ super
35
39
  end
36
40
  end
37
41
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2021 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
@@ -15,23 +17,23 @@
15
17
  module NATS
16
18
  class KeyValue
17
19
  class BucketStatus
18
- attr_reader :bucket
20
+ attr_reader :bucket, :stream_info
19
21
 
20
22
  def initialize(info, bucket)
21
- @nfo = info
23
+ @stream_info = info
22
24
  @bucket = bucket
23
25
  end
24
26
 
25
27
  def values
26
- @nfo.state.messages
28
+ @stream_info.state.messages
27
29
  end
28
30
 
29
31
  def history
30
- @nfo.config.max_msgs_per_subject
32
+ @stream_info.config.max_msgs_per_subject
31
33
  end
32
34
 
33
35
  def ttl
34
- @nfo.config.max_age / ::NATS::NANOSECONDS
36
+ @stream_info.config.max_age / ::NATS::NANOSECONDS
35
37
  end
36
38
  end
37
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2021 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
@@ -12,7 +14,7 @@
12
14
  # limitations under the License.
13
15
  #
14
16
 
15
- require_relative '../errors'
17
+ require_relative "../errors"
16
18
 
17
19
  module NATS
18
20
  class KeyValue
@@ -21,7 +23,7 @@ module NATS
21
23
  # When a key is not found.
22
24
  class KeyNotFoundError < Error
23
25
  attr_reader :entry, :op
24
- def initialize(params={})
26
+ def initialize(params = {})
25
27
  @entry = params[:entry]
26
28
  @op = params[:op]
27
29
  @message = params[:message]
@@ -52,9 +54,30 @@ module NATS
52
54
  def initialize(msg)
53
55
  @msg = msg
54
56
  end
57
+
55
58
  def to_s
56
59
  "nats: #{@msg}"
57
60
  end
58
61
  end
62
+
63
+ # When there are no keys.
64
+ class NoKeysFoundError < Error
65
+ def to_s
66
+ "nats: no keys found"
67
+ end
68
+ end
69
+
70
+ # When history is too large.
71
+ class KeyHistoryTooLargeError < Error
72
+ def to_s
73
+ "nats: history limited to a max of 64"
74
+ end
75
+ end
76
+
77
+ class InvalidKeyError < Error
78
+ def to_s
79
+ "nats: invalid key"
80
+ end
81
+ end
59
82
  end
60
83
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2021 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
@@ -15,7 +17,7 @@
15
17
  module NATS
16
18
  class KeyValue
17
19
  module Manager
18
- def key_value(bucket)
20
+ def key_value(bucket, params = {})
19
21
  stream = "KV_#{bucket}"
20
22
  begin
21
23
  si = stream_info(stream)
@@ -31,16 +33,18 @@ module NATS
31
33
  stream: stream,
32
34
  pre: "$KV.#{bucket}.",
33
35
  js: self,
34
- direct: si.config.allow_direct
36
+ direct: si.config.allow_direct,
37
+ validate_keys: params[:validate_keys]
35
38
  )
36
39
  end
37
40
 
38
41
  def create_key_value(config)
39
- config = if not config.is_a?(KeyValue::API::KeyValueConfig)
40
- KeyValue::API::KeyValueConfig.new(config)
41
- else
42
- config
43
- end
42
+ config = if !config.is_a?(KeyValue::API::KeyValueConfig)
43
+ config = {bucket: config} if config.is_a?(String)
44
+ KeyValue::API::KeyValueConfig.new(config)
45
+ else
46
+ config
47
+ end
44
48
  config.history ||= 1
45
49
  config.replicas ||= 1
46
50
  duplicate_window = 2 * 60 # 2 minutes
@@ -51,6 +55,10 @@ module NATS
51
55
  config.ttl = config.ttl * ::NATS::NANOSECONDS
52
56
  end
53
57
 
58
+ if config.history > 64
59
+ raise NATS::KeyValue::KeyHistoryTooLargeError
60
+ end
61
+
54
62
  stream = JetStream::API::StreamConfig.new(
55
63
  name: "KV_#{config.bucket}",
56
64
  description: config.description,
@@ -68,8 +76,8 @@ module NATS
68
76
  max_msgs_per_subject: config.history,
69
77
  num_replicas: config.replicas,
70
78
  storage: config.storage,
71
- republish: config.republish,
72
- )
79
+ republish: config.republish
80
+ )
73
81
 
74
82
  si = add_stream(stream)
75
83
  KeyValue.new(
@@ -77,7 +85,8 @@ module NATS
77
85
  stream: stream.name,
78
86
  pre: "$KV.#{config.bucket}.",
79
87
  js: self,
80
- direct: si.config.allow_direct
88
+ direct: si.config.allow_direct,
89
+ validate_keys: config.validate_keys
81
90
  )
82
91
  end
83
92