protobuf-nats 0.11.0.pre1 → 0.13.0.pre0

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
- SHA1:
3
- metadata.gz: 983e924ae43d3fca4ccdfc73d8715c5c01b3f2bb
4
- data.tar.gz: 9b88a322f6a04be4f2a0392b32555ecc9f29f284
2
+ SHA256:
3
+ metadata.gz: 34ffc46779be7a7549af169386c7212ed7210997dba1c1af19326a10aa92f98f
4
+ data.tar.gz: 9ca966e67099fc4332bcd4dc9270032f0238cf8afa7f18cb57a6c81e4307ed6e
5
5
  SHA512:
6
- metadata.gz: 211f48f2481d731e09593fb926f87cf2d93b92e32ceab6e27952735c6da9ee30ea9f2d2bdb5edd9c3986e8efeb0180782f03451d318f3df086887fe49fe133a2
7
- data.tar.gz: 0e70e256836e424a011cef424b9f589b36c4f3bd1d203c29db9e24e58e885043f6d409af531cfb9b29ec57087c179c5cfb83a29c9045380db3dd9920c95b32ec
6
+ metadata.gz: 4d11137ae46ebfe8a1f003fda41aeba78d4a23893efafb38ad803969ba44df594dd3b0d0b5289e6bface579458baea1327749919f92ecbbbf6e3acbd2235c020
7
+ data.tar.gz: 5f6b0a643ea4d85063232800dff9fefba05ac70b8787666253244afa474f8314ae6de67b2f10efb970884c47150ce404a78dbf9d18b8ac425727f2172196f836
@@ -0,0 +1,84 @@
1
+ version: 2.1
2
+
3
+ jobs:
4
+ build_and_test:
5
+ parameters:
6
+ docker_image:
7
+ type: string
8
+ description: "The Ruby or JRuby Docker image to test against"
9
+
10
+ docker:
11
+ # 1. The Primary Container (where your code actually runs)
12
+ - image: << parameters.docker_image >>
13
+ environment:
14
+ JRUBY_OPTS: "-J-Xmx1024m"
15
+ RAILS_ENV: test
16
+
17
+ # 2. The Service Container (runs in the background)
18
+ - image: nats:2.14-linux
19
+
20
+ working_directory: ~/project
21
+
22
+ steps:
23
+ - run:
24
+ name: Install System Dependencies
25
+ command: |
26
+ if [ "$(id -u)" = "0" ]; then
27
+ apt-get update && apt-get install -y build-essential git
28
+ else
29
+ sudo apt-get update && sudo apt-get install -y build-essential git
30
+ fi
31
+ - checkout
32
+ # Note: We added the docker_image parameter to the cache key
33
+ # so MRI and JRuby gems don't conflict.
34
+ - restore_cache:
35
+ keys:
36
+ - v1-gems-<< parameters.docker_image >>-{{ checksum "Gemfile.lock" }}
37
+ - v1-gems-<< parameters.docker_image >>-
38
+
39
+ - run:
40
+ name: Install Ruby Dependencies
41
+ command: |
42
+ gem install bundler
43
+ bundle config set --local path 'vendor/bundle'
44
+ bundle install --jobs=4 --retry=3
45
+
46
+ - save_cache:
47
+ paths:
48
+ - ./vendor/bundle
49
+ key: v1-gems-<< parameters.docker_image >>-{{ checksum "Gemfile.lock" }}
50
+
51
+ # Wait for NATS to be ready before running tests.
52
+ # Service containers can sometimes take a few seconds to boot up.
53
+ - run:
54
+ name: Wait for NATS
55
+ command: |
56
+ if [ "$(id -u)" = "0" ]; then
57
+ apt-get install -y netcat-openbsd
58
+ else
59
+ sudo apt-get install -y netcat-openbsd
60
+ fi
61
+
62
+ echo "Waiting for NATS to start..."
63
+ while ! nc -z localhost 4222; do
64
+ sleep 1
65
+ done
66
+ echo "NATS is ready!"
67
+
68
+ - run:
69
+ name: Run Tests
70
+ command: bundle exec rspec
71
+
72
+ workflows:
73
+ version: 2
74
+ ruby_compatibility_matrix:
75
+ jobs:
76
+ - build_and_test:
77
+ name: test-<< matrix.docker_image >>
78
+ matrix:
79
+ parameters:
80
+ docker_image:
81
+ - "cimg/ruby:3.1"
82
+ - "cimg/ruby:3.4"
83
+ - "jruby:9.4"
84
+ - "jruby:10.0"
data/.travis.yml CHANGED
@@ -1,12 +1,16 @@
1
1
  sudo: false
