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