protobuf-nats 0.12.0.pre0 → 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
2
  SHA256:
3
- metadata.gz: c2df179aaf5b89613d47a629da54127097597956a5154f6628a27155359796f0
4
- data.tar.gz: 23f4c10524cd40ec892fdfea7e8c5545590fe667111d0683cc24a9be47e32b60
3
+ metadata.gz: 34ffc46779be7a7549af169386c7212ed7210997dba1c1af19326a10aa92f98f
4
+ data.tar.gz: 9ca966e67099fc4332bcd4dc9270032f0238cf8afa7f18cb57a6c81e4307ed6e
5
5
  SHA512:
6
- metadata.gz: b4ff63c7224a17541320cbc34e196efead0bf0555f0de5aabf110b2e49bb8c0a45ffc8579c295077ace52b40ade6ee991f332df74dba2e4def0d5a4f69917376
7
- data.tar.gz: 1772c0bbafddc6364734dbe25d3618e32c93f6d5194241f1bd54e46e3239902916c9195c184ff95953a6db4d28c9e1dd85d756adc4717fda52e5b0bcef76da26
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/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
@@ -49,8 +49,6 @@ used to allow JVM based servers to warm-up slowly to prevent jolts in runtime pe
49
49
 
50
50
  `PB_NATS_CLIENT_SUBSCRIPTION_POOL_SIZE` - If subscription pooling is desired for the request/response cycle then the pool size maximum should be set; the pool is lazy and therefore will only start new subscriptions as necessary (default: 0)
51
51
 
52
- `PB_NATS_DISABLE_JNATS` - Disable the default jruby jnats client on the jruby platform, use the nats-pure.rb client instead (default: false).
53
-
54
52
  `PROTOBUF_NATS_CONFIG_PATH` - Custom path to the config yaml (default: "config/protobuf_nats.yml").
55
53
 
56
54
  ### YAML Config
@@ -150,13 +148,20 @@ And we can see the message was sent to the server and the server replied with a
150
148
  If we were to add another service endpoint called `search` to the `UserService` but fail to define an instance method
151
149
  `search`, then `protobuf-nats` will not subscribe to that route.
152
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
+
153
159
  ## Development
154
160
 
155
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.
156
162
 
157
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).
158
164
 
159
- The java-nats client is temporarily forked to support jruby > 9.2.10.0. The living branch for that is here: https://github.com/film42/java-nats/tree/jruby-compat. This will be removed when we upgrade to the new nats.java client.
160
165
 
161
166
  ## Contributing
162
167
 
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
 
@@ -1,132 +1,20 @@
1
+ require 'securerandom'
1
2
  require "connection_pool"
2
3
  require "protobuf/nats"
3
- require "protobuf/nats/platform"
4
4
  require "protobuf/rpc/connectors/base"
5
5
  require "monitor"
6
6
 
7
+ # Load this independently because we store the class singleton in a const.
8
+ require "protobuf/nats/response_muxer"
9
+
7
10
  module Protobuf
8
11
  module Nats