2
2
  language: ruby
3
- jdk: openjdk8
3
+ jdk:
4
+ - openjdk8
4
5
  rvm:
5
6
  - 2.3.0
7
+ - 2.7.0
6
8
  - jruby-9.1.7.0
9
+ - jruby-9.2.13.0
7
10
  before_install:
11
+ # Install and start gnatsd
12
+ - ./scripts/install_gnatsd.sh
13
+ - $HOME/nats-server/nats-server &
14
+ # Install deps for project
8
15
  - gem install bundler
9
16
  - gem update --system
10
- - wget https://github.com/nats-io/gnatsd/releases/download/v1.3.0/gnatsd-v1.3.0-linux-amd64.zip
11
- - unzip gnatsd-v1.3.0-linux-amd64.zip
12
- - ./gnatsd-v1.3.0-linux-amd64/gnatsd &
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## Changelog
2
+
3
+ ### 0.13.0 - WIP
4
+ - Removed JNats (`nats-pure` is fast enough for JRuby and CRuby parallel work)
5
+ - Added ResponseMuxer (similar to Golang)
6
+ - Added instrumentation when encountering unexpected messages.
7
+
data/README.md CHANGED
@@ -148,15 +148,24 @@ And we can see the message was sent to the server and the server replied with a
148
148
  If we were to add another service endpoint called `search` to the `UserService` but fail to define an instance method
149
149
  `search`, then `protobuf-nats` will not subscribe to that route.
150
150
 
151
+ ## Future Improvements (locked behind ruby version)
152
+ - Migrate to native `Random.new.uuid_v7`
153
+ ```ruby
154
+ @prng_lock.synchronize { @prng.uuid_v7(extra_timestamp_bits: 12) }
155
+ ```
156
+ - Change ResponseMuxer to use `.pop()` with a timeout.
157
+
158
+
151
159
  ## Development
152
160
 
153
161
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
154
162
 
155
163
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
156
164
 
165
+
157
166
  ## Contributing
158
167
 
159
- Bug reports and pull requests are welcome on GitHub at https://github.com/abrandoned/protobuf-nats.
168
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mxenabled/protobuf-nats.
160
169
 
161
170
 
162
171
  ## License
data/bench/bench.md ADDED
@@ -0,0 +1,16 @@
1
+
2
+ Notes:
3
+ `-Xjit.threshold=0` - Setting the threshold to 0 forces JRuby to compile every method into Java bytecode immediately before its very first execution. This is particularly useful for debugging or bypassing warm-up times during profiling
4
+
5
+
6
+ `-Xjit.threshold=10 -J-XX:CompileThreshold=10` - If you are running benchmarks and want both JRuby and the JVM to aggressively optimize early, you can lower both thresholds simultaneously
7
+
8
+ `bundle; bx ruby -I lib bench/real_client.rb`
9
+
10
+ Start local nats server so details can be monitored.
11
+ `/opt/homebrew/opt/nats-server/bin/nats-server -DV -m 8222 -p 4222`
12
+
13
+
14
+ ```
15
+ export JRUBY_OPTS="--disable:did_you_mean -J-Djava.security.egd=file:/dev/./urandom -J-Xmx2g -J-Xms1024m -J-Xmn512m -Xjit.threshold=10 -J-XX:CompileThreshold=10"
16
+ ```
data/bench/console.rb ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "protobuf/nats"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ # ENV["PB_CLIENT_TYPE"] = "protobuf/nats/client"
14
+ # ENV["PB_SERVER_TYPE"] = "protobuf/nats/runner"
15
+
16
+ require "irb"
17
+ IRB.start(__FILE__)
data/bench/real_client.rb CHANGED
@@ -7,8 +7,8 @@ require "./examples/warehouse/app"
7
7
  Protobuf::Logging.logger = ::Logger.new(nil)
