nats-pure 0.3.0 → 0.4.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: 6511a5fc9c87e5a783322e9fe0b02f66a2b36d47c1ea806208877ec2c63c6a23
4
- data.tar.gz: bfdc75088d4a39cf5e020248664d72e3ae2cdc774a26a69fcc15ddf67727e531
3
+ metadata.gz: 59129f0c5e0180f95490a2ca5e2acb8cf0c1d630c32e4d4ab6f0cf859242500b
4
+ data.tar.gz: c23db05317523a59762b9b2e73917165baf8ee3a898aaa74e84a74b0977c1942
5
5
  SHA512:
6
- metadata.gz: 300ab49335e50800e0dc0c5971dd5b96682a7e0a5be9c9f09edf10f0a1c97a3c3f10749d1b87276cd4a709d724c8e3c627ef239a5fc49ff3cfff9f2e32ac71f7
7
- data.tar.gz: 49a3618d2dc8ab3fd7844d4e7d98035c9fbfdb68e0f336e94437edeec0572197437cc02d14f136b006b52a641a8f588cd4abaec688f1b6252efb8e361391b9a5
6
+ metadata.gz: b82fdc592bea6ddc5ca1e0792b76071dd09db70a86fcaef517207d33c339d1b105076efd2b3d3cc83960a6b470739d01ec9d2f41574f57111935e0245eb02e5d
7
+ data.tar.gz: 4439c2f2f0c3c6118b5a4b93587a60ecb069f8b85e0907539bec6a497a8394a1f78a9a961aa2565436e7c7ab4ee3bc1bd459310935499da67c1d7e7c11a0d5c7
@@ -39,6 +39,10 @@ module NATS
39
39
  DEFAULT_CONNECT_TIMEOUT = 2
40
40
  DEFAULT_READ_WRITE_TIMEOUT = 2
41
41
 
42
+ # Default Pending Limits
43
+ DEFAULT_SUB_PENDING_MSGS_LIMIT = 65536
44
+ DEFAULT_SUB_PENDING_BYTES_LIMIT = 65536 * 1024
45
+
42
46
  CR_LF = ("\r\n".freeze)
43
47
  CR_LF_SIZE = (CR_LF.bytesize)
44
48
 
@@ -84,6 +88,9 @@ module NATS
84
88
  # When we use an invalid subject.
85
89
  class BadSubject < Error; end
86
90
 
91
+ # When a subscription hits the pending messages limit.
92
+ class SlowConsumer < Error; end
93
+
87
94
  class Client
88
95
  include MonitorMixin
89
96
 
@@ -286,11 +293,17 @@ module NATS
286
293
  sid = (@ssid += 1)
287
294
  sub = @subs[sid] = Subscription.new
288
295
  end
296
+ opts[:pending_msgs_limit] ||= DEFAULT_SUB_PENDING_MSGS_LIMIT
297
+ opts[:pending_bytes_limit] ||= DEFAULT_SUB_PENDING_BYTES_LIMIT
298
+
289
299
  sub.subject = subject
290
300
  sub.callback = callback
291
301
  sub.received = 0
292
302
  sub.queue = opts[:queue] if opts[:queue]
293
303
  sub.max = opts[:max] if opts[:max]
304
+ sub.pending_msgs_limit = opts[:pending_msgs_limit]
305
+ sub.pending_bytes_limit = opts[:pending_bytes_limit]
306
+ sub.pending_queue = SizedQueue.new(sub.pending_msgs_limit)
294
307
 
295
308
  send_command("SUB #{subject} #{opts[:queue]} #{sid}#{CR_LF}")
296
309
  @flush_queue << :sub
@@ -298,6 +311,36 @@ module NATS
298
311
  # Setup server support for auto-unsubscribe when receiving enough messages
299
312
  unsubscribe(sid, opts[:max]) if opts[:max]
300
313
 
314
+ # Async subscriptions each own a single thread for the
315
+ # delivery of messages.
316
+ # FIXME: Support shared thread pool with configurable limits
317
+ # to better support case of having a lot of subscriptions.
318
+ sub.wait_for_msgs_t = Thread.new do
319
+ loop do
320
+ msg = sub.pending_queue.pop
321
+
322
+ cb = nil
323
+ sub.synchronize do
324
+ # Decrease pending size since consumed already
325
+ sub.pending_size -= msg.data.size
326
+ cb = sub.callback
327
+ end
328
+
329
+ begin
330
+ case cb.arity
331
+ when 0 then cb.call
332
+ when 1 then cb.call(msg.data)
333
+ when 2 then cb.call(msg.data, msg.reply)
334
+ else cb.call(msg.data, msg.reply, msg.subject)
335
+ end
336
+ rescue => e
337
+ synchronize do
338
+ @err_cb.call(e) if @err_cb
339
+ end
340
+ end
341
+ end
342
+ end
343
+
301
344
  sid
