nats-pure 2.0.0.pre.rc1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 210e29c9b6fcc68e5c89e841a068c1a389ac78a67cadcb639161d0d5c02c4bf0
4
- data.tar.gz: 2b181886b3a96277ae494cdda48b8bf16e9721666294ea69440976b85888b40c
3
+ metadata.gz: 6f0d4251703ba9fd6a91f501686025e2f71e93dc7869f65984da7cd312361266
4
+ data.tar.gz: 411fdfd23878cf89b26c76e04b68c760e10aaef14f2a68fae19c69a8d1392990
5
5
  SHA512:
6
- metadata.gz: 910ffb697d692a3040fd6b40e835d1a2486f16525a5e814ea33dfdb2e732dd6ba3f1876b5e6dbc7dee07e9491154a6c181b41b2db08988434d9e48900ae3dcf0
7
- data.tar.gz: a4e4d9cc9be95d1c67331576c814841b7b7784986a6ac9503ec44846e5dfbf2af978990fe482c8a9ea1064112ab54b4c65cfc619a931a20904f712d2896547a1
6
+ metadata.gz: a3b88a713e2c1b6a515ac4f505cb6724c0059e72693d8d5c2ee61f3579d1b4b846408282e131864735dccc8b224e4a22933be91409d404fedadf0ec6c72051cf
7
+ data.tar.gz: 9ef88d1a391c2cfec91731aca2742382634c923384b801fce4ee9155418de39bd538f7aaafceb6ba2a1b40b57c6b7db4363ad4cf673d8ff3b5e87b5f74104789
@@ -227,6 +227,7 @@ module NATS
227
227
  opts[:pedantic] = false if opts[:pedantic].nil?
228
228
  opts[:reconnect] = true if opts[:reconnect].nil?
229
229
  opts[:old_style_request] = false if opts[:old_style_request].nil?
230
+ opts[:ignore_discovered_urls] = false if opts[:ignore_discovered_urls].nil?
230
231
  opts[:reconnect_time_wait] = NATS::IO::RECONNECT_TIME_WAIT if opts[:reconnect_time_wait].nil?
231
232
  opts[:max_reconnect_attempts] = NATS::IO::MAX_RECONNECT_ATTEMPTS if opts[:max_reconnect_attempts].nil?
232
233
  opts[:ping_interval] = NATS::IO::DEFAULT_PING_INTERVAL if opts[:ping_interval].nil?
@@ -237,6 +238,7 @@ module NATS
237
238
  opts[:pedantic] = ENV['NATS_PEDANTIC'].downcase == 'true' unless ENV['NATS_PEDANTIC'].nil?
238
239
  opts[:reconnect] = ENV['NATS_RECONNECT'].downcase == 'true' unless ENV['NATS_RECONNECT'].nil?
239
240
  opts[:reconnect_time_wait] = ENV['NATS_RECONNECT_TIME_WAIT'].to_i unless ENV['NATS_RECONNECT_TIME_WAIT'].nil?
241
+ opts[:ignore_discovered_urls] = ENV['NATS_IGNORE_DISCOVERED_URLS'].downcase == 'true' unless ENV['NATS_IGNORE_DISCOVERED_URLS'].nil?
240
242
  opts[:max_reconnect_attempts] = ENV['NATS_MAX_RECONNECT_ATTEMPTS'].to_i unless ENV['NATS_MAX_RECONNECT_ATTEMPTS'].nil?
241
243
  opts[:ping_interval] = ENV['NATS_PING_INTERVAL'].to_i unless ENV['NATS_PING_INTERVAL'].nil?
242
244
  opts[:max_outstanding_pings] = ENV['NATS_MAX_OUTSTANDING_PINGS'].to_i unless ENV['NATS_MAX_OUTSTANDING_PINGS'].nil?
@@ -256,7 +258,7 @@ module NATS
256
258
  end
257
259
  @server_pool << {
258
260
  :uri => nats_uri,
259
- :hostname => nats_uri.host
261
+ :hostname => nats_uri.hostname
260
262
  }
261
263
  end
262
264
 
@@ -807,17 +809,17 @@ module NATS
807
809
 
808
810
  # Detect any announced server that we might not be aware of...
809
811
  connect_urls = @server_info[:connect_urls]
810
- if connect_urls
812
+ if !@options[:ignore_discovered_urls] && connect_urls
811
813
  srvs = []