8
8
 
9
9
  Benchmark.ips do |config|
10
- config.warmup = 10
11
- config.time = 10
10
+ config.warmup = 30
11
+ config.time = 30
12
12
 
13
13
  config.report("single threaded performance") do
14
14
  req = Warehouse::Shipment.new(:guid => SecureRandom.uuid)
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+
3
+ export JRUBY_OPTS="-J-server --disable:did_you_mean -Xcompile.invokedynamic=true -Xjit.threshold=10 -J-Djava.security.egd=file:/dev/./urandom -J-Xms2g -J-Xmx2g -J-XX:+UseG1GC -J-XX:MaxGCPauseMillis=100"
4
+
5
+ # export JRUBY_OPTS="-J-server --disable:did_you_mean -Xcompile.invokedynamic=true -Xjit.threshold=0 -J-Djruby.jit.max=0 -J-Djruby.jit.background=false -J-Djava.security.egd=file:/dev/./urandom -J-Xms2g -J-Xmx2g -J-XX:+UseG1GC -J-XX:MaxGCPauseMillis=100"
6
+
7
+
8
+ export PB_SERVER_TYPE="protobuf/nats/runner"
9
+ export PB_CLIENT_TYPE="protobuf/nats/client"
10
+
11
+ echo "$PWD"
12
+
13
+ bundle exec ruby -I lib bench/real_client.rb
@@ -0,0 +1,19 @@
1
+ ENV["PB_CLIENT_TYPE"] = "protobuf/nats/client"
2
+ ENV["PB_SERVER_TYPE"] = "protobuf/nats/runner"
3
+
4
+ require "./examples/warehouse/app"
5
+
6
+ THREAD_COUNT = ENV.fetch("CLIENT_THREADS",4).to_i
7
+
8
+ puts "THREAD_COUNT = #{THREAD_COUNT}"
9
+
10
+ ::Protobuf::Logging.logger = ::Logger.new(nil)
11
+
12
+ while true
13
+ THREAD_COUNT.times.map do |i|
14
+ Thread.new do
15
+ req = Warehouse::Shipment.new(:guid => SecureRandom.uuid, :sleep_time_ms => 5)
16
+ Warehouse::ShipmentService.client.create(req)
17
+ end
18
+ end.each(&:join)
19
+ end
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ export JRUBY_OPTS="-J-server --disable:did_you_mean -Xcompile.invokedynamic=true -Xjit.threshold=10 -J-Djava.security.egd=file:/dev/./urandom -J-Xms2g -J-Xmx2g -J-XX:+UseG1GC -J-XX:MaxGCPauseMillis=100"
4
+
5
+ export PB_SERVER_TYPE="protobuf/nats/runner"
6
+ export PB_CLIENT_TYPE="protobuf/nats/client"
7
+
8
+ export THREAD_COUNT=4
9
+
10
+ bundle exec ruby -I lib bench/real_client_threaded.rb
data/bench/real_server.sh CHANGED
@@ -1 +1,16 @@
1
- PB_SERVER_TYPE="protobuf/nats/runner" PB_CLIENT_TYPE="protobuf/nats/client" bundle exec rpc_server start --threads=20 ./examples/warehouse/app.rb > /dev/null
1
+ #!/bin/bash
2
+
3
+ # export JRUBY_OPTS="-J-server --disable:did_you_mean -Xcompile.invokedynamic=true -Xjit.threshold=0 -J-Djruby.jit.max=0 -J-Djruby.jit.background=false -J-Djava.security.egd=file:/dev/./urandom -J-Xms2g -J-Xmx2g -J-XX:+UseG1GC -J-XX:MaxGCPauseMillis=100"
4
+
5
+ export JRUBY_OPTS="-J-server -J-Xms4g -J-Xmx4g -J-XX:+AlwaysPreTouch -J-XX:+UseParallelGC -J-XX:ReservedCodeCacheSize=768m -J-XX:MaxInlineLevel=18 -J-XX:MaxInlineSize=100 -J-XX:FreqInlineSize=500 -J-XX:LoopUnrollLimit=250 -J-XX:+UseSuperWord -J-Djruby.jit.threshold=0 -J-Djruby.jit.max=0 -J-Djruby.jit.background=false -Xcompile.invokedynamic=true"
6
+
7
+
8
+ export PB_SERVER_TYPE="protobuf/nats/runner"
9
+ export PB_CLIENT_TYPE="protobuf/nats/client"
10
+
11
+ export PB_NATS_SERVER_SLOW_START_DELAY=1
12
+
13
+ export PB_NATS_SERVER_MAX_QUEUE_SIZE=6
14
+
15
+ bundle exec rpc_server start --threads=10 ./examples/warehouse/app.rb
16
+
@@ -12,7 +12,6 @@ module Warehouse
12
12
  class ShipmentRequest < ::Protobuf::Message; end