302
345
  end
303
346
 
@@ -369,6 +412,12 @@ module NATS
369
412
  synchronize do
370
413
  sub.max = opt_max
371
414
  @subs.delete(sid) unless (sub.max && (sub.received < sub.max))
415
+
416
+ # Stop messages delivery thread for async subscribers
417
+ if sub.wait_for_msgs_t && sub.wait_for_msgs_t.alive?
418
+ sub.wait_for_msgs_t.exit
419
+ sub.pending_queue.clear
420
+ end
372
421
  end
373
422
  end
374
423
 
@@ -446,9 +495,11 @@ module NATS
446
495
  synchronize { sub = @subs[sid] }
447
496
  return unless sub
448
497
 
449
- # Check for auto_unsubscribe
498
+ sc = nil
450
499
  sub.synchronize do
451
500
  sub.received += 1
501
+
502
+ # Check for auto_unsubscribe
452
503
  if sub.max
453
504
  case
454
505
  when sub.received > sub.max
@@ -469,20 +520,25 @@ module NATS
469
520
  future.signal
470
521
 
471
522
  return
523
+ elsif sub.callback
524
+ # Async subscribers use a sized queue for processing
525
+ # and should be able to consume messages in parallel.
526
+ if sub.pending_queue.size >= sub.pending_msgs_limit \
527
+ or sub.pending_size >= sub.pending_bytes_limit then
528
+ sc = SlowConsumer.new("nats: slow consumer, messages dropped")
529
+ else
530
+ # Only dispatch message when sure that it would not block
531
+ # the main read loop from the parser.
532
+ sub.pending_queue << Msg.new(subject, reply, data)
533
+ sub.pending_size += data.size
534
+ end
472
535
  end
473
536
  end
474
537
 
475
- # Distinguish between async subscriptions with callbacks
476
- # and request subscriptions which expect a single response.
477
- if sub.callback
478
- cb = sub.callback
479
- case cb.arity
480
- when 0 then cb.call
481
- when 1 then cb.call(data)
482
- when 2 then cb.call(data, reply)
483
- else cb.call(data, reply, subject)
484
- end
485
- end
538
+ synchronize do
539
+ @last_err = sc
540
+ @err_cb.call(sc) if @err_cb
541
+ end if sc
486
542
  end
487
543
 
488
544
  def process_info(line)
@@ -998,7 +1054,15 @@ module NATS
998
1054
  @err_cb.call(e) if @err_cb
999
1055
  end if should_flush
1000
1056
 
1001
- # TODO: Destroy any remaining subscriptions
1057
+ # Destroy any remaining subscriptions.
1058
+ @subs.each do |_, sub|
1059
+ if sub.wait_for_msgs_t && sub.wait_for_msgs_t.alive?
1060
+ sub.wait_for_msgs_t.exit
1061
+ sub.pending_queue.clear
1062
+ end
1063
+ end
1064
+ @subs.clear
1065
+
1002
1066
  if do_cbs
1003
1067
  @disconnect_cb.call(@last_err) if @disconnect_cb
1004
1068
  @close_cb.call if @close_cb
@@ -1195,7 +1259,9 @@ module NATS
1195
1259
  class Subscription
1196
1260
  include MonitorMixin
1197
1261
 
1198
- attr_accessor :subject, :queue, :future, :callback, :response, :received, :max
1262
+ attr_accessor :subject, :queue, :future, :callback, :response, :received, :max, :pending
1263
+ attr_accessor :pending_queue, :pending_size, :wait_for_msgs_t, :is_slow_consumer
1264
+ attr_accessor :pending_msgs_limit, :pending_bytes_limit
1199
1265
 
1200
1266
  def initialize
1201
1267
  super # required to initialize monitor
@@ -1206,6 +1272,15 @@ module NATS
1206
1272
  @response = nil
1207
1273
  @received = 0
1208
1274
  @max = nil
1275
+ @pending = nil
1276
+
1277
+ # State from async subscriber messages delivery
1278
+ @pending_queue = nil
1279
+ @pending_size = 0
1280
+ @pending_msgs_limit = nil
1281
+ @pending_bytes_limit = nil
1282
+ @wait_for_msgs_t = nil
1283
+ @is_slow_consumer = false
1209
1284
  end
1210
1285
  end
1211
1286
 
@@ -1,7 +1,7 @@
1
1
  module NATS
2
2
  module IO
3
3
  # NOTE: These are all announced to the server on CONNECT
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  LANG = "#{RUBY_ENGINE}2".freeze
6
6
  PROTOCOL = 1
7
7
  end
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: 0.3.0
4
+ version: 0.4.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: 2018-02-24 00:00:00.000000000 Z
11
+ date: 2018-03-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: NATS is an open-source, high-performance, lightweight cloud messaging
14
14
  system.