nats-pure 2.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d5c22758be6db2c05382a55c718c2c4f836a2c723f77820d4728bbf1538f30b
4
- data.tar.gz: 1745fb8cfa406b598099779a027a3cc3dcc4cee28c22fd7c08e1b156b6ce4a5b
3
+ metadata.gz: 18d1e6a53bcd6c3dc5c3fe1aa9f03c5b1d17a68fdf6381bae8f7ef3c6a6995b3
4
+ data.tar.gz: ce0f4719c5902ad9b7bec424f341cab7f39a980de31452fe445fbe6733a401d0
5
5
  SHA512:
6
- metadata.gz: 84b049092f60f34c3ca6b5a8ac2b4334a1a32dbaefd2659424f91db3472df105604bbddd4989793a56757a9cdee1c710db6b52fc02e40b95f64bc0b0eae21dd6
7
- data.tar.gz: 38585616a5820faefe491c8c16d815bc52759e0cd2b7f77ab5b3a0227d3728720c4f1ac56d6d2af6354c2c53e54245ab48ce71c2a063efe3fe737e60e21c6af7
6
+ metadata.gz: 48591880c765700baac0ab6762fc437ef1da21cd728c43f64886547da8607208f8b68599cb75802ca3bc82d1e10e6c3875c9eac9c86e9ec955f035049d4e25db
7
+ data.tar.gz: 6de527742e1cb8a380f2806dae0f365612f7a6dbc622ebda50bc9cb46d3a4221dc296308c4c82b9c72c108dc0ff5462138f31aaf8898429d1028e1b7b2c50573
data/CHANGELOG.md CHANGED
@@ -2,6 +2,69 @@
2
2
 
3
3
  ## main
4
4
 
