nats-pure 2.5.0 → 2.7.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.
@@ -16,6 +16,7 @@
16
16
  require_relative "kv"
17
17
  require_relative "jetstream/api"
18
18
  require_relative "jetstream/errors"
19
+ require_relative "jetstream/header"
19
20
  require_relative "jetstream/js"
20
21
  require_relative "jetstream/manager"
21
22
  require_relative "jetstream/msg"
@@ -69,7 +70,19 @@ module NATS
69
70
  # @return [Boolean] Indicates whether the published message is a duplicate.
70
71
  # @!attribute [domain] domain
71
72
  # @return [String] JetStream Domain that processed the ack response.
72
- PubAck = Struct.new(:stream, :seq, :duplicate, :domain, keyword_init: true)
73
+ # @!attribute [val] val
74
+ # @return [String] Value of the counter after the increment, set when publishing to a stream with counters enabled, unless the message was a duplicate.
75
+ # @!attribute [batch] batch
76
+ # @return [String] ID of the batch, set on the ack of an atomic batch commit.
77
+ # @!attribute [count] count
78
+ # @return [Integer] Number of messages the batch stored, set on the ack of an atomic batch commit.
79
+ PubAck = Struct.new(:stream, :seq, :duplicate, :domain, :val, :batch, :count,
80
+ keyword_init: true) do
81
+ # Fields added by newer servers are ignored, so that they cannot break publish.
82
+ def initialize(opts = {})
83
+ super(**opts.slice(*members))
84
+ end
85
+ end
73
86
 
74
87
  # publish produces a message for JetStream.
75
88
  #
@@ -77,21 +90,51 @@ module NATS
77
90
  # @param payload [String] The payload of the message.
78
91
  # @param params [Hash] Options to customize the publish message request.
79
92
  # @option params [Float] :timeout Time to wait for an PubAck response or an error.
80
- # @option params [Hash] :header NATS Headers to use for the message.
93
+ # @option params [Hash] :header NATS Headers to use for the message; the
94
+ # options below replace those that they set.
81
95
  # @option params [String] :stream Expected Stream to which the message is being published.
96
+ # @option params [Integer, Symbol] :ttl Seconds after which the stream
97
+ # removes the message, from 1 to 2**32, or :never to keep it past the max_age of
98
+ # the stream. The stream needs allow_msg_ttl (requires nats-server v2.11.0).
99
+ # @option params [Hash] :schedule Makes the message a schedule, which
100
+ # publishes it to the subject given as :target: once at the Time given
101
+ # as :at; every so many whole seconds, given as :every; or on a :cron
102
+ # expression with seconds, such as "0 30 * * * *", or "@hourly",
103
+ # "@daily", "@weekly", "@monthly" or "@yearly", in UTC or the IANA
104
+ # :time_zone, which only cron schedules take. :at is sent to the
105
+ # nanosecond. :source publishes the last message of that subject
106
+ # instead, :ttl gives the published messages a TTL, like the ttl
107
+ # option, and rollup: true rolls up the target with each. The target
108
+ # and source have to be subjects of the stream, which needs
109
+ # allow_msg_schedules, and allow_msg_ttl for :ttl. Another schedule
110
+ # on the subject replaces the schedule, and deleting its message stops
111
+ # it. Requires nats-server v2.12.0 for :at, :target and :ttl, and
112
+ # v2.14.0 for the others; v2.12 ignores :source and :rollup.
82
113
  # @raise [NATS::Timeout] When it takes too long to receive an ack response.
114
+ # @raise [ArgumentError] When an option is invalid, before the message is sent.
115
+ # @raise [NATS::JetStream::Error::APIError] When the stream refuses the
116
+ # message, as a stream that does not allow TTLs refuses one with a TTL.
117
+ # @raise [NATS::JetStream::Error::NoStreamResponse] When no stream takes
118
+ # the subject.
83
119
  # @return [PubAck] The pub ack response.