812
814
  connect_urls.each do |url|
813
815
  scheme = client_using_secure_connection? ? "tls" : "nats"
814
816
  u = URI.parse("#{scheme}://#{url}")
815
817
 
816
818
  # Skip in case it is the current server which we already know
817
- next if @uri.host == u.host && @uri.port == u.port
819
+ next if @uri.hostname == u.hostname && @uri.port == u.port
818
820
 
819
821
  present = server_pool.detect do |srv|
820
- srv[:uri].host == u.host && srv[:uri].port == u.port
822
+ srv[:uri].hostname == u.hostname && srv[:uri].port == u.port
821
823
  end
822
824
 
823
825
  if not present
@@ -832,7 +834,7 @@ module NATS
832
834
  end
833
835
 
834
836
  # NOTE: Auto discovery won't work here when TLS host verification is enabled.
835
- srv = { :uri => u, :reconnect_attempts => 0, :discovered => true, :hostname => u.host }
837
+ srv = { :uri => u, :reconnect_attempts => 0, :discovered => true, :hostname => u.hostname }
836
838
  srvs << srv
837
839
  end
838
840
  end
@@ -1758,34 +1760,20 @@ module NATS
1758
1760
  end
1759
1761
 
1760
1762
  def process_uri(uris)
1761
- connect_uris = []
1762
- uris.split(',').each do |uri|
1763
+ uris.split(',').map do |uri|
1763
1764
  opts = {}
1764
1765
 
1765
1766
  # Scheme
1766
- if uri.include?("://")
1767
- scheme, uri = uri.split("://")
1768
- opts[:scheme] = scheme
1769
- else
1770
- opts[:scheme] = 'nats'
1771
- end
1767
+ uri = "nats://#{uri}" if !uri.include?("://")
1772
1768
 
1773
- # UserInfo
1774
- if uri.include?("@")
1775
- userinfo, endpoint = uri.split("@")
1776
- host, port = endpoint.split(":")
1777
- opts[:userinfo] = userinfo
1778
- else
1779
- host, port = uri.split(":")
1780
- end
1769
+ uri_object = URI(uri)
1781
1770
 
1782
1771
  # Host and Port
1783
- opts[:host] = host || "localhost"
1784
- opts[:port] = port || DEFAULT_PORT
1772
+ uri_object.hostname ||= "localhost"
1773
+ uri_object.port ||= DEFAULT_PORT
1785
1774
 
1786
- connect_uris << URI::Generic.build(opts)
1775
+ uri_object
1787
1776
  end
1788
- connect_uris
1789
1777
  end
1790
1778
  end
1791
1779
 
@@ -1834,7 +1822,7 @@ module NATS
1834
1822
  end
1835
1823
 
1836
1824
  def connect
1837
- addrinfo = ::Socket.getaddrinfo(@uri.host, nil, ::Socket::AF_UNSPEC, ::Socket::SOCK_STREAM)
1825
+ addrinfo = ::Socket.getaddrinfo(@uri.hostname, nil, ::Socket::AF_UNSPEC, ::Socket::SOCK_STREAM)
1838
1826
  addrinfo.each_with_index do |ai, i|
1839
1827
  begin
1840
1828
  @socket = connect_addrinfo(ai, @uri.port, @connect_timeout)
data/lib/nats/io/js.rb CHANGED
@@ -284,6 +284,7 @@ module NATS
284
284
  end
285
285
  stream = config[:name]
286
286
  raise ArgumentError.new(":name is required to create streams") unless stream
287
+ raise ArgumentError.new("Spaces, tabs, period (.), greater than (>) or asterisk (*) are prohibited in stream names") if stream =~ /(\s|\.|\>|\*)/
287
288
  req_subject = "#{@prefix}.STREAM.CREATE.#{stream}"
288
289
  result = api_request(req_subject, config.to_json, params)
289
290
  JetStream::API::StreamCreateResponse.new(result)
@@ -345,6 +346,7 @@ module NATS
345
346
  stream_name: stream,
346
347
  config: config
347
348
  }
349
+
348
350
  result = api_request(req_subject, req.to_json, params)
349
351
  JetStream::API::ConsumerInfo.new(result).freeze
350
352
  end
@@ -502,11 +504,14 @@ module NATS
502
504
  synchronize do
503
505
  unless @pending_queue.empty?
504
506
  msg = @pending_queue.pop