13
13
  class Shipments < ::Protobuf::Message; end
14
14
 
15
-
16
15
  ##
17
16
  # Message Fields
18
17
  #
@@ -21,6 +20,8 @@ module Warehouse
21
20
  optional :string, :address, 2
22
21
  optional :double, :price, 3
23
22
  optional :string, :package_guid, 4
23
+
24
+ optional :int64, :sleep_time_ms, 100
24
25
  end
25
26
 
26
27
  class ShipmentRequest
@@ -33,7 +34,6 @@ module Warehouse
33
34
  repeated ::Warehouse::Shipment, :records, 1
34
35
  end
35
36
 
36
-
37
37
  ##
38
38
  # Service Classes
39
39
  #
@@ -43,6 +43,12 @@ module Warehouse
43
43
  rpc :search, ::Warehouse::ShipmentRequest, ::Warehouse::Shipments
44
44
 
45
45
  def create
46
+ # Allows for easier testing of multiple threads
47
+ if request.sleep_time_ms > 0
48
+ sleep(request.sleep_time_ms / 1000.0)
49
+ puts "sleep_time:#{request.sleep_time_ms}"
50
+ end
51
+
46
52
  respond_with request
47
53
  end
48
54
 
Binary file
@@ -1,11 +1,21 @@
1
+ require 'securerandom'
1
2
  require "connection_pool"
2
3
  require "protobuf/nats"
3
4
  require "protobuf/rpc/connectors/base"
4
5
  require "monitor"
5
6
 
7
+ # Load this independently because we store the class singleton in a const.
8
+ require "protobuf/nats/response_muxer"
9
+
6
10
  module Protobuf
7
11
  module Nats
8
12
  class Client < ::Protobuf::Rpc::Connectors::Base
13
+
14
+ RESPONSE_MUXER = ::Protobuf::Nats::ResponseMuxer.new
15
+
16
+ @subscription_key_cache = {}
17
+ @subscription_pool_lock = ::Mutex.new
18
+
9
19
  # Structure to hold subscription and inbox to use within pool
10
20
  SubscriptionInbox = ::Struct.new(:subscription, :inbox) do
11
21
  def swap(sub_inbox)
@@ -14,11 +24,26 @@ module Protobuf
14
24
  end
15
25
  end
16
26
 
27
+ def logger
28
+ ::Protobuf::Logging.logger
29
+ end
30
+
31
+ def response_muxer
32
+ RESPONSE_MUXER
33
+ end
34
+
17
35
  def self.subscription_pool
