nats-pure 2.0.0.pre.rc1 → 2.1.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: 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.