84
120
  def publish(subject, payload = "", **params)
85
121
  params[:timeout] ||= @opts[:timeout]
86
- if params[:stream]
87
- params[:header] ||= {}
88
- params[:header][JS::Header::ExpectedStream] = params[:stream]
122
+ # The options add to a copy of the header, which the caller may reuse.
123
+ options = {
124
+ Header::EXPECTED_STREAM => (params[:stream] if params[:stream]),
125
+ Header::MSG_TTL => (msg_ttl(params[:ttl]) if params[:ttl])
126
+ }.compact
127
+ if (schedule = params[:schedule])
128
+ raise ArgumentError.new("nats: invalid schedule #{schedule.inspect}, expected a Hash") unless schedule.is_a?(Hash)
129
+
130
+ options.merge!(schedule_header(**schedule))
89
131
  end
132
+ header = options.empty? ? params[:header] : params[:header].to_h.merge(options)
90
133
 
91
134
  # Send message with headers.
92
135
  msg = NATS::Msg.new(subject: subject,
93
136
  data: payload,
94
- header: params[:header])
137
+ header: header)
95
138
 
96
139
  begin
97
140
  resp = @nc.request_msg(msg, **params)
@@ -305,7 +348,7 @@ module NATS
305
348
  # If attempting to bind, then this is a hard error.
306
349
  raise e if params[:stream] && !multi_filter
307
350
 
308
- config = if !(params[:config])
351
+ config = if !params[:config]
309
352
  JetStream::API::ConsumerConfig.new
310
353
  elsif params[:config].is_a?(JetStream::API::ConsumerConfig)
311
354
  params[:config]
@@ -322,8 +365,8 @@ module NATS
322
365
  add_consumer(stream, config)
323
366
  end
324
367
 
325
- deliver = @nc.new_inbox
326
- sub = @nc.subscribe(deliver)
368
+ # Each pull gets a reply of its own under the subscription.
369
+ sub = @nc.subscribe("#{@nc.new_inbox}.*")
327
370
  sub.extend(PullSubscription)
328
371
 
329
372
  consumer = params[:consumer]
@@ -336,5 +379,51 @@ module NATS
336
379
  )
337
380
  sub
338
381
  end
382
+
383
+ private
384
+
385
+ # msg_ttl formats a message TTL as the server takes it. Longer TTLs than
386
+ # 2**32 seconds, some 136 years, overflow in the server, which then
387
+ # removes the message at once.
388
+ def msg_ttl(ttl)
389
+ return "never" if ttl == :never
390
+ unless ttl.is_a?(Integer) && ttl.between?(1, 2**32)
391
+ raise ArgumentError.new("nats: invalid ttl #{ttl.inspect}, expected whole seconds from 1 to 2**32, or :never")
392
+ end
393
+
394
+ ttl.to_s
395
+ end
396
+
397
+ # schedule_header makes the headers of a message schedule.
398
+ def schedule_header(target:, at: nil, every: nil, cron: nil, source: nil, ttl: nil, time_zone: nil, rollup: nil)
399
+ pattern = if at.is_a?(Time)
400
+ "@at #{at.getutc.iso8601(9)}"
401
+ elsif every.is_a?(Integer) && every >= 1
402
+ "@every #{every}s"
403
+ elsif nonempty_string?(cron)
404
+ cron
405
+ end
406
+ unless pattern && [at, every, cron].compact.size == 1
407
+ raise ArgumentError.new("nats: a schedule needs one of at: a Time, every: whole seconds from 1, or cron: an expression")
408
+ end
409
+ raise ArgumentError.new("nats: a schedule needs a target subject") unless nonempty_string?(target)
410
+ raise ArgumentError.new("nats: invalid schedule source #{source.inspect}") unless source.nil? || nonempty_string?(source)
411
+ raise ArgumentError.new("nats: invalid schedule time zone #{time_zone.inspect}") unless time_zone.nil? || nonempty_string?(time_zone)
412
+ raise ArgumentError.new("nats: only cron: schedules take a time zone") if time_zone && !cron
413
+ raise ArgumentError.new("nats: invalid schedule rollup #{rollup.inspect}") unless [nil, true, false].include?(rollup)
414
+
415
+ {
416
+ Header::SCHEDULE => pattern,
417
+ Header::SCHEDULE_TARGET => target,
418
+ Header::SCHEDULE_SOURCE => source,
419
+ Header::SCHEDULE_TTL => (msg_ttl(ttl) if ttl),
420
+ Header::SCHEDULE_TIME_ZONE => time_zone,
421
+ Header::SCHEDULE_ROLLUP => ("sub" if rollup)
422
+ }.compact
423
+ end
424
+
425
+ def nonempty_string?(value)
426
+ value.is_a?(String) && !value.empty?
427
+ end
339
428
  end