18
- @subscription_pool ||= ::ConnectionPool.new(:size => subscription_pool_size, :timeout => 0.1) do
19
- inbox = ::Protobuf::Nats.client_nats_connection.new_inbox
36
+ return @subscription_pool if @subscription_pool
37
+
38
+ @subscription_pool_lock.synchronize do
39
+ # The double-check ensures we don't create a new pool if another
40
+ # thread created one while we were waiting for the lock.
41
+ return @subscription_pool if @subscription_pool
20
42
 
21
- SubscriptionInbox.new(::Protobuf::Nats.client_nats_connection.subscribe(inbox), inbox)
43
+ @subscription_pool = ::ConnectionPool.new(:size => subscription_pool_size, :timeout => 0.1) do
44
+ inbox = ::Protobuf::Nats.client_nats_connection.new_inbox
45
+ SubscriptionInbox.new(::Protobuf::Nats.client_nats_connection.subscribe(inbox), inbox)
46
+ end
22
47
  end
23
48
  end
24
49
 
@@ -36,6 +61,9 @@ module Protobuf
36
61
 
37
62
  # This will ensure the client is started.
38
63
  ::Protobuf::Nats.start_client_nats_connection
64
+
65
+ # Ensure the response muxer is started
66
+ RESPONSE_MUXER.start
39
67
  end
40
68
 
41
69
  def new_subscription_inbox
@@ -69,7 +97,7 @@ module Protobuf
69
97
  end
70
98
 
71
99
  def self.subscription_key_cache
72
- @subscription_key_cache ||= {}
100
+ @subscription_key_cache
73
101
  end
74
102
 
75
103
  def ack_timeout
@@ -151,12 +179,12 @@ module Protobuf
151
179
  when :ack_timeout
152
180
  ::ActiveSupport::Notifications.instrument "client.request_timeout.protobuf-nats"
153
181
  next if (retries -= 1) > 0
154
- raise ::Protobuf::Nats::Errors::RequestTimeout
182
+ raise ::Protobuf::Nats::Errors::RequestTimeout, formatted_service_and_method_name
155
183
  when :nack
156
184
  ::ActiveSupport::Notifications.instrument "client.request_nack.protobuf-nats"
157
185
  interval = nack_backoff_intervals[nack_retry]
158
186
  nack_retry += 1
159
- raise ::Protobuf::Nats::Errors::RequestTimeout if interval.nil?
187
+ raise ::Protobuf::Nats::Errors::RequestTimeout, formatted_service_and_method_name if interval.nil?
160
188
  sleep((interval + nack_backoff_splay)/1000.0)
161
189
  next
162
190
  end
@@ -165,11 +193,11 @@ module Protobuf
165
193
  end
166
194
 
167
195
  parse_response
168
- rescue ::Protobuf::Nats::Errors::IOException, ::Protobuf::Nats::Errors::IllegalStateException => error
196
+ rescue ::Protobuf::Nats::Errors::IOException => error
169
197
  ::Protobuf::Nats.log_error(error)
170
198
 
171
199
  delay = reconnect_delay
172
- logger.warn "An #{error.class} was raised. We are going to sleep for #{delay} seconds."
200
+ logger.warn "An IOException was raised. We are going to sleep for #{delay} seconds."
173
201
  sleep delay
174
202
 
175
203
  retry if (retries -= 1) > 0
@@ -186,115 +214,65 @@ module Protobuf
186
214
  end
187
215
  end
188
216
 
