nats-pure 2.2.0 → 2.3.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +201 -0
  3. data/README.md +251 -0
  4. data/lib/nats/io/client.rb +226 -151
  5. data/lib/nats/io/errors.rb +6 -0
  6. data/lib/nats/io/jetstream/api.rb +305 -0
  7. data/lib/nats/io/jetstream/errors.rb +104 -0
  8. data/lib/nats/io/jetstream/js/config.rb +26 -0
  9. data/lib/nats/io/jetstream/js/header.rb +31 -0
  10. data/lib/nats/io/jetstream/js/status.rb +27 -0
  11. data/lib/nats/io/jetstream/js/sub.rb +30 -0
  12. data/lib/nats/io/jetstream/js.rb +93 -0
  13. data/lib/nats/io/jetstream/manager.rb +284 -0
  14. data/lib/nats/io/jetstream/msg/ack.rb +57 -0
  15. data/lib/nats/io/jetstream/msg/ack_methods.rb +111 -0
  16. data/lib/nats/io/jetstream/msg/metadata.rb +37 -0
  17. data/lib/nats/io/jetstream/msg.rb +26 -0
  18. data/lib/nats/io/jetstream/pull_subscription.rb +260 -0
  19. data/lib/nats/io/jetstream/push_subscription.rb +42 -0
  20. data/lib/nats/io/jetstream.rb +269 -0
  21. data/lib/nats/io/kv/api.rb +39 -0
  22. data/lib/nats/io/kv/bucket_status.rb +38 -0
  23. data/lib/nats/io/kv/errors.rb +60 -0
  24. data/lib/nats/io/kv/manager.rb +89 -0
  25. data/lib/nats/io/kv.rb +5 -157
  26. data/lib/nats/io/msg.rb +4 -2
  27. data/lib/nats/io/rails.rb +29 -0
  28. data/lib/nats/io/subscription.rb +70 -5
  29. data/lib/nats/io/version.rb +1 -1
  30. data/lib/nats/io/websocket.rb +75 -0
  31. data/sig/nats/io/client.rbs +304 -0
  32. data/sig/nats/io/errors.rbs +54 -0
  33. data/sig/nats/io/jetstream/api.rbs +35 -0
  34. data/sig/nats/io/jetstream/errors.rbs +54 -0
  35. data/sig/nats/io/jetstream/js/config.rbs +11 -0
  36. data/sig/nats/io/jetstream/js/header.rbs +17 -0
  37. data/sig/nats/io/jetstream/js/status.rbs +13 -0
  38. data/sig/nats/io/jetstream/js/sub.rbs +14 -0
  39. data/sig/nats/io/jetstream/js.rbs +27 -0
  40. data/sig/nats/io/jetstream/manager.rbs +33 -0
  41. data/sig/nats/io/jetstream/msg/ack.rbs +35 -0
  42. data/sig/nats/io/jetstream/msg/ack_methods.rbs +25 -0
  43. data/sig/nats/io/jetstream/msg/metadata.rbs +15 -0
  44. data/sig/nats/io/jetstream/msg.rbs +6 -0
  45. data/sig/nats/io/jetstream/pull_subscription.rbs +14 -0
  46. data/sig/nats/io/jetstream/push_subscription.rbs +7 -0
  47. data/sig/nats/io/jetstream.rbs +15 -0
  48. data/sig/nats/io/kv/api.rbs +8 -0
  49. data/sig/nats/io/kv/bucket_status.rbs +17 -0
  50. data/sig/nats/io/kv/errors.rbs +30 -0
  51. data/sig/nats/io/kv/manager.rbs +11 -0
  52. data/sig/nats/io/kv.rbs +39 -0
  53. data/sig/nats/io/msg.rbs +14 -0
  54. data/sig/nats/io/parser.rbs +32 -0
  55. data/sig/nats/io/subscription.rbs +33 -0
  56. data/sig/nats/io/version.rbs +9 -0
  57. data/sig/nats/nuid.rbs +32 -0
  58. metadata +68 -5
  59. data/lib/nats/io/js.rb +0 -1434