340
429
  end
@@ -30,6 +30,10 @@ module NATS
30
30
  :republish,
31
31
  :direct,
32
32
  :validate_keys,
33
+ # Compress the bucket's stream with S2 (requires nats-server v2.10.0).
34
+ :compression,
35
+ # Freeform metadata of the bucket (requires nats-server v2.10.0).
36
+ :metadata,
33
37
  keyword_init: true
34
38
  ) do
35
39
  def initialize(opts = {})
@@ -35,6 +35,18 @@ module NATS
35
35
  def ttl
36
36
  @stream_info.config.max_age / ::NATS::NANOSECONDS
37
37
  end
38
+
39
+ # Whether the bucket's stream is compressed (requires nats-server v2.10.0).
40
+ def compressed?
41
+ compression = @stream_info.config.compression
42
+ !compression.nil? && compression != "none"
43
+ end
44
+
45
+ # The bucket's metadata, including the metadata that the server sets
46
+ # (requires nats-server v2.10.0).
47
+ def metadata
48
+ @stream_info.config.metadata
49
+ end
38
50
  end
39
51
  end
40
52
  end
@@ -59,6 +59,10 @@ module NATS
59
59
  raise NATS::KeyValue::KeyHistoryTooLargeError
60
60
  end
61
61
 
62
+ unless [true, false, nil].include?(config.compression)
63
+ raise ArgumentError.new("nats: compression must be true or false")
64
+ end
65
+
62
66
  stream = JetStream::API::StreamConfig.new(
63
67
  name: "KV_#{config.bucket}",
64
68
  description: config.description,
@@ -76,7 +80,9 @@ module NATS
76
80
  max_msgs_per_subject: config.history,
77
81
  num_replicas: config.replicas,
78
82
  storage: config.storage,
79
- republish: config.republish
83
+ republish: config.republish,
84
+ compression: config.compression ? "s2" : nil,
85
+ metadata: config.metadata
80
86
  )
81
87
 
82
88
  si = add_stream(stream)
data/lib/nats/io/kv.rb CHANGED
@@ -31,7 +31,7 @@ module NATS
31
31
  ROLLUP = "Nats-Rollup"
32
32
 
33
33
  VALID_BUCKET_RE = /\A[a-zA-Z0-9_-]+$/
34
- VALID_KEY_RE = /\A[-\/_=\.a-zA-Z0-9]+$/
34
+ VALID_KEY_RE = /\A[-\/_=.a-zA-Z0-9]+$/
35
35
 
36
36
  class << self
37
37
  def is_valid_key(key)
@@ -258,7 +258,8 @@ module NATS
258
258
  CONSUMER_STALLED_HDR = "Nats-Consumer-Stalled"
259
259
 
260
260
  # watch will be signaled when a key that matches the keys
261
- # pattern is updated.
261
+ # pattern is updated. keys can also be an Array of patterns
262
+ # (requires nats-server v2.10.0); an empty Array watches all keys.
262
263
  # The first update after starting the watch is nil in case
263
264
  # there are no pending updates.
264
265
  def watch(keys, params = {})