189
- # The Java nats client offers better message queueing so we're going to use
190
- # that over locking ourselves. This split in code isn't great, but we can
191
- # refactor this later.
192
- if defined? JRUBY_VERSION
193
-
194
- # This is a request that expects two responses.
195
- # 1. An ACK from the server. We use a shorter timeout.
196
- # 2. A PB message from the server. We use a longer timoeut.
197
- def nats_request_with_two_responses(subject, data, opts)
198
- # Wait for the ACK from the server
199
- ack_timeout = opts[:ack_timeout] || 5
200
- # Wait for the protobuf response
201
- timeout = opts[:timeout] || 60
202
-
203
- nats = ::Protobuf::Nats.client_nats_connection
204
-
205
- # Publish to server
206
- with_subscription do |sub_inbox|
207
- begin
208
- completed_request = false
209
-
210
- if !sub_inbox.subscription.is_active # replace the subscription if is has been pooled but is no longer valid (maybe a reconnect)
211
- nats.unsubscribe(sub_inbox.subscription)
212
- sub_inbox.swap(new_subscription_inbox) # this line replaces the sub_inbox in the connection pool if necessary
213
- end
214
-
215
- nats.publish(subject, data, sub_inbox.inbox)
216
-
217
- # Wait for reply
218
- first_message = nats.next_message(sub_inbox.subscription, ack_timeout)
219
- return :ack_timeout if first_message.nil?
220
-
221
- first_message_data = first_message.data
222
- return :nack if first_message_data == ::Protobuf::Nats::Messages::NACK
217
+ def formatted_service_and_method_name
218
+ klass = @options[:service]
219
+ method_name = @options[:method]
220
+ "#{klass}##{method_name}"
221
+ end
223
222
 
224
- second_message = nats.next_message(sub_inbox.subscription, timeout)
225
- second_message_data = second_message.nil? ? nil : second_message.data
223
+ def nats_request_with_two_responses(subject, data, opts)
224
+ # Wait for the ACK from the server
225
+ ack_timeout = opts[:ack_timeout] || 5
226
+ # Wait for the protobuf response
227
+ timeout = opts[:timeout] || 60
226
228
 
227
- # Check messages
228
- response = case ::Protobuf::Nats::Messages::ACK
229
- when first_message_data then second_message_data
230
- when second_message_data then first_message_data
231
- else return :ack_timeout
232
- end
229
+ nats = Protobuf::Nats.client_nats_connection
233
230
 
234
- fail(::Protobuf::Nats::Errors::ResponseTimeout, subject) unless response
231
+ # Publish message with the reply topic pointed at the response muxer.
232
+ req = RESPONSE_MUXER.new_request
233
+ req.publish(subject, data)
235
234
 
236
- completed_request = true
237
- response
238
- ensure
239
- if !completed_request
240
- nats.unsubscribe(sub_inbox.subscription)
241
- sub_inbox.swap(new_subscription_inbox) # this line replaces the sub_inbox in the connection pool if necessary
242
- end
243
- end
244
- end
235
+ # Receive the first message
236
+ begin
237
+ first_message = req.next_message(ack_timeout)
238
+ logger.debug { "received message with subject:#{first_message.subject}" } if logger.debug?
239
+ rescue ::NATS::Timeout => e
240
+ return :ack_timeout
245
241
  end
246
242
 
247
- else
248
-
249
- def nats_request_with_two_responses(subject, data, opts)
250
- nats = Protobuf::Nats.client_nats_connection
251
- inbox = nats.new_inbox
252
- lock = ::Monitor.new
253
- received = lock.new_cond
254
- messages = []
255
- first_message = nil
256
- second_message = nil
257
- response = nil
258
-
259
- sid = nats.subscribe(inbox, :max => 2) do |message, _, _|
260
- lock.synchronize do
261
- messages << message
262
- received.signal
263
- end
264
- end
243
+ # Check for a NACK
244
+ return :nack if first_message.data == ::Protobuf::Nats::Messages::NACK
265
245
 
266
- lock.synchronize do
267
- # Publish to server
268
- nats.publish(subject, data, inbox)
269
-
270
- # Wait for the ACK from the server
271
- ack_timeout = opts[:ack_timeout] || 5
272
- received.wait(ack_timeout) if messages.empty?
273
- first_message = messages.shift
274
-
275
- return :ack_timeout if first_message.nil?
276
- return :nack if first_message == ::Protobuf::Nats::Messages::NACK
246
+ # Receive the second message
247
+ begin
248
+ second_message = req.next_message(timeout)
249
+ rescue ::NATS::Timeout
250
+ # ignore to raise a repsonse timeout below
251
+ end
277
252
 