data/lib/nats/io/js.rb DELETED
@@ -1,1434 +0,0 @@
1
- # Copyright 2021 The NATS Authors
2
- # Licensed under the Apache License, Version 2.0 (the "License");
3
- # you may not use this file except in compliance with the License.
4
- # You may obtain a copy of the License at
5
- #
6
- # http://www.apache.org/licenses/LICENSE-2.0
7
- #
8
- # Unless required by applicable law or agreed to in writing, software
9
- # distributed under the License is distributed on an "AS IS" BASIS,
10
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
- # See the License for the specific language governing permissions and
12
- # limitations under the License.
13
- #
14
- require_relative 'msg'
15
- require_relative 'client'
16
- require_relative 'errors'
17
- require_relative 'kv'
18
- require 'time'
19
- require 'base64'
20
-
21
- module NATS
22
- # JetStream returns a context with a similar API as the NATS::Client
23
- # but with enhanced functions to persist and consume messages from
24
- # the NATS JetStream engine.
25
- #
26
- # @example
27
- # nc = NATS.connect("demo.nats.io")
28
- # js = nc.jetstream()
29
- #
30
- class JetStream
31
- # Create a new JetStream context for a NATS connection.
32
- #
33
- # @param conn [NATS::Client]
34
- # @param params [Hash] Options to customize JetStream context.
35
- # @option params [String] :prefix JetStream API prefix to use for the requests.
36
- # @option params [String] :domain JetStream Domain to use for the requests.
37
- # @option params [Float] :timeout Default timeout to use for JS requests.
38
- def initialize(conn, params={})
39
- @nc = conn
40
- @prefix = if params[:prefix]
41
- params[:prefix]
42
- elsif params[:domain]
43
- "$JS.#{params[:domain]}.API"
44
- else
45
- JS::DefaultAPIPrefix
46
- end
47
- @opts = params
48
- @opts[:timeout] ||= 5 # seconds
49
- params[:prefix] = @prefix
50
-
51
- # Include JetStream::Manager
52
- extend Manager
53
- extend KeyValue::Manager
54
- end
55
-
56
- # PubAck is the API response from a successfully published message.
57
- #
58
- # @!attribute [stream] stream
59
- # @return [String] Name of the stream that processed the published message.
60
- # @!attribute [seq] seq
61
- # @return [Fixnum] Sequence of the message in the stream.
62
- # @!attribute [duplicate] duplicate
63
- # @return [Boolean] Indicates whether the published message is a duplicate.
64
- # @!attribute [domain] domain
65
- # @return [String] JetStream Domain that processed the ack response.
66
- PubAck = Struct.new(:stream, :seq, :duplicate, :domain, keyword_init: true)
67
-
68
- # publish produces a message for JetStream.
69
- #
70
- # @param subject [String] The subject from a stream where the message will be sent.
71
- # @param payload [String] The payload of the message.
72
- # @param params [Hash] Options to customize the publish message request.
73
- # @option params [Float] :timeout Time to wait for an PubAck response or an error.
74
- # @option params [Hash] :header NATS Headers to use for the message.
75
- # @option params [String] :stream Expected Stream to which the message is being published.
76
- # @raise [NATS::Timeout] When it takes too long to receive an ack response.
77
- # @return [PubAck] The pub ack response.
78
- def publish(subject, payload="", **params)
79
- params[:timeout] ||= @opts[:timeout]
80
- if params[:stream]
81
- params[:header] ||= {}
82
- params[:header][JS::Header::ExpectedStream] = params[:stream]
83
- end
84
-
85
- # Send message with headers.
86
- msg = NATS::Msg.new(subject: subject,
87
- data: payload,
88
- header: params[:header])
89
-
90
- begin
91
- resp = @nc.request_msg(msg, **params)
92
- result = JSON.parse(resp.data, symbolize_names: true)
93
- rescue ::NATS::IO::NoRespondersError
94
- raise JetStream::Error::NoStreamResponse.new("nats: no response from stream")
95
- end
96
- raise JS.from_error(result[:error]) if result[:error]
97
-
98
- PubAck.new(result)
99
- end
100
-
101
- # subscribe binds or creates a push subscription to a JetStream pull consumer.
102
- #
103
- # @param subject [String] Subject from which the messages will be fetched.
104
- # @param params [Hash] Options to customize the PushSubscription.
105
- # @option params [String] :stream Name of the Stream to which the consumer belongs.
106
- # @option params [String] :consumer Name of the Consumer to which the PushSubscription will be bound.
107
- # @option params [String] :durable Consumer durable name from where the messages will be fetched.
108
- # @option params [Hash] :config Configuration for the consumer.
109
- # @return [NATS::JetStream::PushSubscription]
110
- def subscribe(subject, params={}, &cb)
111
- params[:consumer] ||= params[:durable]
112
- stream = params[:stream].nil? ? find_stream_name_by_subject(subject) : params[:stream]
113
-
114
- queue = params[:queue]
115
- durable = params[:durable]
116
- flow_control = params[:flow_control]
117
- manual_ack = params[:manual_ack]
118
- idle_heartbeat = params[:idle_heartbeat]
119
- flow_control = params[:flow_control]
120
- config = params[:config]
121
-
122
- if queue
123
- if durable and durable != queue
124
- raise NATS::JetStream::Error.new("nats: cannot create queue subscription '#{queue}' to consumer '#{durable}'")
125
- else
126
- durable = queue
127
- end
128
- end
129
-
130
- cinfo = nil
131
- consumer_found = false
132
- should_create = false
133
-
134
- if not durable
135
- should_create = true
136
- else
137
- begin
138
- cinfo = consumer_info(stream, durable)
139
- config = cinfo.config
140
- consumer_found = true
141
- consumer = durable
142
- rescue NATS::JetStream::Error::NotFound
143
- should_create = true
144
- consumer_found = false
145
- end
146
- end
147
-
148
- if consumer_found
149
- if not config.deliver_group
150
- if queue
151
- raise NATS::JetStream::Error.new("nats: cannot create a queue subscription for a consumer without a deliver group")
152
- elsif cinfo.push_bound
153
- raise NATS::JetStream::Error.new("nats: consumer is already bound to a subscription")
154
- end
155
- else
156
- if not queue
157
- raise NATS::JetStream::Error.new("nats: cannot create a subscription for a consumer with a deliver group #{config.deliver_group}")
158
- elsif queue != config.deliver_group
159
- raise NATS::JetStream::Error.new("nats: cannot create a queue subscription #{queue} for a consumer with a deliver group #{config.deliver_group}")
160
- end
161
- end
162
- elsif should_create
163
- # Auto-create consumer if none found.
164
- if config.nil?
165
- # Defaults
166
- config = JetStream::API::ConsumerConfig.new({ack_policy: "explicit"})
167
- elsif config.is_a?(Hash)
168
- config = JetStream::API::ConsumerConfig.new(config)
169
- elsif !config.is_a?(JetStream::API::ConsumerConfig)
170
- raise NATS::JetStream::Error.new("nats: invalid ConsumerConfig")
171
- end
172
-
173
- config.durable_name = durable if not config.durable_name
174
- config.deliver_group = queue if not config.deliver_group
175
-
176
- # Create inbox for push consumer.
177
- deliver = @nc.new_inbox
178
- config.deliver_subject = deliver
179
-
180
- # Auto created consumers use the filter subject.
181
- config.filter_subject = subject
182
-
183
- # Heartbeats / FlowControl
184
- config.flow_control = flow_control
185
- if idle_heartbeat or config.idle_heartbeat
186
- idle_heartbeat = config.idle_heartbeat if config.idle_heartbeat
187
- idle_heartbeat = idle_heartbeat * ::NATS::NANOSECONDS
188
- config.idle_heartbeat = idle_heartbeat
189
- end
190
-
191
- # Auto create the consumer.
192
- cinfo = add_consumer(stream, config)
193
- consumer = cinfo.name
194
- end
195
-
196
- # Enable auto acking for async callbacks unless disabled.
197
- if cb and not manual_ack
198
- ocb = cb
199
- new_cb = proc do |msg|
200
- ocb.call(msg)
201
- msg.ack rescue JetStream::Error::MsgAlreadyAckd
202
- end
203
- cb = new_cb
204
- end
205
- sub = @nc.subscribe(config.deliver_subject, queue: config.deliver_group, &cb)
206
- sub.extend(PushSubscription)
207
- sub.jsi = JS::Sub.new(
208
- js: self,
209
- stream: stream,
210
- consumer: consumer,
211
- )
212
- sub
213
- end
214
-
215
- # pull_subscribe binds or creates a subscription to a JetStream pull consumer.
216
- #
217
- # @param subject [String] Subject from which the messages will be fetched.
218
- # @param durable [String] Consumer durable name from where the messages will be fetched.
219
- # @param params [Hash] Options to customize the PullSubscription.
220
- # @option params [String] :stream Name of the Stream to which the consumer belongs.
221
- # @option params [String] :consumer Name of the Consumer to which the PullSubscription will be bound.
222
- # @option params [Hash] :config Configuration for the consumer.
223
- # @return [NATS::JetStream::PullSubscription]
224
- def pull_subscribe(subject, durable, params={})
225
- if durable.empty? && !params[:consumer]
226
- raise JetStream::Error::InvalidDurableName.new("nats: invalid durable name")
227
- end
228
- params[:consumer] ||= durable
229
- stream = params[:stream].nil? ? find_stream_name_by_subject(subject) : params[:stream]
230
-
231
- begin
232
- consumer_info(stream, params[:consumer])
233
- rescue NATS::JetStream::Error::NotFound => e
234
- # If attempting to bind, then this is a hard error.
235
- raise e if params[:stream]
236
-
237
- config = if not params[:config]
238
- JetStream::API::ConsumerConfig.new
239
- elsif params[:config].is_a?(JetStream::API::ConsumerConfig)
240
- params[:config]
241
- else
242
- JetStream::API::ConsumerConfig.new(params[:config])
243
- end
244
- config[:durable_name] = durable
245
- config[:ack_policy] ||= JS::Config::AckExplicit
246
- add_consumer(stream, config)
247
- end
248
-
249
- deliver = @nc.new_inbox
250
- sub = @nc.subscribe(deliver)
251
- sub.extend(PullSubscription)
252
-
253
- consumer = params[:consumer]
254
- subject = "#{@prefix}.CONSUMER.MSG.NEXT.#{stream}.#{consumer}"
255
- sub.jsi = JS::Sub.new(
256
- js: self,
257
- stream: stream,
258
- consumer: params[:consumer],
259
- nms: subject
260
- )
261
- sub
262
- end
263
-
264
- # A JetStream::Manager can be used to make requests to the JetStream API.
265
- #
266
- # @example
267
- # require 'nats/client'
268
- #
269
- # nc = NATS.connect("demo.nats.io")
270
- #
271
- # config = JetStream::API::StreamConfig.new()
272
- # nc.jsm.add_stream(config)
273
- #
274
- #
275
- module Manager
276
- # add_stream creates a stream with a given config.
277
- # @param config [JetStream::API::StreamConfig] Configuration of the stream to create.
278
- # @param params [Hash] Options to customize API request.
279
- # @option params [Float] :timeout Time to wait for response.
280
- # @return [JetStream::API::StreamCreateResponse] The result of creating a Stream.
281
- def add_stream(config, params={})
282
- config = if not config.is_a?(JetStream::API::StreamConfig)
283
- JetStream::API::StreamConfig.new(config)
284
- else
285
- config
286
- end
287
- stream = config[:name]
288
- raise ArgumentError.new(":name is required to create streams") unless stream
289
- raise ArgumentError.new("Spaces, tabs, period (.), greater than (>) or asterisk (*) are prohibited in stream names") if stream =~ /(\s|\.|\>|\*)/
290
- req_subject = "#{@prefix}.STREAM.CREATE.#{stream}"
291
- result = api_request(req_subject, config.to_json, params)
292
- JetStream::API::StreamCreateResponse.new(result)
293
- end
294
-
295
- # stream_info retrieves the current status of a stream.
296
- # @param stream [String] Name of the stream.
297
- # @param params [Hash] Options to customize API request.
298
- # @option params [Float] :timeout Time to wait for response.
299
- # @return [JetStream::API::StreamInfo] The latest StreamInfo of the stream.
300
- def stream_info(stream, params={})
301
- raise JetStream::Error::InvalidStreamName.new("nats: invalid stream name") if stream.nil? or stream.empty?
302
-
303
- req_subject = "#{@prefix}.STREAM.INFO.#{stream}"
304
- result = api_request(req_subject, '', params)
305
- JetStream::API::StreamInfo.new(result)
306
- end
307
-
308
- # delete_stream deletes a stream.
309
- # @param stream [String] Name of the stream.
310
- # @param params [Hash] Options to customize API request.
311
- # @option params [Float] :timeout Time to wait for response.
312
- # @return [Boolean]
313
- def delete_stream(stream, params={})
314
- raise JetStream::Error::InvalidStreamName.new("nats: invalid stream name") if stream.nil? or stream.empty?
315
-
316
- req_subject = "#{@prefix}.STREAM.DELETE.#{stream}"
317
- result = api_request(req_subject, '', params)
318
- result[:success]
319
- end
320
-
321
- # add_consumer creates a consumer with a given config.
322
- # @param stream [String] Name of the stream.
323
- # @param config [JetStream::API::ConsumerConfig] Configuration of the consumer to create.
324
- # @param params [Hash] Options to customize API request.
325
- # @option params [Float] :timeout Time to wait for response.
326
- # @return [JetStream::API::ConsumerInfo] The result of creating a Consumer.
327
- def add_consumer(stream, config, params={})
328
- raise JetStream::Error::InvalidStreamName.new("nats: invalid stream name") if stream.nil? or stream.empty?
329
- config = if not config.is_a?(JetStream::API::ConsumerConfig)
330
- JetStream::API::ConsumerConfig.new(config)
331
- else
332
- config
333
- end
334
-
335
- req_subject = case
336
- when config[:name]
337
- # NOTE: Only supported after nats-server v2.9.0
338
- if config[:filter_subject] && config[:filter_subject] != ">"
339
- "#{@prefix}.CONSUMER.CREATE.#{stream}.#{config[:name]}.#{config[:filter_subject]}"
340
- else
341
- "#{@prefix}.CONSUMER.CREATE.#{stream}.#{config[:name]}"
342
- end
343
- when config[:durable_name]
344
- "#{@prefix}.CONSUMER.DURABLE.CREATE.#{stream}.#{config[:durable_name]}"
345
- else
346
- "#{@prefix}.CONSUMER.CREATE.#{stream}"
347
- end
348
-
349
- config[:ack_policy] ||= JS::Config::AckExplicit
350
- # Check if have to normalize ack wait so that it is in nanoseconds for Go compat.
351
- if config[:ack_wait]
352
- raise ArgumentError.new("nats: invalid ack wait") unless config[:ack_wait].is_a?(Integer)
353
- config[:ack_wait] = config[:ack_wait] * ::NATS::NANOSECONDS
354
- end
355
- if config[:inactive_threshold]
356
- raise ArgumentError.new("nats: invalid inactive threshold") unless config[:inactive_threshold].is_a?(Integer)
357
- config[:inactive_threshold] = config[:inactive_threshold] * ::NATS::NANOSECONDS
358
- end
359
-
360
- req = {
361
- stream_name: stream,
362
- config: config
363
- }
364
-
365
- result = api_request(req_subject, req.to_json, params)
366
- JetStream::API::ConsumerInfo.new(result).freeze
367
- end
368
-
369
- # consumer_info retrieves the current status of a consumer.
370
- # @param stream [String] Name of the stream.
371
- # @param consumer [String] Name of the consumer.
372
- # @param params [Hash] Options to customize API request.
373
- # @option params [Float] :timeout Time to wait for response.
374
- # @return [JetStream::API::ConsumerInfo] The latest ConsumerInfo of the consumer.
375
- def consumer_info(stream, consumer, params={})
376
- raise JetStream::Error::InvalidStreamName.new("nats: invalid stream name") if stream.nil? or stream.empty?
377
- raise JetStream::Error::InvalidConsumerName.new("nats: invalid consumer name") if consumer.nil? or consumer.empty?
378
-
379
- req_subject = "#{@prefix}.CONSUMER.INFO.#{stream}.#{consumer}"
380
- result = api_request(req_subject, '', params)
381
- JetStream::API::ConsumerInfo.new(result)
382
- end
383
-
384
- # delete_consumer deletes a consumer.
385
- # @param stream [String] Name of the stream.
386
- # @param consumer [String] Name of the consumer.
387
- # @param params [Hash] Options to customize API request.
388
- # @option params [Float] :timeout Time to wait for response.
389
- # @return [Boolean]
390
- def delete_consumer(stream, consumer, params={})
391
- raise JetStream::Error::InvalidStreamName.new("nats: invalid stream name") if stream.nil? or stream.empty?
392
- raise JetStream::Error::InvalidConsumerName.new("nats: invalid consumer name") if consumer.nil? or consumer.empty?
393
-
394
- req_subject = "#{@prefix}.CONSUMER.DELETE.#{stream}.#{consumer}"
395
- result = api_request(req_subject, '', params)
396
- result[:success]
397
- end
398
-
399
- # find_stream_name_by_subject does a lookup for the stream to which
400
- # the subject belongs.
401
- # @param subject [String] The subject that belongs to a stream.
402
- # @param params [Hash] Options to customize API request.
403
- # @option params [Float] :timeout Time to wait for response.
404
- # @return [String] The name of the JetStream stream for the subject.
405
- def find_stream_name_by_subject(subject, params={})
406
- req_subject = "#{@prefix}.STREAM.NAMES"
407
- req = { subject: subject }
408
- result = api_request(req_subject, req.to_json, params)
409
- raise JetStream::Error::NotFound unless result[:streams]
410
-
411
- result[:streams].first
412
- end
413
-
414
- # get_msg retrieves a message from the stream.
415
- # @param next [Boolean] Fetch the next message for a subject.
416
- # @param seq [Integer] Sequence number of a message.
417
- # @param subject [String] Subject of the message.
418
- # @param direct [Boolean] Use direct mode to for faster access (requires NATS v2.9.0)
419
- def get_msg(stream_name, params={})
420
- req = {}
421
- case
422
- when params[:next]
423
- req[:seq] = params[:seq]
424
- req[:next_by_subj] = params[:subject]
425
- when params[:seq]
426
- req[:seq] = params[:seq]
427
- when params[:subject]
428
- req[:last_by_subj] = params[:subject]
429
- end
430
-
431
- data = req.to_json
432
- if params[:direct]
433
- if params[:subject] and not params[:seq]
434
- # last_by_subject type request requires no payload.
435
- data = ''
436
- req_subject = "#{@prefix}.DIRECT.GET.#{stream_name}.#{params[:subject]}"
437
- else
438
- req_subject = "#{@prefix}.DIRECT.GET.#{stream_name}"
439
- end
440
- else
441
- req_subject = "#{@prefix}.STREAM.MSG.GET.#{stream_name}"
442
- end
443
- resp = api_request(req_subject, data, direct: params[:direct])
444
- msg = if params[:direct]
445
- _lift_msg_to_raw_msg(resp)
446
- else
447
- JetStream::API::RawStreamMsg.new(resp[:message])
448
- end
449
-
450
- msg
451
- end
452
-
453
- def get_last_msg(stream_name, subject, params={})
454
- params[:subject] = subject
455
- get_msg(stream_name, params)
456
- end
457
-
458
- def account_info
459
- api_request("#{@prefix}.INFO")
460
- end
461
-
462
- private
463
-
464
- def api_request(req_subject, req="", params={})
465
- params[:timeout] ||= @opts[:timeout]
466
- msg = begin
467
- @nc.request(req_subject, req, **params)
468
- rescue NATS::IO::NoRespondersError
469
- raise JetStream::Error::ServiceUnavailable
470
- end
471
-
472
- result = if params[:direct]
473
- msg
474
- else
475
- JSON.parse(msg.data, symbolize_names: true)
476
- end
477
- if result.is_a?(Hash) and result[:error]
478
- raise JS.from_error(result[:error])
479
- end
480
-
481
- result
482
- end
483
-
484
- def _lift_msg_to_raw_msg(msg)
485
- if msg.header and msg.header['Status']
486
- status = msg.header['Status']
487
- if status == '404'
488
- raise ::NATS::JetStream::Error::NotFound.new
489
- else
490
- raise JS.from_msg(msg)
491
- end
492
- end
493
- subject = msg.header['Nats-Subject']
494
- seq = msg.header['Nats-Sequence']
495
- raw_msg = JetStream::API::RawStreamMsg.new(
496
- subject: subject,
497
- seq: seq,
498
- headers: msg.header,
499
- )
500
- raw_msg.data = msg.data
501
-
502
- raw_msg
503
- end
504
- end
505
-
506
- # PushSubscription is included into NATS::Subscription so that it
507
- #
508
- # @example Create a push subscription using JetStream context.
509
- #
510
- # require 'nats/client'
511
- #
512
- # nc = NATS.connect
513
- # js = nc.jetstream
514
- # sub = js.subscribe("foo", "bar")
515
- # msg = sub.next_msg
516
- # msg.ack
517
- # sub.unsubscribe
518
- #
519
- # @!visibility public
520
- module PushSubscription
521
- # consumer_info retrieves the current status of the pull subscription consumer.
522
- # @param params [Hash] Options to customize API request.
523
- # @option params [Float] :timeout Time to wait for response.
524
- # @return [JetStream::API::ConsumerInfo] The latest ConsumerInfo of the consumer.
525
- def consumer_info(params={})
526
- @jsi.js.consumer_info(@jsi.stream, @jsi.consumer, params)
527
- end
528
- end
529
- private_constant :PushSubscription
530
-
531
- # PullSubscription is included into NATS::Subscription so that it
532
- # can be used to fetch messages from a pull based consumer from
533
- # JetStream.
534
- #
535
- # @example Create a pull subscription using JetStream context.
536
- #
537
- # require 'nats/client'
538
- #
539
- # nc = NATS.connect
540
- # js = nc.jetstream
541
- # psub = js.pull_subscribe("foo", "bar")
542
- #
543
- # loop do
544
- # msgs = psub.fetch(5)
545
- # msgs.each do |msg|
546
- # msg.ack
547
- # end
548
- # end
549
- #
550
- # @!visibility public
551
- module PullSubscription
552
- # next_msg is not available for pull based subscriptions.
553
- # @raise [NATS::JetStream::Error]
554
- def next_msg(params={})
555
- raise ::NATS::JetStream::Error.new("nats: pull subscription cannot use next_msg")
556
- end
557
-
558
- # fetch makes a request to be delivered more messages from a pull consumer.
559
- #
560
- # @param batch [Fixnum] Number of messages to pull from the stream.
561
- # @param params [Hash] Options to customize the fetch request.
562
- # @option params [Float] :timeout Duration of the fetch request before it expires.
563
- # @return [Array<NATS::Msg>]
564
- def fetch(batch=1, params={})
565
- if batch < 1
566
- raise ::NATS::JetStream::Error.new("nats: invalid batch size")
567
- end
568
-
569
- t = MonotonicTime.now
570
- timeout = params[:timeout] ||= 5
571
- expires = (timeout * 1_000_000_000) - 100_000
572
- next_req = {
573
- batch: batch
574
- }
575
-
576
- msgs = []
577
- case
578
- when batch < 1
579
- raise ::NATS::JetStream::Error.new("nats: invalid batch size")
580
- when batch == 1
581
- ####################################################
582
- # Fetch (1) #
583
- ####################################################
584
-
585
- # Check if there is any pending message in the queue that is
586
- # ready to be consumed.
587
- synchronize do
588
- unless @pending_queue.empty?
589
- msg = @pending_queue.pop
590
- @pending_size -= msg.data.size
591
- # Check for a no msgs response status.
592
- if JS.is_status_msg(msg)
593
- case msg.header["Status"]
594
- when JS::Status::NoMsgs
595
- msg = nil
596
- when JS::Status::RequestTimeout
597
- # Skip
598
- else
599
- raise JS.from_msg(msg)
600
- end
601
- else
602
- msgs << msg
603
- end
604
- end
605
- end
606
-
607
- # Make lingering request with expiration.
608
- next_req[:expires] = expires
609
- if msgs.empty?
610
- # Make publish request and wait for response.
611
- @nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
612
-
613
- # Wait for result of fetch or timeout.
614
- synchronize { wait_for_msgs_cond.wait(timeout) }
615
-
616
- unless @pending_queue.empty?
617
- msg = @pending_queue.pop
618
- @pending_size -= msg.data.size
619
-
620
- msgs << msg
621
- end
622
-
623
- duration = MonotonicTime.since(t)
624
- if duration > timeout
625
- raise ::NATS::Timeout.new("nats: fetch timeout")
626
- end
627
-
628
- # Should have received at least a message at this point,
629
- # if that is not the case then error already.
630
- if JS.is_status_msg(msgs.first)
631
- msg = msgs.first
632
- case msg.header[JS::Header::Status]
633
- when JS::Status::RequestTimeout
634
- raise NATS::Timeout.new("nats: fetch request timeout")
635
- else
636
- raise JS.from_msg(msgs.first)
637
- end
638
- end
639
- end
640
- when batch > 1
641
- ####################################################
642
- # Fetch (n) #
643
- ####################################################
644
-
645
- # Check if there already enough in the pending buffer.
646
- synchronize do
647
- if batch <= @pending_queue.size
648
- batch.times do
649
- msg = @pending_queue.pop
650
- @pending_size -= msg.data.size
651
-
652
- # Check for a no msgs response status.
653
- if JS.is_status_msg(msg)
654
- case msg.header[JS::Header::Status]
655
- when JS::Status::NoMsgs, JS::Status::RequestTimeout
656
- # Skip these
657
- next
658
- else
659
- raise JS.from_msg(msg)
660
- end
661
- else
662
- msgs << msg
663
- end
664
- end
665
-
666
- return msgs
667
- end
668
- end
669
-
670
- # Make publish request and wait any response.
671
- next_req[:no_wait] = true
672
- @nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
673
-
674
- # Not receiving even one is a timeout.
675
- start_time = MonotonicTime.now
676
- msg = nil
677
-
678
- synchronize do
679
- wait_for_msgs_cond.wait(timeout)
680
-
681
- unless @pending_queue.empty?
682
- msg = @pending_queue.pop
683
- @pending_size -= msg.data.size
684
- end
685
- end
686
-
687
- # Check if the first message was a response saying that
688
- # there are no messages.
689
- if !msg.nil? && JS.is_status_msg(msg)
690
- case msg.header[JS::Header::Status]
691
- when JS::Status::NoMsgs
692
- # Make another request that does wait.
693
- next_req[:expires] = expires
694
- next_req.delete(:no_wait)
695
-
696
- @nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
697
- when JS::Status::RequestTimeout
698
- raise NATS::Timeout.new("nats: fetch request timeout")
699
- else
700
- raise JS.from_msg(msg)
701
- end
702
- else
703
- msgs << msg unless msg.nil?
704
- end
705
-
706
- # Check if have not received yet a single message.
707
- duration = MonotonicTime.since(start_time)
708
- if msgs.empty? and duration > timeout
709
- raise NATS::Timeout.new("nats: fetch timeout")
710
- end
711
-
712
- needed = batch - msgs.count
713
- while needed > 0 and MonotonicTime.since(start_time) < timeout
714
- duration = MonotonicTime.since(start_time)
715
-
716
- # Wait for the rest of the messages.
717
- synchronize do
718
-
719
- # Wait until there is a message delivered.
720
- if @pending_queue.empty?
721
- deadline = timeout - duration
722
- wait_for_msgs_cond.wait(deadline) if deadline > 0
723
-
724
- duration = MonotonicTime.since(start_time)
725
- if msgs.empty? && @pending_queue.empty? and duration > timeout
726
- raise NATS::Timeout.new("nats: fetch timeout")
727
- end
728
- else
729
- msg = @pending_queue.pop
730
- @pending_size -= msg.data.size
731
-
732
- if JS.is_status_msg(msg)
733
- case msg.header[JS::Header::Status]
734
- when JS::Status::NoMsgs, JS::Status::RequestTimeout
735
- duration = MonotonicTime.since(start_time)
736
-
737
- if duration > timeout
738
- # Only received a subset of the messages.
739
- if !msgs.empty?
740
- return msgs
741
- else
742
- raise NATS::Timeout.new("nats: fetch timeout")
743
- end
744
- end
745
- else
746
- raise JS.from_msg(msg)
747
- end
748
-
749
- else
750
- # Add to the set of messages that will be returned.
751
- msgs << msg
752
- needed -= 1
753
- end
754
- end
755
- end # :end: synchronize
756
- end
757
- end
758
-
759
- msgs
760
- end
761
-
762
- # consumer_info retrieves the current status of the pull subscription consumer.
763
- # @param params [Hash] Options to customize API request.
764
- # @option params [Float] :timeout Time to wait for response.
765
- # @return [JetStream::API::ConsumerInfo] The latest ConsumerInfo of the consumer.
766
- def consumer_info(params={})
767
- @jsi.js.consumer_info(@jsi.stream, @jsi.consumer, params)
768
- end
769
- end
770
- private_constant :PullSubscription
771
-
772
- #######################################
773
- # #
774
- # JetStream Message and Ack Methods #
775
- # #
776
- #######################################
777
-
778
- # JetStream::Msg module includes the methods so that a regular NATS::Msg
779
- # can be enhanced with JetStream features like acking and metadata.
780
- module Msg
781
- module Ack
782
- # Ack types
783
- Ack = ("+ACK".freeze)
784
- Nak = ("-NAK".freeze)
785
- Progress = ("+WPI".freeze)
786
- Term = ("+TERM".freeze)
787
-
788
- Empty = (''.freeze)
789
- DotSep = ('.'.freeze)
790
- NoDomainName = ('_'.freeze)
791
-
792
- # Position
793
- Prefix0 = ('$JS'.freeze)
794
- Prefix1 = ('ACK'.freeze)
795
- Domain = 2
796
- AccHash = 3
797
- Stream = 4
798
- Consumer = 5
799
- NumDelivered = 6
800
- StreamSeq = 7
801
- ConsumerSeq = 8
802
- Timestamp = 9
803
- NumPending = 10
804
-
805
- # Subject without domain:
806
- # $JS.ACK.<stream>.<consumer>.<delivered>.<sseq>.<cseq>.<tm>.<pending>
807
- #
808
- V1TokenCounts = 9
809
-
810
- # Subject with domain:
811
- # $JS.ACK.<domain>.<account hash>.<stream>.<consumer>.<delivered>.<sseq>.<cseq>.<tm>.<pending>.<a token with a random value>
812
- #
813
- V2TokenCounts = 12
814
-
815
- SequencePair = Struct.new(:stream, :consumer)
816
- end
817
- private_constant :Ack
818
-
819
- class Metadata
820
- attr_reader :sequence, :num_delivered, :num_pending, :timestamp, :stream, :consumer, :domain
821
-
822
- def initialize(opts)
823
- @sequence = Ack::SequencePair.new(opts[Ack::StreamSeq].to_i, opts[Ack::ConsumerSeq].to_i)
824
- @domain = opts[Ack::Domain]
825
- @num_delivered = opts[Ack::NumDelivered].to_i
826
- @num_pending = opts[Ack::NumPending].to_i
827
- @timestamp = Time.at((opts[Ack::Timestamp].to_i / 1_000_000_000.0))
828
- @stream = opts[Ack::Stream]
829
- @consumer = opts[Ack::Consumer]
830
- # TODO: Not exposed in Go client either right now.
831
- # account = opts[Ack::AccHash]
832
- end
833
- end
834
-
835
- module AckMethods
836
- def ack(**params)
837
- ensure_is_acked_once!
838
-
839
- resp = if params[:timeout]
840
- @nc.request(@reply, Ack::Ack, **params)
841
- else
842
- @nc.publish(@reply, Ack::Ack)
843
- end
844
- @sub.synchronize { @ackd = true }
845
-
846
- resp
847
- end
848
-
849
- def ack_sync(**params)
850
- ensure_is_acked_once!
851
-
852
- params[:timeout] ||= 0.5
853
- resp = @nc.request(@reply, Ack::Ack, **params)
854
- @sub.synchronize { @ackd = true }
855
-
856
- resp
857
- end
858
-
859
- def nak(**params)
860
- ensure_is_acked_once!
861
-
862
- resp = if params[:timeout]
863
- @nc.request(@reply, Ack::Nak, **params)
864
- else
865
- @nc.publish(@reply, Ack::Nak)
866
- end
867
- @sub.synchronize { @ackd = true }
868
-
869
- resp
870
- end
871
-
872
- def term(**params)
873
- ensure_is_acked_once!
874
-
875
- resp = if params[:timeout]
876
- @nc.request(@reply, Ack::Term, **params)
877
- else
878
- @nc.publish(@reply, Ack::Term)
879
- end
880
- @sub.synchronize { @ackd = true }
881
-
882
- resp
883
- end
884
-
885
- def in_progress(**params)
886
- params[:timeout] ? @nc.request(@reply, Ack::Progress, **params) : @nc.publish(@reply, Ack::Progress)
887
- end
888
-
889
- def metadata
890
- @meta ||= parse_metadata(reply)
891
- end
892
-
893
- private
894
-
895
- def ensure_is_acked_once!
896
- @sub.synchronize do
897
- if @ackd
898
- raise JetStream::Error::MsgAlreadyAckd.new("nats: message was already acknowledged: #{self}")
899
- end
900
- end
901
- end
902
-
903
- def parse_metadata(reply)
904
- tokens = reply.split(Ack::DotSep)
905
- n = tokens.count
906
-
907
- case
908
- when n < Ack::V1TokenCounts || (n > Ack::V1TokenCounts and n < Ack::V2TokenCounts)
909
- raise NotJSMessage.new("nats: not a jetstream message")
910
- when tokens[0] != Ack::Prefix0 || tokens[1] != Ack::Prefix1
911
- raise NotJSMessage.new("nats: not a jetstream message")
912
- when n == Ack::V1TokenCounts
913
- tokens.insert(Ack::Domain, Ack::Empty)
914
- tokens.insert(Ack::AccHash, Ack::Empty)
915
- when tokens[Ack::Domain] == Ack::NoDomainName
916
- tokens[Ack::Domain] = Ack::Empty
917
- end
918
-
919
- Metadata.new(tokens)
920
- end
921
- end
922
- end
923
-
924
- ####################################
925
- # #
926
- # JetStream Configuration Options #
927
- # #
928
- ####################################
929
-
930
- # Misc internal functions to support JS API.
931
- # @private
932
- module JS
933
- DefaultAPIPrefix = ("$JS.API".freeze)
934
-
935
- module Status
936
- CtrlMsg = ("100".freeze)
937
- NoMsgs = ("404".freeze)
938
- NotFound = ("404".freeze)
939
- RequestTimeout = ("408".freeze)
940
- ServiceUnavailable = ("503".freeze)
941
- end
942
-
943
- module Header
944
- Status = ("Status".freeze)
945
- Desc = ("Description".freeze)
946
- MsgID = ("Nats-Msg-Id".freeze)
947
- ExpectedStream = ("Nats-Expected-Stream".freeze)
948
- ExpectedLastSeq = ("Nats-Expected-Last-Sequence".freeze)
949
- ExpectedLastSubjSeq = ("Nats-Expected-Last-Subject-Sequence".freeze)
950
- ExpectedLastMsgID = ("Nats-Expected-Last-Msg-Id".freeze)
951
- LastConsumerSeq = ("Nats-Last-Consumer".freeze)
952
- LastStreamSeq = ("Nats-Last-Stream".freeze)
953
- end
954
-
955
- module Config
956
- # AckPolicy
957
- AckExplicit = ("explicit".freeze)
958
- AckAll = ("all".freeze)
959
- AckNone = ("none".freeze)
960
- end
961
-
962
- class Sub
963
- attr_reader :js, :stream, :consumer, :nms
964
-
965
- def initialize(opts={})
966
- @js = opts[:js]
967
- @stream = opts[:stream]
968
- @consumer = opts[:consumer]
969
- @nms = opts[:nms]
970
- end
971
- end
972
-
973
- class << self
974
- def next_req_to_json(next_req)
975
- req = {}
976
- req[:batch] = next_req[:batch]
977
- req[:expires] = next_req[:expires].to_i if next_req[:expires]
978
- req[:no_wait] = next_req[:no_wait] if next_req[:no_wait]
979
- req.to_json
980
- end
981
-
982
- def is_status_msg(msg)
983
- return (!msg.nil? and (!msg.header.nil? and msg.header[Header::Status]))
984
- end
985
-
986
- # check_503_error raises exception when a NATS::Msg has a 503 status header.
987
- # @param msg [NATS::Msg] The message with status headers.
988
- # @raise [NATS::JetStream::Error::ServiceUnavailable]
989
- def check_503_error(msg)
990
- return if msg.nil? or msg.header.nil?
991
- if msg.header[Header::Status] == Status::ServiceUnavailable
992
- raise ::NATS::JetStream::Error::ServiceUnavailable
993
- end
994
- end
995
-
996
- # from_msg takes a plain NATS::Msg and checks its headers to confirm
997
- # if it was an error:
998
- #
999
- # msg.header={"Status"=>"503"})
1000
- # msg.header={"Status"=>"408", "Description"=>"Request Timeout"})
1001
- #
1002
- # @param msg [NATS::Msg] The message with status headers.
1003
- # @return [NATS::JetStream::API::Error]
1004
- def from_msg(msg)
1005
- check_503_error(msg)
1006
- code = msg.header[JS::Header::Status]
1007
- desc = msg.header[JS::Header::Desc]
1008
- return ::NATS::JetStream::API::Error.new({code: code, description: desc})
1009
- end
1010
-
1011
- # from_error takes an API response that errored and maps the error
1012
- # into a JetStream error type based on the status and error code.
1013
- def from_error(err)
1014
- return unless err
1015
- case err[:code]
1016
- when 503
1017
- ::NATS::JetStream::Error::ServiceUnavailable.new(err)
1018
- when 500
1019
- ::NATS::JetStream::Error::ServerError.new(err)
1020
- when 404
1021
- case err[:err_code]
1022
- when 10059
1023
- ::NATS::JetStream::Error::StreamNotFound.new(err)
1024
- when 10014
1025
- ::NATS::JetStream::Error::ConsumerNotFound.new(err)
1026
- else
1027
- ::NATS::JetStream::Error::NotFound.new(err)
1028
- end
1029
- when 400
1030
- ::NATS::JetStream::Error::BadRequest.new(err)
1031
- else
1032
- ::NATS::JetStream::API::Error.new(err)
1033
- end
1034
- end
1035
- end
1036
- end
1037
- private_constant :JS
1038
-
1039
- #####################
1040
- # #
1041
- # JetStream Errors #
1042
- # #
1043
- #####################
1044
-
1045
- # Error is any error that may arise when interacting with JetStream.
1046
- class Error < Error
1047
-
1048
- # When there is a NATS::IO::NoResponders error after making a publish request.
1049
- class NoStreamResponse < Error; end
1050
-
1051
- # When an invalid durable or consumer name was attempted to be used.
1052
- class InvalidDurableName < Error; end
1053
-
1054
- # When an ack not longer valid.
1055
- class InvalidJSAck < Error; end
1056
-
1057
- # When an ack has already been acked.
1058
- class MsgAlreadyAckd < Error; end
1059
-
1060
- # When the delivered message does not behave as a message delivered by JetStream,
1061
- # for example when the ack reply has unrecognizable fields.
1062
- class NotJSMessage < Error; end
1063
-
1064
- # When the stream name is invalid.
1065
- class InvalidStreamName < Error; end
1066
-
1067
- # When the consumer name is invalid.
1068
- class InvalidConsumerName < Error; end
1069
-
1070
- # When the server responds with an error from the JetStream API.
1071
- class APIError < Error
1072
- attr_reader :code, :err_code, :description, :stream, :seq
1073
-
1074
- def initialize(params={})
1075
- @code = params[:code]
1076
- @err_code = params[:err_code]
1077
- @description = params[:description]
1078
- @stream = params[:stream]
1079
- @seq = params[:seq]
1080
- end
1081
-
1082
- def to_s
1083
- "#{@description} (status_code=#{@code}, err_code=#{@err_code})"
1084
- end
1085
- end
1086
-
1087
- # When JetStream is not currently available, this could be due to JetStream
1088
- # not being enabled or temporarily unavailable due to a leader election when
1089
- # running in cluster mode.
1090
- # This condition is represented with a message that has 503 status code header.
1091
- class ServiceUnavailable < APIError
1092
- def initialize(params={})
1093
- super(params)
1094
- @code ||= 503
1095
- end
1096
- end
1097
-
1098
- # When there is a hard failure in the JetStream.
1099
- # This condition is represented with a message that has 500 status code header.
1100
- class ServerError < APIError
1101
- def initialize(params={})
1102
- super(params)
1103
- @code ||= 500
1104
- end
1105
- end
1106
-
1107
- # When a JetStream object was not found.
1108
- # This condition is represented with a message that has 404 status code header.
1109
- class NotFound < APIError
1110
- def initialize(params={})
1111
- super(params)
1112
- @code ||= 404
1113
- end
1114
- end
1115
-
1116
- # When the stream is not found.
1117
- class StreamNotFound < NotFound; end
1118
-
1119
- # When the consumer or durable is not found by name.
1120
- class ConsumerNotFound < NotFound; end
1121
-
1122
- # When the JetStream client makes an invalid request.
1123
- # This condition is represented with a message that has 400 status code header.
1124
- class BadRequest < APIError
1125
- def initialize(params={})
1126
- super(params)
1127
- @code ||= 400
1128
- end
1129
- end
1130
- end
1131
-
1132
- #######################
1133
- # #
1134
- # JetStream API Types #
1135
- # #
1136
- #######################
1137
-
1138
- # JetStream::API are the types used to interact with the JetStream API.
1139
- module API
1140
- # When the server responds with an error from the JetStream API.
1141
- Error = ::NATS::JetStream::Error::APIError
1142
-
1143
- # SequenceInfo is a pair of consumer and stream sequence and last activity.
1144
- # @!attribute consumer_seq
1145
- # @return [Integer] The consumer sequence.
1146
- # @!attribute stream_seq
1147
- # @return [Integer] The stream sequence.
1148
- SequenceInfo = Struct.new(:consumer_seq, :stream_seq, :last_active,
1149
- keyword_init: true) do
1150
- def initialize(opts={})
1151
- # Filter unrecognized fields and freeze.
1152
- rem = opts.keys - members
1153
- opts.delete_if { |k| rem.include?(k) }
1154
- super(opts)
1155
- freeze
1156
- end
1157
- end
1158
-
1159
- # ConsumerInfo is the current status of a JetStream consumer.
1160
- #
1161
- # @!attribute stream_name
1162
- # @return [String] name of the stream to which the consumer belongs.
1163
- # @!attribute name
1164
- # @return [String] name of the consumer.
1165
- # @!attribute created
1166
- # @return [String] time when the consumer was created.
1167
- # @!attribute config
1168
- # @return [ConsumerConfig] consumer configuration.
1169
- # @!attribute delivered
1170
- # @return [SequenceInfo]
1171
- # @!attribute ack_floor
1172
- # @return [SequenceInfo]
1173
- # @!attribute num_ack_pending
1174
- # @return [Integer]
1175
- # @!attribute num_redelivered
1176
- # @return [Integer]
1177
- # @!attribute num_waiting
1178
- # @return [Integer]
1179
- # @!attribute num_pending
1180
- # @return [Integer]
1181
- # @!attribute cluster
1182
- # @return [Hash]
1183
- ConsumerInfo = Struct.new(:type, :stream_name, :name, :created,
1184
- :config, :delivered, :ack_floor,
1185
- :num_ack_pending, :num_redelivered, :num_waiting,
1186
- :num_pending, :cluster, :push_bound,
1187
- keyword_init: true) do
1188
- def initialize(opts={})
1189
- opts[:created] = Time.parse(opts[:created])
1190
- opts[:ack_floor] = SequenceInfo.new(opts[:ack_floor])
1191
- opts[:delivered] = SequenceInfo.new(opts[:delivered])
1192
- opts[:config][:ack_wait] = opts[:config][:ack_wait] / ::NATS::NANOSECONDS
1193
- opts[:config] = ConsumerConfig.new(opts[:config])
1194
- opts.delete(:cluster)
1195
- # Filter unrecognized fields just in case.
1196
- rem = opts.keys - members
1197
- opts.delete_if { |k| rem.include?(k) }
1198
- super(opts)
1199
- freeze
1200
- end
1201
- end
1202
-
1203
- # ConsumerConfig is the consumer configuration.
1204
- #
1205
- # @!attribute durable_name
1206
- # @return [String]
1207
- # @!attribute deliver_policy
1208
- # @return [String]
1209
- # @!attribute ack_policy
1210
- # @return [String]
1211
- # @!attribute ack_wait
1212
- # @return [Integer]
1213
- # @!attribute max_deliver
1214
- # @return [Integer]
1215
- # @!attribute replay_policy
1216
- # @return [String]
1217
- # @!attribute max_waiting
1218
- # @return [Integer]
1219
- # @!attribute max_ack_pending
1220
- # @return [Integer]
1221
- ConsumerConfig = Struct.new(:name, :durable_name, :description,
1222
- :deliver_policy, :opt_start_seq, :opt_start_time,
1223
- :ack_policy, :ack_wait, :max_deliver, :backoff,
1224
- :filter_subject, :replay_policy, :rate_limit_bps,
1225
- :sample_freq, :max_waiting, :max_ack_pending,
1226
- :flow_control, :idle_heartbeat, :headers_only,
1227
-
1228
- # Pull based options
1229
- :max_batch, :max_expires,
1230
- # Push based consumers
1231
- :deliver_subject, :deliver_group,
1232
- # Ephemeral inactivity threshold
1233
- :inactive_threshold,
1234
- # Generally inherited by parent stream and other markers,
1235
- # now can be configured directly.
1236
- :num_replicas,
1237
- # Force memory storage
1238
- :mem_storage,
1239
- keyword_init: true) do
1240
- def initialize(opts={})
1241
- # Filter unrecognized fields just in case.
1242
- rem = opts.keys - members
1243
- opts.delete_if { |k| rem.include?(k) }
1244
- super(opts)
1245
- end
1246
-
1247
- def to_json(*args)
1248
- config = self.to_h
1249
- config.delete_if { |_k, v| v.nil? }
1250
- config.to_json(*args)
1251
- end
1252
- end
1253
-
1254
- # StreamConfig represents the configuration of a stream from JetStream.
1255
- #
1256
- # @!attribute type
1257
- # @return [String]
1258
- # @!attribute config
1259
- # @return [Hash]
1260
- # @!attribute created
1261
- # @return [String]
1262
- # @!attribute state
1263
- # @return [StreamState]
1264
- # @!attribute did_create
1265
- # @return [Boolean]
1266
- # @!attribute name
1267
- # @return [String]
1268
- # @!attribute subjects
1269
- # @return [Array]
1270
- # @!attribute retention
1271
- # @return [String]
1272
- # @!attribute max_consumers
1273
- # @return [Integer]
1274
- # @!attribute max_msgs
1275
- # @return [Integer]
1276
- # @!attribute max_bytes
1277
- # @return [Integer]
1278
- # @!attribute max_age
1279
- # @return [Integer]
1280
- # @!attribute max_msgs_per_subject
1281
- # @return [Integer]
1282
- # @!attribute max_msg_size
1283
- # @return [Integer]
1284
- # @!attribute discard
1285
- # @return [String]
1286
- # @!attribute storage
1287
- # @return [String]
1288
- # @!attribute num_replicas
1289
- # @return [Integer]
1290
- # @!attribute duplicate_window
1291
- # @return [Integer]
1292
- StreamConfig = Struct.new(
1293
- :name,
1294
- :description,
1295
- :subjects,
1296
- :retention,
1297
- :max_consumers,
1298
- :max_msgs,
1299
- :max_bytes,
1300
- :discard,
1301
- :max_age,
1302
- :max_msgs_per_subject,
1303
- :max_msg_size,
1304
- :storage,
1305
- :num_replicas,
1306
- :no_ack,
1307
- :duplicate_window,
1308
- :placement,
1309
- :mirror,
1310
- :sources,
1311
- :sealed,
1312
- :deny_delete,
1313
- :deny_purge,
1314
- :allow_rollup_hdrs,
1315
- :republish,
1316
- :allow_direct,
1317
- :mirror_direct,
1318
- keyword_init: true) do
1319
- def initialize(opts={})
1320
- # Filter unrecognized fields just in case.
1321
- rem = opts.keys - members
1322
- opts.delete_if { |k| rem.include?(k) }
1323
- super(opts)
1324
- end
1325
-
1326
- def to_json(*args)
1327
- config = self.to_h
1328
- config.delete_if { |_k, v| v.nil? }
1329
- config.to_json(*args)
1330
- end
1331
- end
1332
-
1333
- # StreamInfo is the info about a stream from JetStream.
1334
- #
1335
- # @!attribute type
1336
- # @return [String]
1337
- # @!attribute config
1338
- # @return [Hash]
1339
- # @!attribute created
1340
- # @return [String]
1341
- # @!attribute state
1342
- # @return [Hash]
1343
- # @!attribute domain
1344
- # @return [String]
1345
- StreamInfo = Struct.new(:type, :config, :created, :state, :domain,
1346
- keyword_init: true) do
1347
- def initialize(opts={})
1348
- opts[:config] = StreamConfig.new(opts[:config])
1349
- opts[:state] = StreamState.new(opts[:state])
1350
- opts[:created] = ::Time.parse(opts[:created])
1351
-
1352
- # Filter fields and freeze.
1353
- rem = opts.keys - members
1354
- opts.delete_if { |k| rem.include?(k) }
1355
- super(opts)
1356
- freeze
1357
- end
1358
- end
1359
-
1360
- # StreamState is the state of a stream.
1361
- #
1362
- # @!attribute messages
1363
- # @return [Integer]
1364
- # @!attribute bytes
1365
- # @return [Integer]
1366
- # @!attribute first_seq
1367
- # @return [Integer]
1368
- # @!attribute last_seq
1369
- # @return [Integer]
1370
- # @!attribute consumer_count
1371
- # @return [Integer]
1372
- StreamState = Struct.new(:messages, :bytes, :first_seq, :first_ts,
1373
- :last_seq, :last_ts, :consumer_count,
1374
- keyword_init: true) do
1375
- def initialize(opts={})
1376
- rem = opts.keys - members
1377
- opts.delete_if { |k| rem.include?(k) }
1378
- super(opts)
1379
- end
1380
- end
1381
-
1382
- # StreamCreateResponse is the response from the JetStream $JS.API.STREAM.CREATE API.
1383
- #
1384
- # @!attribute type
1385
- # @return [String]
1386
- # @!attribute config
1387
- # @return [StreamConfig]
1388
- # @!attribute created
1389
- # @return [String]
1390
- # @!attribute state
1391
- # @return [StreamState]
1392
- # @!attribute did_create
1393
- # @return [Boolean]
1394
- StreamCreateResponse = Struct.new(:type, :config, :created, :state, :did_create,
1395
- keyword_init: true) do
1396
- def initialize(opts={})
1397
- rem = opts.keys - members
1398
- opts.delete_if { |k| rem.include?(k) }
1399
- opts[:config] = StreamConfig.new(opts[:config])
1400
- opts[:state] = StreamState.new(opts[:state])
1401
- super(opts)
1402
- freeze
1403
- end
1404
- end
1405
-
1406
- RawStreamMsg = Struct.new(:subject, :seq, :data, :headers, keyword_init: true) do
1407
- def initialize(opts)
1408
- opts[:data] = Base64.decode64(opts[:data]) if opts[:data]
1409
- if opts[:hdrs]
1410
- header = Base64.decode64(opts[:hdrs])
1411
- hdr = {}
1412
- lines = header.lines
1413
- lines.slice(1, header.size).each do |line|
1414
- line.rstrip!
1415
- next if line.empty?
1416
- key, value = line.strip.split(/\s*:\s*/, 2)
1417
- hdr[key] = value
1418
- end
1419
- opts[:headers] = hdr
1420
- end
1421
-
1422
- # Filter out members not present.
1423
- rem = opts.keys - members
1424
- opts.delete_if { |k| rem.include?(k) }
1425
- super(opts)
1426
- end
1427
-
1428
- def sequence
1429
- self.seq
1430
- end
1431
- end
1432
- end
1433
- end
1434
- end