9
- class ResponseMuxerRequest
10
- def initialize(muxer, token)
11
- @muxer = muxer
12
- @token = token
13
- end
14
-
15
- def publish(subject, data)
16
- @muxer.publish(subject, data, @token)
17
- end
18
-
19
- def next_message(timeout)
20
- @muxer.next_message(@token, timeout)
21
- end
22
-
23
- def cleanup
24
- @muxer.cleanup(@token)
25
- end
26
- end
27
-
28
- class ResponseMuxer
29
- LOCK = ::Mutex.new
30
-
31
- def initialize
32
- @resp_map = Hash.new { |h,k| h[k] = { } }
33
- end
34
-
35
- def cleanup(token)
36
- @resp_sub.synchronize { @resp_map.delete(token) }
37
- end
38
-
39
- def next_message(token, timeout)
40
- ::NATS::MonotonicTime::with_nats_timeout(timeout) do
41
- @resp_sub.synchronize do
42
- break if @resp_map[token].key?(:response) &&
43
- !@resp_map[token][:response].empty?
44
-
45
- @resp_map[token][:signal].wait(timeout)
46
- end
47
- end
48
-
49
- @resp_sub.synchronize { @resp_map[token][:response].shift }
50
- end
51
-
52
- def new_request
53
- nats = Protobuf::Nats.client_nats_connection
54
- token = nats.new_inbox.split('.').last
55
- @resp_sub.synchronize do
56
- @resp_map[token][:signal] = @resp_sub.new_cond
57
- end
58
-
59
- ResponseMuxerRequest.new(self, token)
60
- end
61
-
62
- def publish(subject, data, token)
63
- nats = Protobuf::Nats.client_nats_connection
64
- reply_to = "#{@resp_inbox_prefix}.#{token}"
65
- nats.publish(subject, data, reply_to)
66
- end
67
-
68
- def restart
69
- start unless started?
70
-
71
- LOCK.synchronize do
72
- @resp_handler&.kill
73
- @started = false
74
- end
75
-
76
- start
77
- end
78
-
79
- def start
80
- return if started?
81
- LOCK.synchronize do
82
- # We check this twice in case another thread was waiting for the lock to
83
- # start this party.
84
- return if started?
85
-
86
- nats = ::Protobuf::Nats.client_nats_connection
87
- return if nats.nil?
88
-
89
- @resp_inbox_prefix = nats.new_inbox
90
- @resp_sub = nats.subscribe("#{@resp_inbox_prefix}.*")
91
- @started = true
92
- end
93
-
94
- @resp_handler = Thread.new do
95
- begin
96
- loop do
97
- msg = @resp_sub.pending_queue.pop
98
- next if msg.nil?
99
- @resp_sub.synchronize do
100
- # Decrease pending size since consumed already
101
- @resp_sub.pending_size -= msg.data.size
102
- end
103
- token = msg.subject.split('.').last
104
-
105
- @resp_sub.synchronize do
106
- # Reject if the token is missing from the request map
107
- break unless @resp_map.key?(token)
108
-
109
- signal = @resp_map[token][:signal]
110
- @resp_map[token][:response] ||= []
111
- @resp_map[token][:response] << msg
112
- signal.signal
113
- end
114
- rescue => error
115
- ::Protobuf::Nats.notify_error_callbacks(error)
116
- LOCK.synchronize { @started = false }
117
- end
118
- end
119
- end
120
- end
121
-
122
- def started?
123
- !!@started
124
- end
125
- end
126
-
127
12
  class Client < ::Protobuf::Rpc::Connectors::Base
128
13
 
129
- RESPONSE_MUXER = ResponseMuxer.new
14
+ RESPONSE_MUXER = ::Protobuf::Nats::ResponseMuxer.new
15
+
16
+ @subscription_key_cache = {}
17
+ @subscription_pool_lock = ::Mutex.new
130
18
 
131
19
  # Structure to hold subscription and inbox to use within pool
132
20
  SubscriptionInbox = ::Struct.new(:subscription, :inbox) do
@@ -136,11 +24,26 @@ module Protobuf
136
24
  end
137
25
  end
138
26
 
27
+ def logger
28
+ ::Protobuf::Logging.logger
29
+ end
30
+
31
+ def response_muxer
32
+ RESPONSE_MUXER
33
+ end
34
+
139
35
  def self.subscription_pool
140
- @subscription_pool ||= ::ConnectionPool.new(:size => subscription_pool_size, :timeout => 0.1) do
141
- inbox = ::Protobuf::Nats.client_nats_connection.new_inbox
36
+ return @subscription_pool if @subscription_pool
142
37
 
143
- SubscriptionInbox.new(::Protobuf::Nats.client_nats_connection.subscribe(inbox), inbox)
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
42
+
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
144
47
  end
145
48
  end
146
49
 
@@ -194,7 +97,7 @@ module Protobuf
194
97
  end
195
98
 
196
99
  def self.subscription_key_cache
197
- @subscription_key_cache ||= {}
100
+ @subscription_key_cache
198
101
  end
199
102
 
200
103
  def ack_timeout
@@ -317,112 +220,59 @@ module Protobuf
317
220
  "#{klass}##{method_name}"
318
221
  end
319
222
 
320
- # The Java nats client offers better message queueing so we're going to use
321
- # that over locking ourselves. This split in code isn't great, but we can
322
- # refactor this later.
323
- if ::Protobuf::Nats.jruby?
324
-
325
- # This is a request that expects two responses.
326
- # 1. An ACK from the server. We use a shorter timeout.
327
- # 2. A PB message from the server. We use a longer timoeut.
328
- def nats_request_with_two_responses(subject, data, opts)
329
- # Wait for the ACK from the server
330
- ack_timeout = opts[:ack_timeout] || 5
331
- # Wait for the protobuf response
332
- timeout = opts[:timeout] || 60
333
-
334
- nats = ::Protobuf::Nats.client_nats_connection
335
-
336
- # Publish to server
337
- with_subscription do |sub_inbox|
338
- begin
339
- completed_request = false
340
-
341
- if !sub_inbox.subscription.is_valid # replace the subscription if is has been pooled but is no longer valid (maybe a reconnect)
342
- nats.unsubscribe(sub_inbox.subscription)
343
- sub_inbox.swap(new_subscription_inbox) # this line replaces the sub_inbox in the connection pool if necessary
344
- end
345
-
346
- nats.publish(subject, data, sub_inbox.inbox)
347
-
348
- # Wait for reply
349
- first_message = nats.next_message(sub_inbox.subscription, ack_timeout)
350
- return :ack_timeout if first_message.nil?
351
-
352
- first_message_data = first_message.data
353
- return :nack if first_message_data == ::Protobuf::Nats::Messages::NACK
354
-
355
- second_message = nats.next_message(sub_inbox.subscription, timeout)
356
- 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
357
228
 