507
+ @pending_size -= msg.data.size
505
508
  # Check for a no msgs response status.
506
509
  if JS.is_status_msg(msg)
507
510
  case msg.header["Status"]
508
511
  when JS::Status::NoMsgs
509
512
  msg = nil
513
+ when JS::Status::RequestTimeout
514
+ # Skip
510
515
  else
511
516
  raise JS.from_msg(msg)
512
517
  end
@@ -525,7 +530,12 @@ module NATS
525
530
  # Wait for result of fetch or timeout.
526
531
  synchronize { wait_for_msgs_cond.wait(timeout) }
527
532
 
528
- msgs << @pending_queue.pop unless @pending_queue.empty?
533
+ unless @pending_queue.empty?
534
+ msg = @pending_queue.pop
535
+ @pending_size -= msg.data.size
536
+
537
+ msgs << msg
538
+ end
529
539
 
530
540
  duration = MonotonicTime.since(t)
531
541
  if duration > timeout
@@ -535,7 +545,13 @@ module NATS
535
545
  # Should have received at least a message at this point,
536
546
  # if that is not the case then error already.
537
547
  if JS.is_status_msg(msgs.first)
538
- raise JS.from_msg(msgs.first)
548
+ msg = msgs.first
549
+ case msg.header[JS::Header::Status]
550
+ when JS::Status::RequestTimeout
551
+ raise NATS::Timeout.new("nats: fetch request timeout")
552
+ else
553
+ raise JS.from_msg(msgs.first)
554
+ end
539
555
  end
540
556
  end
541
557
  when batch > 1
@@ -546,7 +562,23 @@ module NATS
546
562
  # Check if there already enough in the pending buffer.
547
563
  synchronize do
548
564
  if batch <= @pending_queue.size
549
- batch.times { msgs << @pending_queue.pop }
565
+ batch.times do
566
+ msg = @pending_queue.pop
567
+ @pending_size -= msg.data.size
568
+
569
+ # Check for a no msgs response status.
570
+ if JS.is_status_msg(msg)
571
+ case msg.header[JS::Header::Status]
572
+ when JS::Status::NoMsgs, JS::Status::RequestTimeout
573
+ # Skip these
574
+ next
575
+ else
576
+ raise JS.from_msg(msg)
577
+ end
578
+ else
579
+ msgs << msg
580
+ end
581
+ end
550
582
 
551
583
  return msgs
552
584
  end
@@ -559,10 +591,15 @@ module NATS
559
591
  # Not receiving even one is a timeout.
560
592
  start_time = MonotonicTime.now
561
593
  msg = nil
562
- synchronize {
594
+
595
+ synchronize do
563
596
  wait_for_msgs_cond.wait(timeout)
564
- msg = @pending_queue.pop unless @pending_queue.empty?
565
- }
597
+
598
+ unless @pending_queue.empty?
599
+ msg = @pending_queue.pop
600
+ @pending_size -= msg.data.size
601
+ end
602
+ end
566
603
 
567
604
  # Check if the first message was a response saying that
568
605
  # there are no messages.
@@ -574,11 +611,13 @@ module NATS
574
611
  next_req.delete(:no_wait)
575
612
 
576
613
  @nc.publish(@jsi.nms, JS.next_req_to_json(next_req), @subject)
614
+ when JS::Status::RequestTimeout
615
+ raise NATS::Timeout.new("nats: fetch request timeout")
577
616
  else
578
617
  raise JS.from_msg(msg)
579
618
  end
580
619
  else
581
- msgs << msg
620
+ msgs << msg unless msg.nil?
582
621
  end
583
622
 
584
623
  # Check if have not received yet a single message.
@@ -605,23 +644,27 @@ module NATS
605
644
  end
606
645
  else
607
646
  msg = @pending_queue.pop
647
+ @pending_size -= msg.data.size
608
648
 
609
649
  if JS.is_status_msg(msg)
610
- case msg.header["Status"]
650
+ case msg.header[JS::Header::Status]
611
651
  when JS::Status::NoMsgs, JS::Status::RequestTimeout
612
652
  duration = MonotonicTime.since(start_time)
613
653
 