@@ -267,13 +268,18 @@ module NATS
267
268
  params[:ignore_deletes] ||= false
268
269
  params[:idle_heartbeat] ||= 5 # seconds
269
270
  params[:inactive_threshold] ||= 5 * 60 # 5 minutes
270
- subject = "#{@pre}#{keys}"
271
+ subject = if keys.is_a?(Array)
272
+ keys = [">"] if keys.empty?
273
+ keys.map { |key| "#{@pre}#{key}" }
274
+ else
275
+ "#{@pre}#{keys}"
276
+ end
271
277
  init_setup = new_cond
272
278
  init_setup_done = false
273
279
  nc = @js.nc
274
280
  watcher = KeyWatcher.new(@js)
275
281
 
276
- deliver_policy = if !(params[:include_history])
282
+ deliver_policy = if !params[:include_history]
277
283
  "last_per_subject"
278
284
  end
279
285
 
@@ -294,7 +300,7 @@ module NATS
294
300
  }
295
301
 
296
302
  # watch_updates callback.
297
- sub = @js.subscribe(subject, config: ordered) do |msg|
303
+ sub = @js.subscribe(subject, stream: @stream, config: ordered) do |msg|
298
304
  synchronize do
299
305
  if !init_setup_done
300
306
  init_setup.wait(@js.opts[:timeout])
@@ -327,15 +333,9 @@ module NATS
327
333
  meta = msg.metadata
328
334
  watcher.synchronize { watcher._active = true }
329
335
  # Track the sequences
330
- #
331
- # $JS.ACK.KV_TEST.CKRGrWpf.1.10.10.1739859923871837000.0
332
- #
333
- tokens = msg.reply.split(".")
334
- sseq = tokens[5]
335
- dseq = tokens[6]
336
336
  watcher.synchronize do
337
- watcher._dseq = dseq.to_i + 1
338
- watcher._sseq = sseq.to_i
337
+ watcher._dseq = meta.sequence.consumer + 1
338
+ watcher._sseq = meta.sequence.stream
339
339
  end
340
340
 
341
341
  # Keys() handling
@@ -385,6 +385,9 @@ module NATS
385
385
  begin
386
386
  cinfo = sub.consumer_info
387
387
  stream_name = cinfo.stream_name
388
+ # Snapshot the keys filter too: subscribe set it on the consumer,
389
+ # not on ordered, and a recreated consumer needs it.
390
+ filter = cinfo.config.to_h.slice(:filter_subject, :filter_subjects)
388
391
 
389
392
  synchronize do
390
393
  init_setup_done = true
@@ -423,9 +426,11 @@ module NATS
423
426
  current
424
427
  }
425
428
  if !active
426
- ccreq = ordered.dup
429
+ ccreq = ordered.merge(filter)
427
430
  ccreq[:deliver_policy] = "by_start_sequence"
428
- ccreq[:opt_start_seq] = watcher._sseq
431
+ # Resume after the last entry. With none yet, that is the start of
432
+ # the stream, so earlier revisions of the keys are replayed too.
433
+ ccreq[:opt_start_seq] = watcher._sseq + 1
429
434
  ccreq[:deliver_subject] = deliver_subject
430
435
  ccreq[:idle_heartbeat] = ordered[:idle_heartbeat]
431
436
  ccreq[:inactive_threshold] = ordered[:inactive_threshold]
@@ -465,6 +470,7 @@ module NATS
465
470
  class KeyWatcher
466
471
  include MonitorMixin
467
472
  include Enumerable
473
+
468
474
  attr_accessor :received, :pending, :_sub, :_updates, :_init_done, :_watcher_cond
469
475
  attr_accessor :_sseq, :_dseq, :_active, :_hb_task
470
476
 
@@ -17,7 +17,7 @@
17
17
  module NATS
18
18
  module Protocol
19
19
  MSG = /\AMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n/i
