nats-pure 0.3.0 → 0.4.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: 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.