5
+ ## v2.7.0
6
+
7
+ This release adds the JetStream features of nats-server 2.10 to 2.14 that the
8
+ client lacked, such as per-message TTLs, message schedules, and consumers that
9
+ can be paused, reset and serve priority groups. `fetch` now pulls once and
10
+ waits, like `Fetch` of the nats.go `jetstream` package, and a subscription read
11
+ with `next_msg` no longer drops every message after its first 64 MiB. Code that
12
+ passed stream or KV settings the client used to drop now sends them; see Changed.
13
+
14
+ ### Added
15
+
16
+ - JetStream: `fetch(batch, no_wait: true)` takes what the server delivers at once, without waiting for more messages, like `FetchNoWait` of nats.go and `fetch` of nats.rs: an empty Array when none are pending, or when other pulls wait for more than are pending. While the consumer may not deliver more until some are acked (`max_ack_pending`), the server holds the pull back, and the fetch returns what it got once its timeout is up, 1 second by default. (#195)
17
+ - JetStream: `fetch` takes a block, to which it passes each delivery as it comes, in the calling thread, so that the message can be acked before the `ack_wait` of the consumer is up: `psub.fetch(100, timeout: 60) { |msg| msg.ack }`. It still returns the messages. (#195)
18
+ - JetStream: stream configs take the settings added in nats-server 2.10: `compression`, `first_seq`, `subject_transform` and `consumer_limits` (whose `inactive_threshold` is in nanoseconds, like the other stream durations). They used to be dropped, so a stream could not be created with them, and sending a fetched config back as an update left them out. (#191)
19
+ - JetStream: stream configs take the settings added in nats-server 2.11 to 2.14: `allow_msg_ttl` and `subject_delete_marker_ttl` (2.11; the latter in nanoseconds, like the other stream durations), `allow_msg_counter`, `allow_atomic`, `allow_msg_schedules` and `persist_mode` (2.12), and `allow_batched` (2.14). Like nats.go, the client does not send them at their defaults, such as `false`, as servers since 2.12, and 2.11 ones in strict mode, refuse settings they do not know. (#194)
20
+ - JetStream: `NATS::JetStream::Header` names the JetStream headers: those for deduplication and optimistic concurrency, such as `MSG_ID` and `EXPECTED_LAST_SUBJECT_SEQUENCE`, and those of nats-server 2.11 to 2.14: `EXPECTED_LAST_SUBJECT_SEQUENCE_SUBJECT`, `MSG_TTL` and `MARKER_REASON` (2.11), the atomic batch headers (2.12; `BATCH_COMMIT` `"eob"` needs 2.14), the message schedule headers (2.12 and 2.14), and `REQUIRED_API_LEVEL` (2.12), which JetStream API requests send as a `header:` option, as in `js.stream_info(name, header: {...})`. It also names the headers that the server sets on the messages it republishes, sources or returns from direct gets, such as `STREAM` and `SEQUENCE`. (#194)
21
+ - JetStream: `js.publish` takes `ttl:`, the whole seconds after which the stream removes the message, or `:never` to keep it past the stream's `max_age`, for streams with `allow_msg_ttl` (nats-server 2.11). Other values raise `ArgumentError`. (#194)
22
+ - JetStream: `js.publish` takes `schedule:`, which makes the message a schedule that publishes it to its `target:` subject: once, `at:` a Time (nats-server 2.12), `every:` so many seconds, or on a `cron:` expression with seconds, in UTC or a `time_zone:` (2.14). The schedule can give the messages it publishes a `ttl:` (2.12), and publish the last message of a `source:` subject instead and roll up the target (`rollup: true`) (2.14; 2.12 ignores both). The stream needs `allow_msg_schedules`. An invalid schedule raises `ArgumentError`, before anything is sent; `NATS::JetStream::Header::SCHEDULE_NEXT` and `SCHEDULER` cancel a schedule. (#194)
23
+ - JetStream: `stream_info` reports the stream's `mirror` and `sources`, including their subject transforms, and both `stream_info` and `consumer_info` report when the server reported the info, as `ts`. (#191)
24
+ - JetStream: `create_consumer` and `update_consumer` use the create and update actions of nats-server 2.10. Creating a consumer that exists with a different config raises `NATS::JetStream::Error::ConsumerAlreadyExists`, and updating one that does not exist raises `NATS::JetStream::Error::ConsumerDoesNotExist`; both are `BadRequest` errors. An update replaces the whole config, so fields left out take their defaults. `add_consumer` still creates or updates. (#191)
25
+ - JetStream: `msg.term(reason: "...")` passes the reason on to the server's advisory for the terminated message. Servers before 2.10.4 do not terminate a message given a reason. (#191)
26
+ - JetStream: consumers can be paused (nats-server 2.11): created paused with `pause_until` in their config, as a `Time` or an RFC 3339 String, or paused and resumed with `pause_consumer` and `resume_consumer`; updating a consumer keeps its pause. `consumer_info` reports whether a consumer is `paused`, and the seconds until it resumes, as `pause_remaining`. Servers before 2.11 create consumers unpaused, and let `pause_consumer` and `resume_consumer` time out. (#193)
27
+ - JetStream: pull consumers can serve priority groups (nats-server 2.11). Consumer configs take `priority_policy`, `priority_groups` and `priority_timeout` (in seconds, like the other consumer durations), and `consumer_info` reports the state of the groups as `priority_groups`. `fetch` takes the `group` to pull from, `min_pending` and `min_ack_pending` for the `"overflow"` policy, and `priority` for the `"prioritized"` policy of nats-server 2.12. (#193)
28
+ - JetStream: with the `"pinned_client"` priority policy, a pull subscription keeps the pin id that the server delivers with its messages, and sends it with its pulls. When the server says that the subscription is no longer pinned, as its pin expired or it was unpinned with the new `unpin_consumer`, a fetch that got no messages raises `NATS::JetStream::Error::PinIdMismatch`, and the next fetch can be pinned again. The server counts the consumer's `priority_timeout` from each pull, not while a pull waits, so keep the fetch timeout, plus the time between fetches, below it, or the subscription loses its pin. (#193, #195)
29
+ - JetStream: `reset_consumer` makes a consumer deliver again from after its ack floor, or from the stream sequence given as `seq` (nats-server 2.14). A sequence that the deliver policy of the consumer does not let it be reset to raises `NATS::JetStream::Error::ConsumerInvalidReset`, a `BadRequest` error, and one that is not an integer of 0 or more raises `ArgumentError`. For a stream or consumer that does not exist, and with servers before 2.14, the request times out. (#193)
30
+ - JetStream: `stream_info` and `consumer_info` report the `cluster` of the stream or consumer as a Hash, including when its leader was elected, as `leader_since` (nats-server 2.12). `consumer_info` used to drop it. (#193)
31
+ - KV: buckets can be created with `compression: true` and with `metadata`, which `status.compressed?` and `status.metadata` report. (#191)
32
+ - KV: `watch` takes an Array of keys, watched by a single consumer (nats-server 2.10). An empty Array watches all keys. (#191)
33
+
34
+ ### Changed
35
+
36
+ - JetStream: `fetch` pulls once, for the whole batch, and waits: it returns as soon as it has the batch, and otherwise the messages that came when its timeout is up, like `Fetch` of the nats.go `jetstream` package and `batch` of nats.rs. A fetch of more than one message used to ask first, without waiting, for the messages that were pending. When it got some, it then waited out its timeout without pulling for more, so it neither took the messages that came meanwhile nor returned sooner. When other pulls of the consumer waited for more messages than were pending, the server turned that first pull away (408 Requests Pending), and `fetch` raised `NATS::IO::Timeout` at once. (#195)
37
+ - JetStream: a pull subscription subscribes to `<inbox>.*`, which `subject` now returns, and each pull gets a reply subject of its own under it, as ADR-13 describes, so that a fetch tells the status that ended its own pull from those that ended the pulls of other fetches. A `fetch` with `no_wait: true` pulls with a subscription of its own, which it drops when done, so that its pull does not linger when the server holds it, as while the consumer may not deliver more messages until some are acked (`max_ack_pending`): the fetch then returns what it got once its timeout is up, and leaves what comes later to the next fetch. A server with leafnodes or gateways delivers to a pull until it is 2 seconds old even once the subscription is gone, so the subscription stays until then. (#195)
38
+ - JetStream: `fetch` raises `ArgumentError` for a `timeout` that is not a finite positive number, such as 0, with which it used to raise `NATS::IO::Timeout` at once. It leaves the options it is given unchanged; it used to set their `:timeout`. (#195)
39
+ - JetStream: as `fetch` always pulls with an expiry of its timeout, a consumer whose `max_expires` is shorter than the timeout turns every fetch away, with 409 "Exceeded MaxRequestExpires", raised as `NATS::JetStream::API::Error`. A fetch of more than one message used to get the messages that were pending first, with a pull that had no expiry. Pass a `timeout` no longer than the consumer's `max_expires`. (#195)
40
+ - JetStream: `fetch` returns when the server ends its pull, so it takes its timeout plus the round trip to the server. When no end comes, it waits up to 1 second longer, like nats.go, before it returns what it got or raises `NATS::IO::Timeout`. (#195)
41
+ - JetStream and KV: settings that used to be dropped are now sent. Code that already passed `compression`, `first_seq`, `subject_transform` or `consumer_limits` to `add_stream`, or `compression` or `metadata` to `create_key_value`, created its streams and buckets without them, so calling it again for an existing stream or bucket now fails with "stream name already in use with a different configuration" (err_code 10058). Apply the setting with `update_stream`, or stop passing it. (#191)
42
+ - JetStream: the same goes for the settings of nats-server 2.11 to 2.14: code that passed them to `add_stream` created its streams without them, so calling it again for an existing stream now fails with err_code 10058. `update_stream` applies them, except `allow_msg_counter` and `persist_mode`, which a stream cannot change. (#194)
43
+ - JetStream: consumer configs now send `pause_until`, `priority_policy`, `priority_groups` and `priority_timeout`, which used to be dropped. As `add_consumer` creates or updates, code that already passed the priority settings to it now gives an existing consumer priority groups, after which the server turns away pulls that name no group (400 "Priority Group missing"). (#193)
44
+ - KV: `create_key_value` takes only `true` or `false` for `compression`; anything else, such as `"s2"`, which used to be ignored, raises `ArgumentError`. (#191)
45
+ - JetStream: `add_consumer` leaves the `ConsumerConfig` it is given unchanged. It used to set its `name` and a default `ack_policy`, and convert its durations to nanoseconds, so passing the same config again sent durations a billion times too long. (#191)
46
+ - JetStream: two `stream_info` or `consumer_info` results are no longer `==` unless they were reported at the same time, as they now carry `ts`, and the `cluster`, `mirror` and `sources`, whose lag and activity change all the time, even with servers that do not report `ts`. (#191, #193)
47
+ - KV: `watch` no longer looks up the bucket's stream before creating its consumer. (#191)
48
+
49
+ ### Fixed
50
+
51
+ - JetStream: `update_stream` with the config from `stream_info` failed with err_code 10052, such as "message TTL status can not be disabled", for streams with per-message TTLs, subject delete markers, counters, message schedules or async persistence, and turned off atomic and fast batches, as `StreamConfig` dropped the settings of nats-server 2.11 to 2.14. It now keeps them. (#194)
52
+ - JetStream: `StreamConfig` dropped `discard_new_per_subject`, so `add_stream` created streams without it, and `update_stream` with the config from `stream_info` turned it off: a subject that held `max_msgs_per_subject` messages then discarded its oldest message instead of refusing the new one. It now keeps it. (#199)
53
+ - JetStream: `js.publish` with `stream:` added `Nats-Expected-Stream` to the header Hash it was given, so a caller that reused the Hash sent it with its later publishes. (#194)
54
+ - JetStream: `js.publish` raised `ArgumentError: unknown keywords` after the server had stored the message, when publishing to a stream with counters or committing an atomic batch, as the acks of nats-server 2.12 carry `val`, `batch` and `count`. `PubAck` now has these fields and ignores any it does not know, so fields added to acks later cannot break publish. (#192, #177, thanks @jnowakplacewise)
55
+ - JetStream: `add_consumer`, `consumer_info`, `subscribe` and `pull_subscribe` raised `NoMethodError` for consumers that do not ack (`ack_policy: "none"`) created without an `ack_wait`, after the consumer had been created. The server omits `ack_wait` for them, and `config.ack_wait` is now `nil`. (#192)
56
+ - JetStream: `fetch` took a status that an earlier pull had left in the subscription, such as the 408 of a fetch that timed out, for the reply to its own pull, and raised `NATS::IO::Timeout` at once, even with messages pending: a fetch of more than one message with any such status, a fetch of one message with a second one. `fetch` now first takes what earlier pulls left, and pulls for the rest only. And a fetch that got messages before an error status, such as 409 Consumer Deleted, raised the error and dropped them; like nats.go, it now returns them. (#193, #195)
57
+ - JetStream: when threads fetched from the same subscription at once, a fetch could take the 408 that ended the pull of another for the end of its own, and raise `NATS::IO::Timeout` before its timeout. (#195)
58
+ - JetStream: `fetch` pulled with an expiry a little short of its timeout only after a first pull, which did not wait, came back empty, so its pull outlived it by the time that first pull took: a message delivered meanwhile waited in the subscription for the next fetch, and, if none came, was delivered again once the `ack_wait` was up. A fetch now waits for the server to end its pull, which expires with its timeout. (#195)
59
+ - JetStream: a `fetch` that waited longer than the `ack_wait` of the consumer returned a message twice when the server delivered it again meanwhile. It now returns each message once, as last delivered. (#195)
60
+ - JetStream: `fetch` took a message of the stream with a `Status` header for a status of the server, and raised an error or ended without it. Statuses are now only the messages with a `Status` header that the server sends without a reply. (#195)
61
+ - JetStream: a `fetch` of one message raised `TypeError: nil can't be coerced into Float` when threads fetched from the same subscription at once: woken up for a message that another thread took, it did not wait on. It now waits until its timeout. (#193, #181, thanks @route)
62
+ - JetStream: a `fetch` of one message raised `NATS::IO::Timeout` ("nats: fetch request timeout") before its timeout when a status that ended an earlier pull, such as the 408 of a fetch that had just timed out, came while it waited, and left its own message for the next fetch. Like nats.go, it now waits on for its message until its timeout. (#193)
63
+ - JetStream: `fetch` could miss the reply to its pull when it came before the fetch waited for it, as when its thread was preempted: a fetch of one message then waited out its timeout and raised `NATS::IO::Timeout`, leaving its message to be redelivered after the ack wait, and a fetch of more messages returned only the first one, after its timeout. `fetch` now takes what is there before it waits. (#193)
64
+ - KV: after its consumer was recreated, as after a server restart, a watcher yielded the entries of every key in the bucket instead of the watched keys. The recreated consumer now keeps the keys filter. (#192)
65
+ - KV: after its consumer was recreated, a watcher yielded its last entry again, and a watcher with no entries yet, such as one on an empty bucket, never got its consumer back (`err_code` 10094) and stayed silent. The recreated consumer now starts after the last entry. (#192, #175, thanks @Xayc73)
66
+ - A subscription without a callback, read with `next_msg`, dropped every message and reported `NATS::IO::SlowConsumer` once it had received its `pending_bytes_limit` in all, 64 MiB by default, however fast it was read, as `next_msg` did not take the messages it returned off the pending bytes. (#196)
67
+
5
68
  ## v2.6.0
6
69
 
7
70
  This release prepares nats-pure for nats-server 2.16, whose new JetStream
data/README.md CHANGED
@@ -102,6 +102,89 @@ loop do
102
102
  end
103
103
  ```
104
104
 
105
+ `fetch` returns as soon as it has the batch, or once its timeout is up, the
106
+ messages that came. With `no_wait: true`, it takes what the server delivers at
107
+ once, without waiting for more. Given a block, it passes each message to it as it comes,
108
+ so that the message can be acked before the `ack_wait` of the consumer is up:
109
+
110
+ ```ruby
111
+ psub.fetch(100, timeout: 60) do |msg|
112
+ puts msg.data
113
+ msg.ack
114
+ end
115
+ ```
116
+
117
+ Messages can expire on their own, in streams that allow it (nats-server 2.11):
118
+
119
+ ```ruby
120
+ js.add_stream(name: "sessions", subjects: ["sessions.>"], allow_msg_ttl: true)
121
+
122
+ # Removed after an hour. With ttl: :never, kept past the max_age of the stream.
123
+ js.publish("sessions.42", "token", ttl: 3600)
124
+ ```
125
+
126
+ Messages can also be scheduled (nats-server 2.12; `every:` and `cron:` need 2.14):
127
+
128
+ ```ruby
129
+ js.add_stream(name: "reminders", subjects: ["reminders.>", "due.>"], allow_msg_schedules: true)
130
+
131
+ # Published to due.42 in an hour. Another schedule on reminders.42 replaces it.
132
+ js.publish("reminders.42", "call back", schedule: {at: Time.now + 3600, target: "due.42"})
133
+
134
+ # Published to due.standup at 9:00 on weekdays, Warsaw time.
135
+ js.publish("reminders.standup", "stand-up",
136
+ schedule: {cron: "0 0 9 * * 1-5", time_zone: "Europe/Warsaw", target: "due.standup"})
137
+
138
+ # Cancels the schedule on reminders.42.
139
+ js.publish("reminders.cancelled", "", header: {
140
+ NATS::JetStream::Header::SCHEDULE_NEXT => "purge",
141
+ NATS::JetStream::Header::SCHEDULER => "reminders.42"
142
+ })
143
+ ```
144
+
145
+ Streams and consumers can also be managed directly:
146
+
147
+ ```ruby
148
+ # Compressed, and no consumer may have more than 1000 unacknowledged messages.
149
+ js.add_stream(name: "orders", subjects: ["orders.>"], compression: "s2",
150
+ consumer_limits: {max_ack_pending: 1000})
151
+
152
+ config = {durable_name: "billing", filter_subject: "orders.paid", max_ack_pending: 100}
153
+
154
+ # Fails with NATS::JetStream::Error::ConsumerAlreadyExists if "billing"
155
+ # exists with a different config.
156
+ js.create_consumer("orders", config)
157
+
158
+ # An update replaces the whole config, so send all of it again.
159
+ # Fails with NATS::JetStream::Error::ConsumerDoesNotExist if there is no "billing".
160
+ js.update_consumer("orders", config.merge(max_ack_pending: 200))
161
+
162
+ # Deliver nothing for an hour, or until resumed.
163
+ js.pause_consumer("orders", "billing", Time.now + 3600)
164
+ js.resume_consumer("orders", "billing")
165
+ ```
166
+
167
+ Pull consumers can serve priority groups. With the `"pinned_client"` policy,
168
+ the server delivers to one subscription at a time, and to another once it
169
+ stops pulling for `priority_timeout` seconds. The server counts it from
170
+ each pull, not while a pull waits: keep the fetch timeout, 5 seconds by
171
+ default, plus the time between fetches, below `priority_timeout`, or the
172
+ subscription can lose its pin:
173
+
174
+ ```ruby
175
+ js.create_consumer("orders", durable_name: "shipping", filter_subject: "orders.paid",
176
+ priority_policy: "pinned_client", priority_groups: ["workers"], priority_timeout: 60)
177
+
178
+ psub = js.pull_subscribe("orders.paid", "shipping", stream: "orders")
179
+ begin
180
+ psub.fetch(5, group: "workers").each(&:ack)
181
+ rescue NATS::JetStream::Error::PinIdMismatch
182
+ # This subscription is no longer pinned; the next fetch may be pinned again.
183
+ rescue NATS::IO::Timeout
184
+ # Nothing to do, or another subscription is pinned.
185
+ end
186
+ ```
187
+
105
188
  ## Service API
106
189
 
107
190
  The service API allows you to easily [build NATS services](docs/service_api.md).
@@ -41,6 +41,27 @@ module NATS
41
41
  end
42
42
  end
43
43
 
44
+ # PriorityGroupState is the state of a priority group of a consumer
45
+ # (requires nats-server v2.11.0).
46
+ #
47
+ # @!attribute group
48
+ # @return [String] Name of the group.
49
+ # @!attribute pinned_client_id
50
+ # @return [String, nil] With the pinned_client priority policy, the
51
+ # pin id of the subscription that the group is pinned to.
52
+ # @!attribute pinned_ts
53
+ # @return [Time, nil] When the group was pinned.
54
+ PriorityGroupState = Struct.new(:group, :pinned_client_id, :pinned_ts,
55
+ keyword_init: true) do
56
+ def initialize(opts = {})
57
+ opts[:pinned_ts] = JS.parse_time(opts[:pinned_ts])
58
+ rem = opts.keys - members
59
+ opts.delete_if { |k| rem.include?(k) }
60
+ super
61
+ freeze
62
+ end
63
+ end
64
+
44
65
  # ConsumerInfo is the current status of a JetStream consumer.
45
66
  #
46
67
  # @!attribute stream_name
@@ -64,21 +85,43 @@ module NATS
64
85
  # @!attribute num_pending
65
86
  # @return [Integer]
66
87
  # @!attribute cluster
67
- # @return [Hash]
88
+ # The cluster of a clustered consumer, such as its leader, its
89
+ # replicas and, as leader_since, when the leader was elected
90
+ # (requires nats-server v2.12.0); nil on a standalone server. The
91
+ # values are the server's: leader_since is a String, and the
92
+ # active of the replicas is in nanoseconds.
93
+ # @return [Hash, nil]
94
+ # @!attribute ts
95
+ # When the server reported this info (requires nats-server v2.10.0).
96
+ # @return [Time]
97
+ # @!attribute paused
98
+ # True while the consumer is paused (requires nats-server v2.11.0).
99
+ # @return [Boolean, nil]
100
+ # @!attribute pause_remaining
101
+ # Seconds until a paused consumer resumes, rounded down, so 0 in its
102
+ # last second (requires nats-server v2.11.0).
103
+ # @return [Integer, nil]
104
+ # @!attribute priority_groups
105
+ # State of the consumer's priority groups (requires nats-server v2.11.0).
106
+ # @return [Array<PriorityGroupState>, nil]
68
107
  ConsumerInfo = Struct.new(:type, :stream_name, :name, :created,
69
108
  :config, :delivered, :ack_floor,
70
109
  :num_ack_pending, :num_redelivered, :num_waiting,
71
- :num_pending, :cluster, :push_bound,
110
+ :num_pending, :cluster, :push_bound, :ts,
111
+ :paused, :pause_remaining, :priority_groups,
72
112
  keyword_init: true) do
73
113
  def initialize(opts = {})
74
114
  opts[:created] = Time.parse(opts[:created])
115
+ opts[:ts] = Time.parse(opts[:ts]) if opts[:ts]
116
+ opts[:pause_remaining] = opts[:pause_remaining] / ::NATS::NANOSECONDS if opts[:pause_remaining]
117
+ opts[:priority_groups] = opts[:priority_groups].map { |state| PriorityGroupState.new(state) } if opts[:priority_groups]
75
118
  opts[:ack_floor] = SequenceInfo.new(opts[:ack_floor])
76
119
  opts[:delivered] = SequenceInfo.new(opts[:delivered])
77
- opts[:config][:ack_wait] = opts[:config][:ack_wait] / ::NATS::NANOSECONDS
120
+ opts[:config][:ack_wait] = opts[:config][:ack_wait] / ::NATS::NANOSECONDS if opts[:config][:ack_wait]
78
121
  opts[:config][:inactive_threshold] = opts[:config][:inactive_threshold] / ::NATS::NANOSECONDS if opts[:config][:inactive_threshold]
79
122
  opts[:config][:idle_heartbeat] = opts[:config][:idle_heartbeat] / ::NATS::NANOSECONDS if opts[:config][:idle_heartbeat]
123
+ opts[:config][:priority_timeout] = opts[:config][:priority_timeout] / ::NATS::NANOSECONDS if opts[:config][:priority_timeout]
80
124
  opts[:config] = ConsumerConfig.new(opts[:config])
81
- opts.delete(:cluster)
82
125
  # Filter unrecognized fields just in case.
83
126
  rem = opts.keys - members
84
127
  opts.delete_if { |k| rem.include?(k) }
@@ -96,7 +139,8 @@ module NATS
96
139
  # @!attribute ack_policy
97
140
  # @return [String]
98
141
  # @!attribute ack_wait
99
- # @return [Integer]
142
+ # Seconds; nil when the server omits it, as for consumers that do not ack.
143
+ # @return [Integer, nil]
100
144
  # @!attribute max_deliver
101
145
  # @return [Integer]
102
146
  # @!attribute replay_policy
@@ -105,6 +149,27 @@ module NATS
105
149
  # @return [Integer]
106
150
  # @!attribute max_ack_pending
107
151
  # @return [Integer]
152
+ # @!attribute pause_until
153
+ # Time until which the consumer delivers no messages, as a Time or
154
+ # as an RFC 3339 String; a fetched config has a String. The server
155
+ # takes it only when it creates the consumer: updates keep the pause,
156
+ # which pause_consumer and resume_consumer change. Requires
157
+ # nats-server v2.11.0; older ones create the consumer unpaused.
158
+ # @return [Time, String, nil]
159
+ # @!attribute priority_policy
160
+ # How the consumer serves the pulls of its priority groups: "none",
161
+ # "overflow", "pinned_client", or "prioritized" (requires
162
+ # nats-server v2.11.0, and v2.12.0 for "prioritized").
163
+ # @return [String, nil]
164
+ # @!attribute priority_groups
165
+ # Names of the consumer's priority groups, one of which each pull
166
+ # has to name (requires nats-server v2.11.0).
167
+ # @return [Array<String>, nil]
168
+ # @!attribute priority_timeout
169
+ # With the pinned_client priority policy, seconds after which a
170
+ # pinned subscription that stops pulling is unpinned
171
+ # (requires nats-server v2.11.0).
172
+ # @return [Integer, nil]
108
173
  ConsumerConfig = Struct.new(:name, :durable_name, :description,
109
174
  :deliver_policy, :opt_start_seq, :opt_start_time,
110
175
  :ack_policy, :ack_wait, :max_deliver, :backoff,
@@ -124,6 +189,8 @@ module NATS
124
189
  :mem_storage,
125
190
  # NATS v2.10 features
126
191
  :metadata, :filter_subjects, :max_bytes,
192
+ # NATS v2.11 features
193
+ :pause_until, :priority_policy, :priority_groups, :priority_timeout,
127
194
  keyword_init: true) do
128
195
  def initialize(opts = {})
129
196
  # Filter unrecognized fields just in case.
@@ -133,8 +200,54 @@ module NATS
133
200
  end
134
201
  end
135
202
 
203
+ # ConsumerPauseResponse is the result of pausing or resuming a consumer
204
+ # (requires nats-server v2.11.0).
205
+ #
206
+ # @!attribute paused
207
+ # @return [Boolean] Whether the consumer is paused.
208
+ # @!attribute pause_until
209
+ # @return [Time, nil] When the consumer resumes; nil once resumed. A time
210
+ # in the past does not pause the consumer, and paused is then false.
211
+ # @!attribute pause_remaining
212
+ # @return [Integer, nil] Seconds until the consumer resumes, rounded down,
213
+ # so 0 in its last second; nil when it is not paused.
214
+ ConsumerPauseResponse = Struct.new(:paused, :pause_until, :pause_remaining,
215
+ keyword_init: true) do
216
+ def initialize(opts = {})
217
+ opts[:pause_until] = JS.parse_time(opts[:pause_until])
218
+ opts[:pause_remaining] = opts[:pause_remaining] / ::NATS::NANOSECONDS if opts[:pause_remaining]
219
+ rem = opts.keys - members
220
+ opts.delete_if { |k| rem.include?(k) }
221
+ super
222
+ freeze
223
+ end
224
+ end
225
+
226
+ # ConsumerResetResponse is the result of resetting a consumer
227
+ # (requires nats-server v2.14.0).
228
+ #
229
+ # @!attribute info
230
+ # @return [ConsumerInfo] The consumer after the reset.
231
+ # @!attribute reset_seq
232
+ # @return [Integer] Stream sequence the consumer delivers from. The next
233
+ # message it delivers can come later, as the first one from there that
234
+ # matches its filter.
235
+ ConsumerResetResponse = Struct.new(:info, :reset_seq,
236
+ keyword_init: true) do
237
+ def initialize(opts = {})
238
+ reset_seq = opts[:reset_seq]
239
+ super(info: ConsumerInfo.new(opts), reset_seq: reset_seq)
240
+ freeze
241
+ end
242
+ end
243
+
136
244
  # StreamConfig represents the configuration of a stream from JetStream.
137
245
  #
246
+ # Settings left nil are not sent, and neither are the settings of
247
+ # nats-server 2.11 to 2.14 left at their defaults, such as false.
248
+ # Servers since v2.12.0, and v2.11 ones in strict mode, refuse the
249
+ # settings that they do not know.
250
+ #
138
251
  # @!attribute type
139
252
  # @return [String]
140
253
  # @!attribute config
@@ -165,12 +278,75 @@ module NATS
165
278
  # @return [Integer]
166
279
  # @!attribute discard
167
280
  # @return [String]
281
+ # @!attribute discard_new_per_subject
282
+ # Whether a subject that holds max_msgs_per_subject messages refuses
283
+ # new ones, instead of discarding its oldest. Needs discard "new" and
284
+ # max_msgs_per_subject; the server reports false as nil.
285
+ # @return [Boolean, nil]
168
286
  # @!attribute storage
169
287
  # @return [String]
170
288
  # @!attribute num_replicas
171
289
  # @return [Integer]
172
290
  # @!attribute duplicate_window
291
+ # Nanoseconds within which the stream stores a message only once for
292
+ # each Header::MSG_ID. Unless set, it is 2 minutes, except that
293
+ # mirrors, and since nats-server v2.14.0 streams with sources, get
294
+ # none: the messages published to them are not deduplicated.
295
+ # @return [Integer]
296
+ # @!attribute compression
297
+ # Storage compression of a file based stream, "s2" or "none"
298
+ # (requires nats-server v2.10.0).
299
+ # @return [String]
300
+ # @!attribute first_seq
301
+ # The sequence of the first message stored in a new stream
302
+ # (requires nats-server v2.10.0).
173
303
  # @return [Integer]
304
+ # @!attribute subject_transform
305
+ # Transform applied to the subject of every message stored, as
306
+ # `{src:, dest:}` (requires nats-server v2.10.0).
307
+ # @return [Hash]
308
+ # @!attribute consumer_limits
309
+ # Defaults and upper limits for the stream's consumers, as
310
+ # `{inactive_threshold:, max_ack_pending:}`. Like the other durations
311
+ # of a stream, inactive_threshold is in nanoseconds
312
+ # (requires nats-server v2.10.0).
313
+ # @return [Hash]
314
+ # @!attribute allow_msg_ttl
315
+ # Whether messages can be published with a TTL of their own
316
+ # (requires nats-server v2.11.0). Once enabled, it cannot be
317
+ # disabled, and before v2.11.2 it cannot be enabled by an update.
318
+ # @return [Boolean]
319
+ # @!attribute subject_delete_marker_ttl
320
+ # Nanoseconds to keep the marker that the server leaves when the last
321
+ # message of a subject expires, at least a second (requires
322
+ # nats-server v2.11.0). Before v2.11.2 it needs allow_msg_ttl. From
323
+ # v2.11.2 on, setting it enables allow_msg_ttl and allow_rollup_hdrs,
324
+ # and allows purges, so an update cannot set it on a stream that
325
+ # denies them.
326
+ # @return [Integer]
327
+ # @!attribute allow_msg_counter
328
+ # Whether the stream holds counters, which cannot change later
329
+ # (requires nats-server v2.12.0).
330
+ # @return [Boolean]
331
+ # @!attribute allow_atomic
332
+ # Whether messages can be published in atomic batches
333
+ # (requires nats-server v2.12.0).
334
+ # @return [Boolean]
335
+ # @!attribute allow_msg_schedules
336
+ # Whether messages can schedule messages (requires nats-server
337
+ # v2.12.0). Once enabled, it cannot be disabled. Setting it enables
338
+ # allow_rollup_hdrs and allows purges, so an update cannot set it on
339
+ # a stream that denies them.
340
+ # @return [Boolean]
341
+ # @!attribute persist_mode
342
+ # "async" to acknowledge messages before they are flushed to disk,
343
+ # or "default"; the server reports the default as nil. It cannot
344
+ # change later (requires nats-server v2.12.0).
345
+ # @return [String, nil]
346
+ # @!attribute allow_batched
347
+ # Whether messages can be published in fast batches
348
+ # (requires nats-server v2.14.0).
349
+ # @return [Boolean]
174
350
  StreamConfig = Struct.new(
175
351
  :name,
176
352
  :description,
@@ -198,6 +374,18 @@ module NATS
198
374
  :allow_direct,
199
375
  :mirror_direct,
200
376
  :metadata,
377
+ :compression,
378
+ :first_seq,
379
+ :subject_transform,
380
+ :consumer_limits,
381
+ :allow_msg_ttl,
382
+ :subject_delete_marker_ttl,
383
+ :allow_msg_counter,
384
+ :allow_atomic,
385
+ :allow_msg_schedules,
386
+ :persist_mode,
387
+ :allow_batched,
388
+ :discard_new_per_subject,
201
389
  keyword_init: true
202
390
  ) do
203
391
  def initialize(opts = {})
@@ -220,12 +408,32 @@ module NATS
220
408
  # @return [Hash]
221
409
  # @!attribute domain
222
410
  # @return [String]
411
+ # @!attribute mirror
412
+ # State of the stream's mirror, such as its lag and subject
413
+ # transforms, as a Hash.
414
+ # @return [Hash]
415
+ # @!attribute sources
416
+ # State of each of the stream's sources, such as its lag and subject
417
+ # transforms, as Hashes.
418
+ # @return [Array<Hash>]
419
+ # @!attribute cluster
420
+ # The cluster of the stream, such as its leader, its replicas and,
421
+ # as leader_since, when the leader was elected (requires nats-server
422
+ # v2.12.0). A standalone server reports only itself, as the leader.
423
+ # The values are the server's: leader_since is a String, and the
424
+ # active of the replicas is in nanoseconds.
425
+ # @return [Hash]
426
+ # @!attribute ts
427
+ # When the server reported this info (requires nats-server v2.10.0).
428
+ # @return [Time]
223
429
  StreamInfo = Struct.new(:type, :config, :created, :state, :domain,
430
+ :mirror, :sources, :cluster, :ts,
224
431
  keyword_init: true) do
225
432
  def initialize(opts = {})
226
433
  opts[:config] = StreamConfig.new(opts[:config])
227
434
  opts[:state] = StreamState.new(opts[:state])
228
435
  opts[:created] = ::Time.parse(opts[:created])
436
+ opts[:ts] = ::Time.parse(opts[:ts]) if opts[:ts]
229
437
 
230
438
  # Filter fields and freeze.
231
439
  rem = opts.keys - members
@@ -115,6 +115,31 @@ module NATS
115
115
  @code ||= 400
116
116
  end
117
117
  end
118
+
119
+ # When create_consumer finds the consumer already exists with a
120
+ # different configuration.
121
+ class ConsumerAlreadyExists < BadRequest; end
122
+
123
+ # When update_consumer finds no consumer to update. The server reports
124
+ # it as a bad request, so unlike ConsumerNotFound it is not NotFound.
125
+ class ConsumerDoesNotExist < BadRequest; end
126
+
127
+ # When reset_consumer is given a sequence that the consumer cannot be
128
+ # reset to: one before its start, or any for a consumer that does
129
+ # not deliver all messages or from a start sequence or time.
130
+ class ConsumerInvalidReset < BadRequest; end
131
+
132
+ # When a fetch from a consumer with the pinned_client priority policy
133
+ # finds the subscription no longer pinned, as its pin expired or it
134
+ # was unpinned. The subscription forgets its pin, so that its next
135
+ # fetch can be pinned again.
136
+ # This condition is represented with a message that has 423 status code header.
137
+ class PinIdMismatch < APIError
138
+ def initialize(params = {})
139
+ super
140
+ @code ||= 423
141
+ end
142
+ end
118
143
  end
119
144
  end
120
145
  end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2026 The NATS Authors
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ module NATS
18
+ class JetStream
19
+ # Header names headers that publishers set for JetStream, and that
20
+ # JetStream sets on the messages that it stores, republishes or returns
21
+ # from direct gets.
22
+ #
23
+ # @example Publish unless the subject got another message since sequence 10
24
+ # js.publish("orders.1", data, header: {
25
+ # NATS::JetStream::Header::EXPECTED_LAST_SUBJECT_SEQUENCE => "10"
26
+ # })
27
+ module Header
28
+ # Id of the message; the stream stores a message only once for each
29
+ # id within its duplicate_window.
30
+ MSG_ID = "Nats-Msg-Id"
31
+ # Name of the stream that has to store the message.
32
+ EXPECTED_STREAM = "Nats-Expected-Stream"
33
+ # Sequence of the last message that the stream has to have.
34
+ EXPECTED_LAST_SEQUENCE = "Nats-Expected-Last-Sequence"
35
+ # Sequence of the last message that the subject has to have.
36
+ EXPECTED_LAST_SUBJECT_SEQUENCE = "Nats-Expected-Last-Subject-Sequence"
37
+ # Subject for EXPECTED_LAST_SUBJECT_SEQUENCE to check instead of the
38
+ # subject of the message (requires nats-server v2.11.0).
39
+ EXPECTED_LAST_SUBJECT_SEQUENCE_SUBJECT = "Nats-Expected-Last-Subject-Sequence-Subject"
40
+ # Id of the last message that the stream has to have.
41
+ EXPECTED_LAST_MSG_ID = "Nats-Expected-Last-Msg-Id"
42
+ # "sub" to remove the earlier messages of the subject, or "all" those
43
+ # of the stream, as the message is stored.
44
+ ROLLUP = "Nats-Rollup"
45
+
46
+ # TTL of the message: seconds, a Go duration such as "1h", or "never"
47
+ # (requires nats-server v2.11.0).
48
+ MSG_TTL = "Nats-TTL"
49
+ # Set by the server on the marker it leaves when the last message of a
50
+ # subject expires, to "MaxAge" (requires nats-server v2.11.0).
51
+ MARKER_REASON = "Nats-Marker-Reason"
52
+
53
+ # Set by the server on the messages that it republishes or returns
54
+ # from direct gets, to the stream of the message.
55
+ STREAM = "Nats-Stream"
56
+ # Set with STREAM, to the subject of the message.
57
+ SUBJECT = "Nats-Subject"
58
+ # Set with STREAM, to the sequence of the message in the stream.
59
+ SEQUENCE = "Nats-Sequence"
60
+ # Set with STREAM, to when the stream stored the message.
61
+ TIME_STAMP = "Nats-Time-Stamp"
62
+ # Set by the server on the messages that it republishes, to the
63
+ # sequence of the message before on the same subject, or "0".
64
+ LAST_SEQUENCE = "Nats-Last-Sequence"
65
+ # Set by the server on the messages that a stream sources, starting
66
+ # with the name of the stream they come from and their sequence there.
67
+ STREAM_SOURCE = "Nats-Stream-Source"
68
+
69
+ # Id of an atomic batch, of up to 64 characters
70
+ # (requires nats-server v2.12.0).
71
+ BATCH_ID = "Nats-Batch-Id"
72
+ # Sequence of the message in its atomic batch, from 1.
73
+ BATCH_SEQUENCE = "Nats-Batch-Sequence"
74
+ # "1" to commit an atomic batch with the message, or, since nats-server
75
+ # v2.14.0, "eob" to commit it without storing the message.
76
+ BATCH_COMMIT = "Nats-Batch-Commit"
77
+
78
+ # Makes the message a schedule: "@at" and an RFC 3339 time (requires
79
+ # nats-server v2.12.0), or "@every" and a Go duration, a cron
80
+ # expression with seconds, or "@hourly", "@daily" (or "@midnight"),
81
+ # "@weekly", "@monthly" or "@yearly" (or "@annually")
82
+ # (requires nats-server v2.14.0).
83
+ SCHEDULE = "Nats-Schedule"
84
+ # Subject that the schedule publishes its messages to.
85
+ SCHEDULE_TARGET = "Nats-Schedule-Target"
86
+ # Subject whose last message the schedule publishes, instead of its
87
+ # own (requires nats-server v2.14.0).
88
+ SCHEDULE_SOURCE = "Nats-Schedule-Source"
89
+ # MSG_TTL of the messages that the schedule publishes.
90
+ SCHEDULE_TTL = "Nats-Schedule-TTL"
91
+ # Time zone of a cron schedule, such as "Europe/Warsaw"
92
+ # (requires nats-server v2.14.0).
93
+ SCHEDULE_TIME_ZONE = "Nats-Schedule-Time-Zone"
94
+ # "sub" to roll up the target with each message that the schedule
95
+ # publishes (requires nats-server v2.14.0).
96
+ SCHEDULE_ROLLUP = "Nats-Schedule-Rollup"
97
+ # Set by the server on the messages that a schedule publishes, to the
98
+ # subject of the schedule. With SCHEDULE_NEXT "purge", names the
99
+ # schedule to cancel.
100
+ SCHEDULER = "Nats-Scheduler"
101
+ # Set by the server on the messages that a schedule publishes, to when
102
+ # the schedule fires next, or to "purge" when it does not. Published
103
+ # as "purge", cancels the SCHEDULER schedule.
104
+ SCHEDULE_NEXT = "Nats-Schedule-Next"
105
+
106
+ # The API level that the server has to have for a JetStream API
107
+ # request, a pull, a direct get, or a message of an atomic batch
108
+ # (since nats-server v2.12.1) or a fast batch, which it otherwise
109
+ # refuses with status 412, and err_code 10185 for API requests and
110
+ # batches (requires nats-server v2.12.0). The server stores batch
111
+ # messages without it, and plain publishes with it, as sent.
112
+ REQUIRED_API_LEVEL = "Nats-Required-Api-Level"
113
+ end
114
+ end
115
+ end
@@ -29,6 +29,7 @@ module NATS
29
29
  ExpectedLastMsgID = "Nats-Expected-Last-Msg-Id"
30
30
  LastConsumerSeq = "Nats-Last-Consumer"
31
31
  LastStreamSeq = "Nats-Last-Stream"
32
+ PinId = "Nats-Pin-Id"
32
33
 
33
34
  # rubocop:enable Naming/ConstantName
34
35
  end
@@ -24,6 +24,7 @@ module NATS
24
24
  NoMsgs = "404"
25
25
  NotFound = "404"
26
26
  RequestTimeout = "408"
27
+ PinIdMismatch = "423"
27
28
  ServiceUnavailable = "503"
28
29
 
29
30
  # rubocop:enable Naming/ConstantName