nats-pure 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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