358
- # Check messages
359
- response = case ::Protobuf::Nats::Messages::ACK
360
- when first_message_data then second_message_data
361
- when second_message_data then first_message_data
362
- else return :ack_timeout
363
- end
229
+ nats = Protobuf::Nats.client_nats_connection
364
230
 
365
- fail(::Protobuf::Nats::Errors::ResponseTimeout, formatted_service_and_method_name) 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)
366
234
 
367
- completed_request = true
368
- response
369
- ensure
370
- if !completed_request
371
- nats.unsubscribe(sub_inbox.subscription)
372
- sub_inbox.swap(new_subscription_inbox) # this line replaces the sub_inbox in the connection pool if necessary
373
- end
374
- end
375
- 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
376
241
  end
377
242
 
378
- else
379
-
380
- def nats_request_with_two_responses(subject, data, opts)
381
- # Wait for the ACK from the server
382
- ack_timeout = opts[:ack_timeout] || 5
383
- # Wait for the protobuf response
384
- timeout = opts[:timeout] || 60
243
+ # Check for a NACK
244
+ return :nack if first_message.data == ::Protobuf::Nats::Messages::NACK
385
245
 
386
- nats = Protobuf::Nats.client_nats_connection
387
-
388
- # Publish message with the reply topic pointed at the response muxer.
389
- req = RESPONSE_MUXER.new_request
390
- req.publish(subject, data)
391
-
392
- # Receive the first message
393
- begin
394
- first_message = req.next_message(ack_timeout)
395
- rescue ::NATS::Timeout => e
396
- return :ack_timeout
397
- end
398
-
399
- # Check for a NACK
400
- return :nack if first_message.data == ::Protobuf::Nats::Messages::NACK
401
-
402
- # Receive the second message
403
- begin
404
- second_message = req.next_message(timeout)
405
- rescue ::NATS::Timeout
406
- # ignore to raise a repsonse timeout below
407
- end
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
408
252
 
409
- # NOTE: This might be nil, so be careful checking the data value
410
- second_message_data = second_message&.data
253
+ # NOTE: This might be nil, so be careful checking the data value
254
+ second_message_data = second_message&.data
411
255
 
412
- # Check messages
413
- response = case ::Protobuf::Nats::Messages::ACK
414
- when first_message.data then second_message_data
415
- when second_message_data then first_message.data
416
- else return :ack_timeout
417
- 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
418
262
 
419
- fail(::Protobuf::Nats::Errors::ResponseTimeout, formatted_service_and_method_name) 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
420
269
 
421
- response
422
- ensure
423
- req.cleanup if req
424
- end
270
+ fail(::Protobuf::Nats::Errors::ResponseTimeout, formatted_service_and_method_name) unless response
425
271
 
272
+ response
273
+ ensure
274
+ # cleanup the token from the request map
275
+ req.cleanup if req
426
276
  end
427
277
 
428
278
  end
@@ -1,3 +1,4 @@
1
+ require "erb"
1
2
  require "openssl"
2
3
  require "yaml"
3
4
 
@@ -39,12 +40,13 @@ 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
+ if ::File.exist?(absolute_config_path)
44
+ yaml_string = ::ERB.new(::File.read(absolute_config_path)).result
43
45
  # Psych 4 and newer requires unsafe_load_file in order for aliases to be used
44
46
  yaml_config = if ::YAML.respond_to?(:unsafe_load_file)
45
- ::YAML.unsafe_load_file(absolute_config_path)[env]
47
+ ::YAML.unsafe_load(yaml_string)[env]
46
48
  else
47
- ::YAML.load_file(absolute_config_path)[env]
49
+ ::YAML.load(yaml_string)[env]
48
50
  end
49
51
  end
50
52
 
@@ -10,14 +10,13 @@ module Protobuf
10
10
  class ResponseTimeout < ClientError
11
11
  end
12
12
 
13
+ class ResponseMuxer < ClientError
14
+ end
15
+
13
16
  class MriIOException < ::StandardError
14
17
  end
15
18
 
16
- IOException = if defined? JRUBY_VERSION
17
- java.io.IOException
18
- else
19
- MriIOException
20
- end
19
+ IOException = MriIOException
21
20
  end
22
21
  end
23
22
  end