614
- # Do not time out if we received at least some messages.
615
- if msgs.empty? && @pending_queue.empty? and duration > timeout
616
- raise NATS::Timeout.new("nats: fetch timeout")
654
+ if duration > timeout
655
+ # Only received a subset of the messages.
656
+ if !msgs.empty?
657
+ return msgs
658
+ else
659
+ raise NATS::Timeout.new("nats: fetch timeout")
660
+ end
617
661
  end
618
-
619
- # Likely only received a subset of the messages.
620
- return msgs
621
662
  else
622
663
  raise JS.from_msg(msg)
623
664
  end
665
+
624
666
  else
667
+ # Add to the set of messages that will be returned.
625
668
  msgs << msg
626
669
  needed -= 1
627
670
  end
data/lib/nats/io/kv.rb ADDED
@@ -0,0 +1,172 @@
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 'errors'
15
+
16
+ module NATS
17
+ class KeyValue
18
+ KV_OP = "KV-Operation"
19
+ KV_DEL = "DEL"
20
+ KV_PURGE = "PURGE"
21
+ MSG_ROLLUP_SUBJECT = "sub"
22
+ MSG_ROLLUP_ALL = "all"
23
+
24
+ def initialize(opts={})
25
+ @name = opts[:name]
26
+ @stream = opts[:stream]
27
+ @pre = opts[:pre]
28
+ @js = opts[:js]
29
+ end
30
+
31
+ # When a key is not found because it was deleted.
32
+ class KeyDeletedError < NATS::Error; end
33
+
34
+ # When there was no bucket present.
35
+ class BucketNotFoundError < NATS::Error; end
36
+
37
+ # When it is an invalid bucket.
38
+ class BadBucketError < NATS::Error; end
39
+
40
+ # get returns the latest value for the key.
41
+ def get(key)
42
+ msg = @js.get_last_msg(@stream, "#{@pre}#{key}")
43
+ entry = Entry.new(bucket: @name, key: key, value: msg.data, revision: msg.seq)
44
+
45
+ if not msg.headers.nil?
46
+ op = msg.headers[KV_OP]
47
+ raise KeyDeletedError.new("nats: key was deleted") if op == KV_DEL or op == KV_PURGE
48
+ end
49
+
50
+ entry
51
+ end
52
+
53
+ # put will place the new value for the key into the store
54
+ # and return the revision number.
55
+ def put(key, value)
56
+ @js.publish("#{@pre}#{key}", value)
57
+ end
58
+
59
+ # delete will place a delete marker and remove all previous revisions.
60
+ def delete(key)
61
+ hdrs = {}
62
+ hdrs[KV_OP] = KV_DEL
63
+ @js.publish("#{@pre}#{key}", header: hdrs)
64
+ end
65
+
66
+ # status retrieves the status and configuration of a bucket.
67
+ def status
68
+ info = @js.stream_info(@stream)
69
+ BucketStatus.new(info, @name)
70
+ end
71
+
72
+ Entry = Struct.new(:bucket, :key, :value, :revision, keyword_init: true) do
73
+ def initialize(opts={})
74
+ rem = opts.keys - members
75
+ opts.delete_if { |k| rem.include?(k) }
76
+ super(opts)
77
+ end
78
+ end
79
+
80
+ class BucketStatus
81
+ attr_reader :bucket
82
+
83
+ def initialize(info, bucket)
84
+ @nfo = info
85
+ @bucket = bucket
86
+ end
87
+
88
+ def values
89
+ @nfo.state.messages
90
+ end
91
+
92
+ def history
93
+ @nfo.config.max_msgs_per_subject
94
+ end
95
+
96
+ def ttl
97
+ @nfo.config.max_age / 1_000_000_000
98
+ end
99
+ end
100
+
101
+ module API
102
+ KeyValueConfig = Struct.new(:bucket, :description, :max_value_size,
103
+ :history, :ttl, :max_bytes, :storage, :replicas,
104
+ keyword_init: true) do
105
+ def initialize(opts={})
106
+ rem = opts.keys - members
107
+ opts.delete_if { |k| rem.include?(k) }
108
+ super(opts)
109
+ end
110
+ end
111
+ end
112
+
113
+ module Manager
114
+ def key_value(bucket)
115
+ stream = "KV_#{bucket}"
116
+ begin
117
+ si = stream_info(stream)
118
+ rescue NATS::JetStream::Error::NotFound
119
+ raise BucketNotFoundError.new("nats: bucket not found")
120
+ end
121
+ if si.config.max_msgs_per_subject < 1
122
+ raise BadBucketError.new("nats: bad bucket")
123
+ end
124
+
125
+ KeyValue.new(
126
+ name: bucket,
127
+ stream: stream,
128
+ pre: "$KV.#{bucket}.",
129
+ js: self,
130
+ )
131
+ end
132
+
133
+ def create_key_value(config)
134
+ config = if not config.is_a?(JetStream::API::StreamConfig)
135
+ KeyValue::API::KeyValueConfig.new(config)
136
+ else
137
+ config
138
+ end
139
+ config.history ||= 1
140
+ config.replicas ||= 1
141
+ if config.ttl
142
+ config.ttl = config.ttl * 1_000_000_000
143
+ end
144
+
145
+ stream = JetStream::API::StreamConfig.new(
146
+ name: "KV_#{config.bucket}",
147
+ subjects: ["$KV.#{config.bucket}.>"],
148
+ max_msgs_per_subject: config.history,
149
+ max_bytes: config.max_bytes,
150
+ max_age: config.ttl,
151
+ max_msg_size: config.max_value_size,
152
+ storage: config.storage,
153
+ num_replicas: config.replicas,
154
+ allow_rollup_hdrs: true,
155
+ deny_delete: true,
156
+ )
157
+ resp = add_stream(stream)
158
+
159
+ KeyValue.new(
160
+ name: config.bucket,
161
+ stream: stream.name,
162
+ pre: "$KV.#{config.bucket}.",
163
+ js: self,
164
+ )
165
+ end
166
+
167
+ def delete_key_value(bucket)
168
+ delete_stream("KV_#{bucket}")
169
+ end
170
+ end
171
+ end
172
+ end
@@ -1,4 +1,4 @@
1
- # Copyright 2016-2021 The NATS Authors
1
+ # Copyright 2016-2022 The NATS Authors
2
2
  # Licensed under the Apache License, Version 2.0 (the "License");