20
- HMSG = /\AHMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?([\d]+)\s+(\d+)\r\n/i
20
+ HMSG = /\AHMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\s+(\d+)\r\n/i
21
21
  OK = /\A\+OK\s*\r\n/i
22
22
  ERR = /\A-ERR\s+('.+')?\r\n/i
23
23
  PING = /\APING\s*\r\n/i
@@ -98,12 +98,12 @@ module NATS
98
98
  return unless @needed && @buf.bytesize >= (@needed + CR_LF_SIZE)
99
99
  if @header_needed
100
100
  hbuf = @buf.slice(0, @header_needed)
101
- payload = @buf.slice(@header_needed, (@needed - @header_needed))
101
+ payload = @buf.slice(@header_needed, @needed - @header_needed)
102
102
  @nc.send(:process_msg, @sub, @sid, @reply, payload, hbuf)
103
103
  else
104
104
  @nc.send(:process_msg, @sub, @sid, @reply, @buf.slice(0, @needed), nil)
105
105
  end
106
- @buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)
106
+ @buf = @buf.slice(@needed + CR_LF_SIZE, @buf.bytesize)
107
107
 
108
108
  @sub = @sid = @reply = @needed = @header_needed = nil
109
109
  @parse_state = AWAITING_CONTROL_LINE
@@ -89,18 +89,19 @@ module NATS
89
89
  def next_msg(opts = {})
90
90
  timeout = opts[:timeout] ||= 0.5
91
91
  synchronize do
92
- return @pending_queue.pop if !@pending_queue.empty?
92
+ if @pending_queue.empty?
93
+ # Wait for a bit until getting a signal.
94
+ MonotonicTime.with_nats_timeout(timeout) do
95
+ wait_for_msgs_cond.wait(timeout)
96
+ end
93
97
 
94
- # Wait for a bit until getting a signal.
95
- MonotonicTime.with_nats_timeout(timeout) do
96
- wait_for_msgs_cond.wait(timeout)
98
+ raise NATS::Timeout if @pending_queue.empty?
97
99
  end
98
100
 
99
- if !@pending_queue.empty?
100
- return @pending_queue.pop
101
- else
102
- raise NATS::Timeout
103
- end
101
+ # Decrease pending size since consumed already
102
+ msg = @pending_queue.pop
103
+ self.pending_size -= msg.data.size
104
+ msg
104
105
  end
105
106
  end
106
107
 
@@ -148,13 +149,20 @@ module NATS
148
149
  msg = pending_queue.pop(true)
149
150
  process(msg)
150
151
  rescue ThreadError # queue is empty
151
- concurrency_semaphore.release
152
+ # No release here: the ensure below releases the permit; a second
153
+ # release would grow the semaphore beyond processing_concurrency.
152
154
  ensure
153
155
  concurrency_semaphore.release
154
156
  [concurrency_semaphore.available_permits, pending_queue.size].min.times do
155
157
  enqueue_processing(executor)
156
158
  end
157
159
  end
160
+ rescue Concurrent::RejectedExecutionError
161
+ # The executor is being shut down (the connection is closing,
162
+ # draining or reconnecting). Release the permit so the
163
+ # subscription can process messages again after a reconnect;
164
+ # the message stays in pending_queue.
165
+ concurrency_semaphore.release
158
166
  end
159
167
  end
160
168
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2016-2025 The NATS Authors
3
+ # Copyright 2016-2026 The NATS Authors
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
6
6
  # You may obtain a copy of the License at
@@ -17,7 +17,7 @@
17
17
  module NATS
18
18
  module IO
19
19
  # VERSION is the version of the client announced on CONNECT to the server.
20
- VERSION = "2.5.0"
20
+ VERSION = "2.7.0"
21
21
 
22
22
  # LANG is the lang runtime of the client announced on CONNECT to the server.
23
23
  LANG = "#{RUBY_ENGINE}#{RUBY_VERSION}".freeze
@@ -47,23 +47,28 @@ module NATS
47
47
  end
48
48
 