278
- # Wait for the protobuf response
279
- timeout = opts[:timeout] || 60
280
- received.wait(timeout) if messages.empty?
281
- second_message = messages.shift
282
- end
253
+ # NOTE: This might be nil, so be careful checking the data value
254
+ second_message_data = second_message&.data
283
255
 
284
- response = case ::Protobuf::Nats::Messages::ACK
285
- when first_message then second_message
286
- when second_message then first_message
287
- else return :ack_timeout
288
- end
256
+ # This should never happen, if it does, then return an :ack_timeout because something went wrong
257
+ if first_message&.data == ::Protobuf::Nats::Messages::ACK &&
258
+ second_message&.data == ::Protobuf::Nats::Messages::ACK
259
+ logger.warn "received ACK/ACK message."
260
+ return :ack_timeout
261
+ end
289
262
 
290
- fail(::Protobuf::Nats::Errors::ResponseTimeout, subject) unless response
263
+ # Check messages
264
+ response = case ::Protobuf::Nats::Messages::ACK
265
+ when first_message&.data then second_message_data
266
+ when second_message&.data then first_message&.data
267
+ else return :ack_timeout
268
+ end
291
269
 
292
- response
293
- ensure
294
- # Ensure we don't leave a subscription sitting around.
295
- nats.unsubscribe(sid) if response.nil?
296
- end
270
+ fail(::Protobuf::Nats::Errors::ResponseTimeout, formatted_service_and_method_name) unless response
297
271
 
272
+ response
273
+ ensure
274
+ # cleanup the token from the request map
275
+ req.cleanup if req
298
276
  end
299
277
 
300
278
  end
@@ -1,3 +1,4 @@
1
+ require "erb"
1
2
  require "openssl"
2
3
  require "yaml"
3
4
 
@@ -39,8 +40,14 @@ module Protobuf
39
40
  yaml_config = {}
40
41
  config_path = ENV["PROTOBUF_NATS_CONFIG_PATH"] || ::File.join("config", "protobuf_nats.yml")
41
42
  absolute_config_path = ::File.expand_path(config_path)
42
- if ::File.exists?(absolute_config_path)
43
- yaml_config = ::YAML.load_file(absolute_config_path)[env]
43
+ if ::File.exist?(absolute_config_path)
44
+ yaml_string = ::ERB.new(::File.read(absolute_config_path)).result
45
+ # Psych 4 and newer requires unsafe_load_file in order for aliases to be used
46
+ yaml_config = if ::YAML.respond_to?(:unsafe_load_file)
47
+ ::YAML.unsafe_load(yaml_string)[env]
48
+ else
49
+ ::YAML.load(yaml_string)[env]
50
+ end
44
51
  end
45
52
 
46
53
  DEFAULTS.each_pair do |key, value|
@@ -1,32 +1,22 @@
1
1
  module Protobuf
2
2
  module Nats
3
3
  module Errors
4
- class Base < ::StandardError
4
+ class ClientError < ::StandardError
5
5
  end
6
6
 
7
- class RequestTimeout < Base
7
+ class RequestTimeout < ClientError
8
8
  end
9
9
 
10
- class ResponseTimeout < Base
10
+ class ResponseTimeout < ClientError
11
11
  end
12
12
 
13
- class MriIOException < ::StandardError
13
+ class ResponseMuxer < ClientError
14
14
  end
15
15
 
16
- class MriIllegalStateException < ::StandardError
16
+ class MriIOException < ::StandardError
17
17
  end
18
18
 
19
- IllegalStateException = if defined? JRUBY_VERSION
20
- java.lang.IllegalStateException
21
- else
22
- MriIllegalStateException
23
- end
24
-
25
- IOException = if defined? JRUBY_VERSION
26
- java.io.IOException
27
- else
28
- MriIOException
29
- end
19
+ IOException = MriIOException
30
20
  end
31
21
  end
32
22
  end