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.
- checksums.yaml +4 -4
- data/LICENSE +201 -0
- data/README.md +251 -0
- data/lib/nats/io/client.rb +226 -151
- data/lib/nats/io/errors.rb +6 -0
- data/lib/nats/io/jetstream/api.rb +305 -0
- data/lib/nats/io/jetstream/errors.rb +104 -0
- data/lib/nats/io/jetstream/js/config.rb +26 -0
- data/lib/nats/io/jetstream/js/header.rb +31 -0
- data/lib/nats/io/jetstream/js/status.rb +27 -0
- data/lib/nats/io/jetstream/js/sub.rb +30 -0
- data/lib/nats/io/jetstream/js.rb +93 -0
- data/lib/nats/io/jetstream/manager.rb +284 -0
- data/lib/nats/io/jetstream/msg/ack.rb +57 -0
- data/lib/nats/io/jetstream/msg/ack_methods.rb +111 -0
- data/lib/nats/io/jetstream/msg/metadata.rb +37 -0
- data/lib/nats/io/jetstream/msg.rb +26 -0
- data/lib/nats/io/jetstream/pull_subscription.rb +260 -0
- data/lib/nats/io/jetstream/push_subscription.rb +42 -0
- data/lib/nats/io/jetstream.rb +269 -0
- data/lib/nats/io/kv/api.rb +39 -0
- data/lib/nats/io/kv/bucket_status.rb +38 -0
- data/lib/nats/io/kv/errors.rb +60 -0
- data/lib/nats/io/kv/manager.rb +89 -0
- data/lib/nats/io/kv.rb +5 -157
- data/lib/nats/io/msg.rb +4 -2
- data/lib/nats/io/rails.rb +29 -0
- data/lib/nats/io/subscription.rb +70 -5
- data/lib/nats/io/version.rb +1 -1
- data/lib/nats/io/websocket.rb +75 -0
- data/sig/nats/io/client.rbs +304 -0
- data/sig/nats/io/errors.rbs +54 -0
- data/sig/nats/io/jetstream/api.rbs +35 -0
- data/sig/nats/io/jetstream/errors.rbs +54 -0
- data/sig/nats/io/jetstream/js/config.rbs +11 -0
- data/sig/nats/io/jetstream/js/header.rbs +17 -0
- data/sig/nats/io/jetstream/js/status.rbs +13 -0
- data/sig/nats/io/jetstream/js/sub.rbs +14 -0
- data/sig/nats/io/jetstream/js.rbs +27 -0
- data/sig/nats/io/jetstream/manager.rbs +33 -0
- data/sig/nats/io/jetstream/msg/ack.rbs +35 -0
- data/sig/nats/io/jetstream/msg/ack_methods.rbs +25 -0
- data/sig/nats/io/jetstream/msg/metadata.rbs +15 -0
- data/sig/nats/io/jetstream/msg.rbs +6 -0
- data/sig/nats/io/jetstream/pull_subscription.rbs +14 -0
- data/sig/nats/io/jetstream/push_subscription.rbs +7 -0
- data/sig/nats/io/jetstream.rbs +15 -0
- data/sig/nats/io/kv/api.rbs +8 -0
- data/sig/nats/io/kv/bucket_status.rbs +17 -0
- data/sig/nats/io/kv/errors.rbs +30 -0
- data/sig/nats/io/kv/manager.rbs +11 -0
- data/sig/nats/io/kv.rbs +39 -0
- data/sig/nats/io/msg.rbs +14 -0
- data/sig/nats/io/parser.rbs +32 -0
- data/sig/nats/io/subscription.rbs +33 -0
- data/sig/nats/io/version.rbs +9 -0
- data/sig/nats/nuid.rbs +32 -0
- metadata +68 -5
- data/lib/nats/io/js.rb +0 -1434
@@ -0,0 +1,260 @@
|
|
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
|
+
|
15
|
+
require_relative 'errors'
|
16
|
+
|
17
|
+
module NATS
|
18
|
+
class JetStream
|
19
|
+
# PullSubscription is included into NATS::Subscription so that it
|
20
|
+
# can be used to fetch messages from a pull based consumer from
|
21
|
+
# JetStream.
|
22
|
+
#
|
23
|
+
# @example Create a pull subscription using JetStream context.
|
24
|
+
#
|
25
|
+
# require 'nats/client'
|
26
|
+
#
|
27
|
+
# nc = NATS.connect
|
28
|
+
# js = nc.jetstream
|
29
|
+
# psub = js.pull_subscribe("foo", "bar")
|
30
|
+
#
|
31
|
+
# loop do
|
32
|
+
# msgs = psub.fetch(5)
|
33
|
+
# msgs.each do |msg|
|
34
|
+
# msg.ack
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @!visibility public
|
39
|
+
module PullSubscription
|
40
|
+
# next_msg is not available for pull based subscriptions.
|
41
|
+
# @raise [NATS::JetStream::Error]
|
42
|
+
def next_msg(params={})
|
43
|
+
raise ::NATS::JetStream::Error.new("nats: pull subscription cannot use next_msg")
|
44
|
+
end
|
45
|
+
|
46
|
+
# fetch makes a request to be delivered more messages from a pull consumer.
|
47
|
+
#
|
48
|
+
# @param batch [Fixnum] Number of messages to pull from the stream.
|
49
|
+
# @param params [Hash] Options to customize the fetch request.
|
50
|
+
# @option params [Float] :timeout Duration of the fetch request before it expires.
|
51
|
+
# @return [Array<NATS::Msg>]
|
52
|
+
def fetch(batch=1, params={})
|
53
|
+
if batch < 1
|
54
|
+
raise ::NATS::JetStream::Error.new("nats: invalid batch size")
|
55
|
+
end
|
56
|
+
|
57
|
+
t = MonotonicTime.now
|
58
|
+
timeout = params[:timeout] ||= 5
|
59
|
+
expires = (timeout * 1_000_000_000) - 100_000
|
60
|
+
next_req = {
|
61
|
+
batch: batch
|
62
|
+
}
|
63
|
+
|
64
|
+
msgs = []
|
65
|
+
case
|
66
|
+
when batch < 1
|
67
|
+
raise ::NATS::JetStream::Error.new("nats: invalid batch size")
|
68
|
+
when batch == 1
|
69
|
+
####################################################
|
70
|
+
# Fetch (1) #
|
71
|
+
####################################################
|
72
|
+
|
73
|
+
# Check if there is any pending message in the queue that is
|
74
|
+
# ready to be consumed.
|
75
|
+
synchronize do
|
76
|
+
unless @pending_queue.empty?
|
77
|
+
msg = @pending_queue.pop
|
78
|
+
@pending_size -= msg.data.size
|
79
|
+
# Check for a no msgs response status.
|
80
|
+
if JS.is_status_msg(msg)
|
81
|
+
case msg.header["Status"]
|
82
|
+
when JS::Status::NoMsgs
|
83
|
+
msg = nil
|
84
|
+
when JS::Status::RequestTimeout
|
85
|
+
# Skip
|
86
|
+
else
|
87
|
+
raise JS.from_msg(msg)
|
88
|
+
end
|
89
|
+
else
|
90
|
+
msgs << msg
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Make lingering request with expiration.
|
96
|
+
next_req[:expires] = expires
|
97
|
+
if msgs.empty?
|
98
|
+
# Make publish request and wait for response.
|
99
|
+
@nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
|
100
|
+
|
101
|
+
# Wait for result of fetch or timeout.
|
102
|
+
synchronize { wait_for_msgs_cond.wait(timeout) }
|
103
|
+
|
104
|
+
unless @pending_queue.empty?
|
105
|
+
msg = @pending_queue.pop
|
106
|
+
@pending_size -= msg.data.size
|
107
|
+
|
108
|
+
msgs << msg
|
109
|
+
end
|
110
|
+
|
111
|
+
duration = MonotonicTime.since(t)
|
112
|
+
if duration > timeout
|
113
|
+
raise ::NATS::Timeout.new("nats: fetch timeout")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Should have received at least a message at this point,
|
117
|
+
# if that is not the case then error already.
|
118
|
+
if JS.is_status_msg(msgs.first)
|
119
|
+
msg = msgs.first
|
120
|
+
case msg.header[JS::Header::Status]
|
121
|
+
when JS::Status::RequestTimeout
|
122
|
+
raise NATS::Timeout.new("nats: fetch request timeout")
|
123
|
+
else
|
124
|
+
raise JS.from_msg(msgs.first)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
when batch > 1
|
129
|
+
####################################################
|
130
|
+
# Fetch (n) #
|
131
|
+
####################################################
|
132
|
+
|
133
|
+
# Check if there already enough in the pending buffer.
|
134
|
+
synchronize do
|
135
|
+
if batch <= @pending_queue.size
|
136
|
+
batch.times do
|
137
|
+
msg = @pending_queue.pop
|
138
|
+
@pending_size -= msg.data.size
|
139
|
+
|
140
|
+
# Check for a no msgs response status.
|
141
|
+
if JS.is_status_msg(msg)
|
142
|
+
case msg.header[JS::Header::Status]
|
143
|
+
when JS::Status::NoMsgs, JS::Status::RequestTimeout
|
144
|
+
# Skip these
|
145
|
+
next
|
146
|
+
else
|
147
|
+
raise JS.from_msg(msg)
|
148
|
+
end
|
149
|
+
else
|
150
|
+
msgs << msg
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
return msgs
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Make publish request and wait any response.
|
159
|
+
next_req[:no_wait] = true
|
160
|
+
@nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
|
161
|
+
|
162
|
+
# Not receiving even one is a timeout.
|
163
|
+
start_time = MonotonicTime.now
|
164
|
+
msg = nil
|
165
|
+
|
166
|
+
synchronize do
|
167
|
+
wait_for_msgs_cond.wait(timeout)
|
168
|
+
|
169
|
+
unless @pending_queue.empty?
|
170
|
+
msg = @pending_queue.pop
|
171
|
+
@pending_size -= msg.data.size
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Check if the first message was a response saying that
|
176
|
+
# there are no messages.
|
177
|
+
if !msg.nil? && JS.is_status_msg(msg)
|
178
|
+
case msg.header[JS::Header::Status]
|
179
|
+
when JS::Status::NoMsgs
|
180
|
+
# Make another request that does wait.
|
181
|
+
next_req[:expires] = expires
|
182
|
+
next_req.delete(:no_wait)
|
183
|
+
|
184
|
+
@nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
|
185
|
+
when JS::Status::RequestTimeout
|
186
|
+
raise NATS::Timeout.new("nats: fetch request timeout")
|
187
|
+
else
|
188
|
+
raise JS.from_msg(msg)
|
189
|
+
end
|
190
|
+
else
|
191
|
+
msgs << msg unless msg.nil?
|
192
|
+
end
|
193
|
+
|
194
|
+
# Check if have not received yet a single message.
|
195
|
+
duration = MonotonicTime.since(start_time)
|
196
|
+
if msgs.empty? and duration > timeout
|
197
|
+
raise NATS::Timeout.new("nats: fetch timeout")
|
198
|
+
end
|
199
|
+
|
200
|
+
needed = batch - msgs.count
|
201
|
+
while needed > 0 and MonotonicTime.since(start_time) < timeout
|
202
|
+
duration = MonotonicTime.since(start_time)
|
203
|
+
|
204
|
+
# Wait for the rest of the messages.
|
205
|
+
synchronize do
|
206
|
+
|
207
|
+
# Wait until there is a message delivered.
|
208
|
+
if @pending_queue.empty?
|
209
|
+
deadline = timeout - duration
|
210
|
+
wait_for_msgs_cond.wait(deadline) if deadline > 0
|
211
|
+
|
212
|
+
duration = MonotonicTime.since(start_time)
|
213
|
+
if msgs.empty? && @pending_queue.empty? and duration > timeout
|
214
|
+
raise NATS::Timeout.new("nats: fetch timeout")
|
215
|
+
end
|
216
|
+
else
|
217
|
+
msg = @pending_queue.pop
|
218
|
+
@pending_size -= msg.data.size
|
219
|
+
|
220
|
+
if JS.is_status_msg(msg)
|
221
|
+
case msg.header[JS::Header::Status]
|
222
|
+
when JS::Status::NoMsgs, JS::Status::RequestTimeout
|
223
|
+
duration = MonotonicTime.since(start_time)
|
224
|
+
|
225
|
+
if duration > timeout
|
226
|
+
# Only received a subset of the messages.
|
227
|
+
if !msgs.empty?
|
228
|
+
return msgs
|
229
|
+
else
|
230
|
+
raise NATS::Timeout.new("nats: fetch timeout")
|
231
|
+
end
|
232
|
+
end
|
233
|
+
else
|
234
|
+
raise JS.from_msg(msg)
|
235
|
+
end
|
236
|
+
|
237
|
+
else
|
238
|
+
# Add to the set of messages that will be returned.
|
239
|
+
msgs << msg
|
240
|
+
needed -= 1
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end # :end: synchronize
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
msgs
|
248
|
+
end
|
249
|
+
|
250
|
+
# consumer_info retrieves the current status of the pull subscription consumer.
|
251
|
+
# @param params [Hash] Options to customize API request.
|
252
|
+
# @option params [Float] :timeout Time to wait for response.
|
253
|
+
# @return [JetStream::API::ConsumerInfo] The latest ConsumerInfo of the consumer.
|
254
|
+
def consumer_info(params={})
|
255
|
+
@jsi.js.consumer_info(@jsi.stream, @jsi.consumer, params)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
private_constant :PullSubscription
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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
|
+
|
15
|
+
module NATS
|
16
|
+
class JetStream
|
17
|
+
# PushSubscription is included into NATS::Subscription so that it
|
18
|
+
#
|
19
|
+
# @example Create a push subscription using JetStream context.
|
20
|
+
#
|
21
|
+
# require 'nats/client'
|
22
|
+
#
|
23
|
+
# nc = NATS.connect
|
24
|
+
# js = nc.jetstream
|
25
|
+
# sub = js.subscribe("foo", "bar")
|
26
|
+
# msg = sub.next_msg
|
27
|
+
# msg.ack
|
28
|
+
# sub.unsubscribe
|
29
|
+
#
|
30
|
+
# @!visibility public
|
31
|
+
module PushSubscription
|
32
|
+
# consumer_info retrieves the current status of the pull subscription consumer.
|
33
|
+
# @param params [Hash] Options to customize API request.
|
34
|
+
# @option params [Float] :timeout Time to wait for response.
|
35
|
+
# @return [JetStream::API::ConsumerInfo] The latest ConsumerInfo of the consumer.
|
36
|
+
def consumer_info(params={})
|
37
|
+
@jsi.js.consumer_info(@jsi.stream, @jsi.consumer, params)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
private_constant :PushSubscription
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,269 @@
|
|
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_relative 'jetstream/api'
|
19
|
+
require_relative 'jetstream/errors'
|
20
|
+
require_relative 'jetstream/js'
|
21
|
+
require_relative 'jetstream/manager'
|
22
|
+
require_relative 'jetstream/msg'
|
23
|
+
require_relative 'jetstream/pull_subscription'
|
24
|
+
require_relative 'jetstream/push_subscription'
|
25
|
+
|
26
|
+
module NATS
|
27
|
+
# JetStream returns a context with a similar API as the NATS::Client
|
28
|
+
# but with enhanced functions to persist and consume messages from
|
29
|
+
# the NATS JetStream engine.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# nc = NATS.connect("demo.nats.io")
|
33
|
+
# js = nc.jetstream()
|
34
|
+
#
|
35
|
+
class JetStream
|
36
|
+
# Create a new JetStream context for a NATS connection.
|
37
|
+
#
|
38
|
+
# @param conn [NATS::Client]
|
39
|
+
# @param params [Hash] Options to customize JetStream context.
|
40
|
+
# @option params [String] :prefix JetStream API prefix to use for the requests.
|
41
|
+
# @option params [String] :domain JetStream Domain to use for the requests.
|
42
|
+
# @option params [Float] :timeout Default timeout to use for JS requests.
|
43
|
+
def initialize(conn, params={})
|
44
|
+
@nc = conn
|
45
|
+
@prefix = if params[:prefix]
|
46
|
+
params[:prefix]
|
47
|
+
elsif params[:domain]
|
48
|
+
"$JS.#{params[:domain]}.API"
|
49
|
+
else
|
50
|
+
JS::DefaultAPIPrefix
|
51
|
+
end
|
52
|
+
@opts = params
|
53
|
+
@opts[:timeout] ||= 5 # seconds
|
54
|
+
params[:prefix] = @prefix
|
55
|
+
|
56
|
+
# Include JetStream::Manager
|
57
|
+
extend Manager
|
58
|
+
extend KeyValue::Manager
|
59
|
+
end
|
60
|
+
|
61
|
+
# PubAck is the API response from a successfully published message.
|
62
|
+
#
|
63
|
+
# @!attribute [stream] stream
|
64
|
+
# @return [String] Name of the stream that processed the published message.
|
65
|
+
# @!attribute [seq] seq
|
66
|
+
# @return [Fixnum] Sequence of the message in the stream.
|
67
|
+
# @!attribute [duplicate] duplicate
|
68
|
+
# @return [Boolean] Indicates whether the published message is a duplicate.
|
69
|
+
# @!attribute [domain] domain
|
70
|
+
# @return [String] JetStream Domain that processed the ack response.
|
71
|
+
PubAck = Struct.new(:stream, :seq, :duplicate, :domain, keyword_init: true)
|
72
|
+
|
73
|
+
# publish produces a message for JetStream.
|
74
|
+
#
|
75
|
+
# @param subject [String] The subject from a stream where the message will be sent.
|
76
|
+
# @param payload [String] The payload of the message.
|
77
|
+
# @param params [Hash] Options to customize the publish message request.
|
78
|
+
# @option params [Float] :timeout Time to wait for an PubAck response or an error.
|
79
|
+
# @option params [Hash] :header NATS Headers to use for the message.
|
80
|
+
# @option params [String] :stream Expected Stream to which the message is being published.
|
81
|
+
# @raise [NATS::Timeout] When it takes too long to receive an ack response.
|
82
|
+
# @return [PubAck] The pub ack response.
|
83
|
+
def publish(subject, payload="", **params)
|
84
|
+
params[:timeout] ||= @opts[:timeout]
|
85
|
+
if params[:stream]
|
86
|
+
params[:header] ||= {}
|
87
|
+
params[:header][JS::Header::ExpectedStream] = params[:stream]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Send message with headers.
|
91
|
+
msg = NATS::Msg.new(subject: subject,
|
92
|
+
data: payload,
|
93
|
+
header: params[:header])
|
94
|
+
|
95
|
+
begin
|
96
|
+
resp = @nc.request_msg(msg, **params)
|
97
|
+
result = JSON.parse(resp.data, symbolize_names: true)
|
98
|
+
rescue ::NATS::IO::NoRespondersError
|
99
|
+
raise JetStream::Error::NoStreamResponse.new("nats: no response from stream")
|
100
|
+
end
|
101
|
+
raise JS.from_error(result[:error]) if result[:error]
|
102
|
+
|
103
|
+
PubAck.new(result)
|
104
|
+
end
|
105
|
+
|
106
|
+
# subscribe binds or creates a push subscription to a JetStream pull consumer.
|
107
|
+
#
|
108
|
+
# @param subject [String] Subject from which the messages will be fetched.
|
109
|
+
# @param params [Hash] Options to customize the PushSubscription.
|
110
|
+
# @option params [String] :stream Name of the Stream to which the consumer belongs.
|
111
|
+
# @option params [String] :consumer Name of the Consumer to which the PushSubscription will be bound.
|
112
|
+
# @option params [String] :durable Consumer durable name from where the messages will be fetched.
|
113
|
+
# @option params [Hash] :config Configuration for the consumer.
|
114
|
+
# @return [NATS::JetStream::PushSubscription]
|
115
|
+
def subscribe(subject, params={}, &cb)
|
116
|
+
params[:consumer] ||= params[:durable]
|
117
|
+
stream = params[:stream].nil? ? find_stream_name_by_subject(subject) : params[:stream]
|
118
|
+
|
119
|
+
queue = params[:queue]
|
120
|
+
durable = params[:durable]
|
121
|
+
flow_control = params[:flow_control]
|
122
|
+
manual_ack = params[:manual_ack]
|
123
|
+
idle_heartbeat = params[:idle_heartbeat]
|
124
|
+
flow_control = params[:flow_control]
|
125
|
+
config = params[:config]
|
126
|
+
|
127
|
+
if queue
|
128
|
+
if durable and durable != queue
|
129
|
+
raise NATS::JetStream::Error.new("nats: cannot create queue subscription '#{queue}' to consumer '#{durable}'")
|
130
|
+
else
|
131
|
+
durable = queue
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
cinfo = nil
|
136
|
+
consumer_found = false
|
137
|
+
should_create = false
|
138
|
+
|
139
|
+
if not durable
|
140
|
+
should_create = true
|
141
|
+
else
|
142
|
+
begin
|
143
|
+
cinfo = consumer_info(stream, durable)
|
144
|
+
config = cinfo.config
|
145
|
+
consumer_found = true
|
146
|
+
consumer = durable
|
147
|
+
rescue NATS::JetStream::Error::NotFound
|
148
|
+
should_create = true
|
149
|
+
consumer_found = false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
if consumer_found
|
154
|
+
if not config.deliver_group
|
155
|
+
if queue
|
156
|
+
raise NATS::JetStream::Error.new("nats: cannot create a queue subscription for a consumer without a deliver group")
|
157
|
+
elsif cinfo.push_bound
|
158
|
+
raise NATS::JetStream::Error.new("nats: consumer is already bound to a subscription")
|
159
|
+
end
|
160
|
+
else
|
161
|
+
if not queue
|
162
|
+
raise NATS::JetStream::Error.new("nats: cannot create a subscription for a consumer with a deliver group #{config.deliver_group}")
|
163
|
+
elsif queue != config.deliver_group
|
164
|
+
raise NATS::JetStream::Error.new("nats: cannot create a queue subscription #{queue} for a consumer with a deliver group #{config.deliver_group}")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
elsif should_create
|
168
|
+
# Auto-create consumer if none found.
|
169
|
+
if config.nil?
|
170
|
+
# Defaults
|
171
|
+
config = JetStream::API::ConsumerConfig.new({ack_policy: "explicit"})
|
172
|
+
elsif config.is_a?(Hash)
|
173
|
+
config = JetStream::API::ConsumerConfig.new(config)
|
174
|
+
elsif !config.is_a?(JetStream::API::ConsumerConfig)
|
175
|
+
raise NATS::JetStream::Error.new("nats: invalid ConsumerConfig")
|
176
|
+
end
|
177
|
+
|
178
|
+
config.durable_name = durable if not config.durable_name
|
179
|
+
config.deliver_group = queue if not config.deliver_group
|
180
|
+
|
181
|
+
# Create inbox for push consumer.
|
182
|
+
deliver = @nc.new_inbox
|
183
|
+
config.deliver_subject = deliver
|
184
|
+
|
185
|
+
# Auto created consumers use the filter subject.
|
186
|
+
config.filter_subject = subject
|
187
|
+
|
188
|
+
# Heartbeats / FlowControl
|
189
|
+
config.flow_control = flow_control
|
190
|
+
if idle_heartbeat or config.idle_heartbeat
|
191
|
+
idle_heartbeat = config.idle_heartbeat if config.idle_heartbeat
|
192
|
+
idle_heartbeat = idle_heartbeat * ::NATS::NANOSECONDS
|
193
|
+
config.idle_heartbeat = idle_heartbeat
|
194
|
+
end
|
195
|
+
|
196
|
+
# Auto create the consumer.
|
197
|
+
cinfo = add_consumer(stream, config)
|
198
|
+
consumer = cinfo.name
|
199
|
+
end
|
200
|
+
|
201
|
+
# Enable auto acking for async callbacks unless disabled.
|
202
|
+
if cb and not manual_ack
|
203
|
+
ocb = cb
|
204
|
+
new_cb = proc do |msg|
|
205
|
+
ocb.call(msg)
|
206
|
+
msg.ack rescue JetStream::Error::MsgAlreadyAckd
|
207
|
+
end
|
208
|
+
cb = new_cb
|
209
|
+
end
|
210
|
+
sub = @nc.subscribe(config.deliver_subject, queue: config.deliver_group, &cb)
|
211
|
+
sub.extend(PushSubscription)
|
212
|
+
sub.jsi = JS::Sub.new(
|
213
|
+
js: self,
|
214
|
+
stream: stream,
|
215
|
+
consumer: consumer,
|
216
|
+
)
|
217
|
+
sub
|
218
|
+
end
|
219
|
+
|
220
|
+
# pull_subscribe binds or creates a subscription to a JetStream pull consumer.
|
221
|
+
#
|
222
|
+
# @param subject [String] Subject from which the messages will be fetched.
|
223
|
+
# @param durable [String] Consumer durable name from where the messages will be fetched.
|
224
|
+
# @param params [Hash] Options to customize the PullSubscription.
|
225
|
+
# @option params [String] :stream Name of the Stream to which the consumer belongs.
|
226
|
+
# @option params [String] :consumer Name of the Consumer to which the PullSubscription will be bound.
|
227
|
+
# @option params [Hash] :config Configuration for the consumer.
|
228
|
+
# @return [NATS::JetStream::PullSubscription]
|
229
|
+
def pull_subscribe(subject, durable, params={})
|
230
|
+
if durable.empty? && !params[:consumer]
|
231
|
+
raise JetStream::Error::InvalidDurableName.new("nats: invalid durable name")
|
232
|
+
end
|
233
|
+
params[:consumer] ||= durable
|
234
|
+
stream = params[:stream].nil? ? find_stream_name_by_subject(subject) : params[:stream]
|
235
|
+
|
236
|
+
begin
|
237
|
+
consumer_info(stream, params[:consumer])
|
238
|
+
rescue NATS::JetStream::Error::NotFound => e
|
239
|
+
# If attempting to bind, then this is a hard error.
|
240
|
+
raise e if params[:stream]
|
241
|
+
|
242
|
+
config = if not params[:config]
|
243
|
+
JetStream::API::ConsumerConfig.new
|
244
|
+
elsif params[:config].is_a?(JetStream::API::ConsumerConfig)
|
245
|
+
params[:config]
|
246
|
+
else
|
247
|
+
JetStream::API::ConsumerConfig.new(params[:config])
|
248
|
+
end
|
249
|
+
config[:durable_name] = durable
|
250
|
+
config[:ack_policy] ||= JS::Config::AckExplicit
|
251
|
+
add_consumer(stream, config)
|
252
|
+
end
|
253
|
+
|
254
|
+
deliver = @nc.new_inbox
|
255
|
+
sub = @nc.subscribe(deliver)
|
256
|
+
sub.extend(PullSubscription)
|
257
|
+
|
258
|
+
consumer = params[:consumer]
|
259
|
+
subject = "#{@prefix}.CONSUMER.MSG.NEXT.#{stream}.#{consumer}"
|
260
|
+
sub.jsi = JS::Sub.new(
|
261
|
+
js: self,
|
262
|
+
stream: stream,
|
263
|
+
consumer: params[:consumer],
|
264
|
+
nms: subject
|
265
|
+
)
|
266
|
+
sub
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
@@ -0,0 +1,39 @@
|
|
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
|
+
|
15
|
+
module NATS
|
16
|
+
class KeyValue
|
17
|
+
module API
|
18
|
+
KeyValueConfig = Struct.new(
|
19
|
+
:bucket,
|
20
|
+
:description,
|
21
|
+
:max_value_size,
|
22
|
+
:history,
|
23
|
+
:ttl,
|
24
|
+
:max_bytes,
|
25
|
+
:storage,
|
26
|
+
:replicas,
|
27
|
+
:placement,
|
28
|
+
:republish,
|
29
|
+
:direct,
|
30
|
+
keyword_init: true) do
|
31
|
+
def initialize(opts={})
|
32
|
+
rem = opts.keys - members
|
33
|
+
opts.delete_if { |k| rem.include?(k) }
|
34
|
+
super(opts)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
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
|
+
|
15
|
+
module NATS
|
16
|
+
class KeyValue
|
17
|
+
class BucketStatus
|
18
|
+
attr_reader :bucket
|
19
|
+
|
20
|
+
def initialize(info, bucket)
|
21
|
+
@nfo = info
|
22
|
+
@bucket = bucket
|
23
|
+
end
|
24
|
+
|
25
|
+
def values
|
26
|
+
@nfo.state.messages
|
27
|
+
end
|
28
|
+
|
29
|
+
def history
|
30
|
+
@nfo.config.max_msgs_per_subject
|
31
|
+
end
|
32
|
+
|
33
|
+
def ttl
|
34
|
+
@nfo.config.max_age / ::NATS::NANOSECONDS
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|