49
49
  def read(max_bytes = MAX_SOCKET_READ_BYTES, deadline = nil)
50
- data = super
51
- @frame << data
52
- result = []
53
- while (msg = @frame.next)
54
- result << msg
50
+ # Hand over what read_line decoded beyond its last line first,
51
+ # otherwise those bytes are lost once the read loop takes over.
52
+ if @line_buf && !@line_buf.empty?
53
+ return @line_buf.slice!(0, @line_buf.bytesize)
55
54
  end
56
- result.join
55
+
56
+ read_frames(max_bytes, deadline)
57
57
  end
58
58
 
59
59
  def read_line(deadline = nil)
60
- data = super
61
- @frame << data
62
- result = []
63
- while (msg = @frame.next)
64
- result << msg
60
+ @line_buf ||= +""
61
+ loop do
62
+ if (idx = @line_buf =~ /\r?\n/)
63
+ return @line_buf.slice!(0, idx + Regexp.last_match(0).length)
64
+ end
65
+
66
+ # Pull more data from the wire; never from @line_buf itself,
67
+ # or a partial line would be fed back forever.
68
+ data = read_frames(MAX_SOCKET_READ_BYTES, deadline)
69
+ return nil unless data
70
+ @line_buf << data
65
71
  end
66
- result.join
67
72
  end
68
73
 
69
74
  def write(data, deadline = nil)
@@ -72,6 +77,21 @@ module NATS
72
77
  frame = ::WebSocket::Frame::Outgoing::Client.new(data: data, type: :binary, version: @handshake.version)
73
78
  super(frame.to_s)
74
79
  end
80
+
81
+ private
82
+
83
+ # Reads from the socket and returns the payloads of all complete
84
+ # frames decoded so far.
85
+ def read_frames(max_bytes, deadline)
86
+ data = Socket.instance_method(:read).bind_call(self, max_bytes, deadline)
87
+ @frame << data
88
+ [].tap do |parts|
89
+ while (msg = @frame.next)
90
+ payload = msg.respond_to?(:data) ? msg.data : msg
91
+ parts << payload if payload
92
+ end
93
+ end.join
94
+ end
75
95
  end
76
96
  end
77
97
  end
@@ -6,8 +6,8 @@ module NATS
6
6
  REGEX = {
7
7
  name: /[A-Za-z0-9\-_]+$/,
8
8
  version: /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,
9
- subject: /^[^ >]*[>]?$/,
10
- queue: /^[^ >]*[>]?$/
9
+ subject: /^[^ >]*>?$/,
10
+ queue: /^[^ >]*>?$/
11
11
  }.freeze
12
12
 
13
13
  class << self
@@ -7,18 +7,27 @@ module NATS
7
7
  class SequenceInfo < Struct[Integer]
8
8
  end
9
9
 
10
- class ConsumerInfo < Struct[String | NATS::JetStream::API::ConsumerConfig | NATS::JetStream::API::SequenceInfo | Integer | Hash[Symbol, untyped]]
10
+ class PriorityGroupState < Struct[String | Time]
11
11
  end
12
12
 
13
- class ConsumerConfig < Struct[String | Integer]
13
+ class ConsumerInfo < Struct[String | NATS::JetStream::API::ConsumerConfig | NATS::JetStream::API::SequenceInfo | Integer | Hash[Symbol, untyped] | Time | bool | Array[NATS::JetStream::API::PriorityGroupState]]
14
+ end
15
+
16
+ class ConsumerConfig < Struct[String | Integer | Time | Array[String]]
14
17
  def to_json: (*Hash[untyped, untyped]) -> String
15
18
  end
16
19
 
20
+ class ConsumerPauseResponse < Struct[bool | Time | Integer]
21
+ end
22
+
23
+ class ConsumerResetResponse < Struct[NATS::JetStream::API::ConsumerInfo | Integer]
24
+ end
25
+
17
26
  class StreamConfig < Struct[String | Hash[Symbol, untyped] | NATS::JetStream::API::StreamState | bool | Integer]