3
3
  # you may not use this file except in compliance with the License.
4
4
  # You may obtain a copy of the License at
@@ -15,7 +15,7 @@
15
15
  module NATS
16
16
  module IO
17
17
  # VERSION is the version of the client announced on CONNECT to the server.
18
- VERSION = "2.0.0-rc1".freeze
18
+ VERSION = "2.1.0".freeze
19
19
 
20
20
  # LANG is the lang runtime of the client announced on CONNECT to the server.
21
21
  LANG = "#{RUBY_ENGINE}#{RUBY_VERSION}".freeze
data/lib/nats/nuid.rb CHANGED
@@ -15,7 +15,7 @@ require 'securerandom'
15
15
 
16
16
  module NATS
17
17
  class NUID
18
- DIGITS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
18
+ DIGITS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
19
19
  BASE = 62
20
20
  PREFIX_LENGTH = 12
21
21
  SEQ_LENGTH = 10
@@ -25,6 +25,8 @@ module NATS
25
25
  MAX_INC = 333
26
26
  INC = MAX_INC - MIN_INC
27
27
 
28
+ Ractor.make_shareable(DIGITS) if defined?(Ractor)
29
+
28
30
  def initialize
29
31
  @prand = Random.new
30
32
  @seq = @prand.rand(MAX_SEQ)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nats-pure
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre.rc1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Waldemar Quevedo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-29 00:00:00.000000000 Z
11
+ date: 2022-06-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: NATS is an open-source, high-performance, lightweight cloud messaging
14
14
  system.
@@ -23,6 +23,7 @@ files:
23
23
  - lib/nats/io/client.rb
24
24
  - lib/nats/io/errors.rb
25
25
  - lib/nats/io/js.rb
26
+ - lib/nats/io/kv.rb
26
27
  - lib/nats/io/msg.rb
27
28
  - lib/nats/io/parser.rb
28
29
  - lib/nats/io/subscription.rb
@@ -43,11 +44,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
43
44
  version: '0'
44
45
  required_rubygems_version: !ruby/object:Gem::Requirement
45
46
  requirements:
46
- - - ">"
47
+ - - ">="
47
48
  - !ruby/object:Gem::Version
48
- version: 1.3.1
49
+ version: '0'
49
50
  requirements: []
50
- rubygems_version: 3.2.22
51
+ rubygems_version: 3.3.3
51
52
  signing_key:
52
53
  specification_version: 4
53
54
  summary: NATS is an open-source, high-performance, lightweight cloud messaging system.