18
27
  def to_json: (*Hash[untyped, untyped]) -> String
19
28
  end
20
29
 
21
- class StreamInfo < Struct[String | Hash[Symbol, untyped]]
30
+ class StreamInfo < Struct[String | Hash[Symbol, untyped] | Array[Hash[Symbol, untyped]] | Time]
22
31
  end
23
32
 
24
33
  class StreamState < Struct[Integer]
@@ -49,6 +49,18 @@ module NATS
49
49
 
50
50
  class BadRequest < NATS::JetStream::Error::APIError
51
51
  end
52
+
53
+ class ConsumerAlreadyExists < NATS::JetStream::Error::BadRequest
54
+ end
55
+
56
+ class ConsumerDoesNotExist < NATS::JetStream::Error::BadRequest
57
+ end
58
+
59
+ class ConsumerInvalidReset < NATS::JetStream::Error::BadRequest
60
+ end
61
+
62
+ class PinIdMismatch < NATS::JetStream::Error::APIError
63
+ end
52
64
  end
53
65
  end
54
66
  end
@@ -0,0 +1,33 @@
1
+ module NATS
2
+ class JetStream
3
+ module Header
4
+ MSG_ID: 'Nats-Msg-Id'
5
+ EXPECTED_STREAM: 'Nats-Expected-Stream'
6
+ EXPECTED_LAST_SEQUENCE: 'Nats-Expected-Last-Sequence'
7
+ EXPECTED_LAST_SUBJECT_SEQUENCE: 'Nats-Expected-Last-Subject-Sequence'
8
+ EXPECTED_LAST_SUBJECT_SEQUENCE_SUBJECT: 'Nats-Expected-Last-Subject-Sequence-Subject'
9
+ EXPECTED_LAST_MSG_ID: 'Nats-Expected-Last-Msg-Id'
10
+ ROLLUP: 'Nats-Rollup'
11
+ MSG_TTL: 'Nats-TTL'
12
+ MARKER_REASON: 'Nats-Marker-Reason'
13
+ STREAM: 'Nats-Stream'
14
+ SUBJECT: 'Nats-Subject'
15
+ SEQUENCE: 'Nats-Sequence'
16
+ TIME_STAMP: 'Nats-Time-Stamp'
17
+ LAST_SEQUENCE: 'Nats-Last-Sequence'
18
+ STREAM_SOURCE: 'Nats-Stream-Source'
19
+ BATCH_ID: 'Nats-Batch-Id'
20
+ BATCH_SEQUENCE: 'Nats-Batch-Sequence'
21
+ BATCH_COMMIT: 'Nats-Batch-Commit'
22
+ SCHEDULE: 'Nats-Schedule'
23
+ SCHEDULE_TARGET: 'Nats-Schedule-Target'
24
+ SCHEDULE_SOURCE: 'Nats-Schedule-Source'
25
+ SCHEDULE_TTL: 'Nats-Schedule-TTL'
26
+ SCHEDULE_TIME_ZONE: 'Nats-Schedule-Time-Zone'
27
+ SCHEDULE_ROLLUP: 'Nats-Schedule-Rollup'
28
+ SCHEDULER: 'Nats-Scheduler'
29
+ SCHEDULE_NEXT: 'Nats-Schedule-Next'
30
+ REQUIRED_API_LEVEL: 'Nats-Required-Api-Level'
31
+ end
32
+ end
33
+ end
@@ -11,6 +11,7 @@ module NATS
11
11
  ExpectedLastMsgID: 'Nats-Expected-Last-Msg-Id'
12
12
  LastConsumerSeq: 'Nats-Last-Consumer'
13
13
  LastStreamSeq: 'Nats-Last-Stream'
14
+ PinId: 'Nats-Pin-Id'
14
15
  end
15
16
  end
16
17
  end
@@ -6,6 +6,7 @@ module NATS
6
6
  NoMsgs: '404'
7
7
  NotFound: '404'
8
8
  RequestTimeout: '408'
9
+ PinIdMismatch: '423'
9
10
  ServiceUnavailable: '503'
10
11
  end
11
12
  end
@@ -6,11 +6,13 @@ module NATS
6
6
  # class << self
7
7
  def self.next_req_to_json: (Hash[Symbol, untyped]) -> String
8
8
 
9
+ def self.parse_time: (String?) -> Time?
10
+
9
11
  def self.is_status_msg: (NATS::Msg) -> bool
10
12
 
11
13
  def self.check_503_error: (NATS::Msg) -> void
12
14
 
13
- def self.from_msg: (NATS::Msg) -> NATS::JetStream::API::Error
15
+ def self.from_msg: (NATS::Msg) -> (NATS::JetStream::API::Error | NATS::JetStream::Error::PinIdMismatch)
14
16
 
15
17
  def self.from_error: (Hash[Symbol, untyped]) -> (
16
18
  NATS::JetStream::Error::ServiceUnavailable |
@@ -9,12 +9,24 @@ module NATS
9
9
 
10
10
  def delete_stream: (String, ?Hash[Symbol, untyped]) -> bool
11
11
 
12
- def add_consumer: (String, JetStream::API::ConsumerConfig, ?Hash[Symbol, untyped]) -> JetStream::API::ConsumerInfo
12
+ def add_consumer: (String, JetStream::API::ConsumerConfig | Hash[Symbol, untyped], ?Hash[Symbol, untyped]) -> JetStream::API::ConsumerInfo
13
+
14
+ def create_consumer: (String, JetStream::API::ConsumerConfig | Hash[Symbol, untyped], ?Hash[Symbol, untyped]) -> JetStream::API::ConsumerInfo
15
+
16
+ def update_consumer: (String, JetStream::API::ConsumerConfig | Hash[Symbol, untyped], ?Hash[Symbol, untyped]) -> JetStream::API::ConsumerInfo
13
17
 
14
18
  def consumer_info: (String, String, ?Hash[Symbol, untyped]) -> JetStream::API::ConsumerInfo
15
19
 
16
20
  def delete_consumer: (String, String, ?Hash[Symbol, untyped]) -> bool
17
21
 
22
+ def pause_consumer: (String, String, Time | String, ?Hash[Symbol, untyped]) -> JetStream::API::ConsumerPauseResponse
23
+
24
+ def resume_consumer: (String, String, ?Hash[Symbol, untyped]) -> JetStream::API::ConsumerPauseResponse
25
+
26
+ def unpin_consumer: (String, String, String, ?Hash[Symbol, untyped]) -> bool
27
+
28
+ def reset_consumer: (String, String, ?Hash[Symbol, untyped]) -> JetStream::API::ConsumerResetResponse
29
+
18
30
  def find_stream_name_by_subject: (String, ?Hash[Symbol, untyped]) -> String
19
31
 
20
32
  def get_msg: (String, ?Hash[Symbol, untyped]) -> JetStream::API::RawStreamMsg
@@ -25,9 +37,17 @@ module NATS
25
37
 
26
38
  private
27
39
 
40
+ def upsert_consumer: (String, JetStream::API::ConsumerConfig | Hash[Symbol, untyped], String?, Hash[Symbol, untyped]) -> JetStream::API::ConsumerInfo
41
+
42
+ def request_pause: (String, String, Hash[Symbol, String], Hash[Symbol, untyped]) -> JetStream::API::ConsumerPauseResponse
43
+
44
+ def rfc3339: (Time | String | nil) -> String?
45
+
28
46
  def api_request: (String, String, ?Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
29
47
 
30
48
  def _lift_msg_to_raw_msg: (NATS::Msg) -> JetStream::API::RawStreamMsg
49
+
50
+ def stream_config_json: (JetStream::API::StreamConfig) -> String
31
51
  end
32
52
  end
33
53
  end