message_bus 3.3.7 → 4.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +1 -8
  3. data/.github/workflows/ci.yml +50 -20
  4. data/.prettierrc +1 -0
  5. data/CHANGELOG +99 -46
  6. data/README.md +31 -53
  7. data/Rakefile +22 -22
  8. data/docker-compose.yml +1 -1
  9. data/lib/message_bus/backends/base.rb +14 -0
  10. data/lib/message_bus/backends/memory.rb +13 -0
  11. data/lib/message_bus/backends/postgres.rb +55 -22
  12. data/lib/message_bus/backends/redis.rb +17 -1
  13. data/lib/message_bus/client.rb +26 -22
  14. data/lib/message_bus/distributed_cache.rb +1 -0
  15. data/lib/message_bus/rack/middleware.rb +0 -6
  16. data/lib/message_bus/rack/thin_ext.rb +1 -0
  17. data/lib/message_bus/version.rb +1 -1
  18. data/lib/message_bus.rb +53 -71
  19. data/message_bus.gemspec +4 -3
  20. data/package.json +2 -5
  21. data/spec/helpers.rb +6 -1
  22. data/spec/lib/fake_async_middleware.rb +1 -0
  23. data/spec/lib/message_bus/backend_spec.rb +20 -3
  24. data/spec/lib/message_bus/client_spec.rb +1 -0
  25. data/spec/lib/message_bus/connection_manager_spec.rb +4 -0
  26. data/spec/lib/message_bus/multi_process_spec.rb +21 -10
  27. data/spec/lib/message_bus/rack/middleware_spec.rb +2 -49
  28. data/spec/lib/message_bus/timer_thread_spec.rb +1 -5
  29. data/spec/lib/message_bus_spec.rb +12 -3
  30. data/spec/performance/backlog.rb +80 -0
  31. data/spec/performance/publish.rb +4 -4
  32. data/spec/spec_helper.rb +1 -1
  33. data/vendor/assets/javascripts/message-bus-ajax.js +38 -0
  34. data/vendor/assets/javascripts/message-bus.js +549 -0
  35. metadata +8 -31
  36. data/assets/application.jsx +0 -121
  37. data/assets/babel.min.js +0 -25
  38. data/assets/react-dom.js +0 -19851
  39. data/assets/react.js +0 -3029
  40. data/examples/diagnostics/Gemfile +0 -6
  41. data/examples/diagnostics/config.ru +0 -22
  42. data/lib/message_bus/diagnostics.rb +0 -62
  43. data/lib/message_bus/rack/diagnostics.rb +0 -120
data/lib/message_bus.rb CHANGED
@@ -7,9 +7,7 @@ require_relative "message_bus/version"
7
7
  require_relative "message_bus/message"
8
8
  require_relative "message_bus/client"
9
9
  require_relative "message_bus/connection_manager"
10
- require_relative "message_bus/diagnostics"
11
10
  require_relative "message_bus/rack/middleware"
12
- require_relative "message_bus/rack/diagnostics"
13
11
  require_relative "message_bus/timer_thread"
14
12
  require_relative "message_bus/codec/base"
15
13
  require_relative "message_bus/backends"
@@ -41,21 +39,10 @@ module MessageBus::Implementation
41
39
  def initialize
42
40
  @config = {}
43
41
  @mutex = Synchronizer.new
44
- end
45
-
46
- # @param [Boolean] val whether or not to cache static assets for the diagnostics pages
47
- # @return [void]
48
- def cache_assets=(val)
49
- configure(cache_assets: val)
50
- end
51
-
52
- # @return [Boolean] whether or not to cache static assets for the diagnostics pages
53
- def cache_assets
54
- if defined? @config[:cache_assets]
55
- @config[:cache_assets]
56
- else
57
- true
58
- end
42
+ @off = false
43
+ @destroyed = false
44
+ @timer_thread = nil
45
+ @subscriber_thread = nil
59
46
  end
60
47
 
61
48
  # @param [Logger] logger a logger object to be used by the bus
@@ -293,15 +280,20 @@ module MessageBus::Implementation
293
280
  @config[:transport_codec] ||= MessageBus::Codec::Json.new
294
281
  end
295
282
 
296
- # @param [MessageBus::Backend::Base] pub_sub a configured backend
283
+ # @param [MessageBus::Backend::Base] backend_instance A configured backend
297
284
  # @return [void]
285
+ def backend_instance=(backend_instance)
286
+ configure(backend_instance: backend_instance)
287
+ end
288
+
298
289
  def reliable_pub_sub=(pub_sub)
299
- configure(reliable_pub_sub: pub_sub)
290
+ logger.warn "MessageBus.reliable_pub_sub= is deprecated, use MessageBus.backend_instance= instead."
291
+ self.backend_instance = pub_sub
300
292
  end
301
293
 
302
294
  # @return [MessageBus::Backend::Base] the configured backend. If not
303
295
  # explicitly set, will be loaded based on the configuration provided.
304
- def reliable_pub_sub
296
+ def backend_instance
305
297
  @mutex.synchronize do
306
298
  return nil if @destroyed
307
299
 
@@ -309,7 +301,7 @@ module MessageBus::Implementation
309
301
  # passed to backend.
310
302
  logger
311
303
 
312
- @config[:reliable_pub_sub] ||= begin
304
+ @config[:backend_instance] ||= begin
313
305
  @config[:backend_options] ||= {}
314
306
  require "message_bus/backends/#{backend}"
315
307
  MessageBus::BACKENDS[backend].new @config
@@ -317,17 +309,16 @@ module MessageBus::Implementation
317
309
  end
318
310
  end
319
311
 
312
+ def reliable_pub_sub
313
+ logger.warn "MessageBus.reliable_pub_sub is deprecated, use MessageBus.backend_instance instead."
314
+ backend_instance
315
+ end
316
+
320
317
  # @return [Symbol] the name of the backend implementation configured
321
318
  def backend
322
319
  @config[:backend] || :redis
323
320
  end
324
321
 
325
- # Enables diagnostics tracking
326
- # @return [void]
327
- def enable_diagnostics
328
- MessageBus::Diagnostics.enable(self)
329
- end
330
-
331
322
  # Publishes a message to a channel
332
323
  #
333
324
  # @param [String] channel the name of the channel to which the message should be published
@@ -394,7 +385,7 @@ module MessageBus::Implementation
394
385
  end
395
386
 
396
387
  encoded_channel_name = encode_channel_name(channel, site_id)
397
- reliable_pub_sub.publish(encoded_channel_name, encoded_data, channel_opts)
388
+ backend_instance.publish(encoded_channel_name, encoded_data, channel_opts)
398
389
  end
399
390
 
400
391
  # Subscribe to messages. Each message will be delivered by yielding to the
@@ -410,9 +401,9 @@ module MessageBus::Implementation
410
401
  # @return [void]
411
402
  def blocking_subscribe(channel = nil, &blk)
412
403
  if channel
413
- reliable_pub_sub.subscribe(encode_channel_name(channel), &blk)
404
+ backend_instance.subscribe(encode_channel_name(channel), &blk)
414
405
  else
415
- reliable_pub_sub.global_subscribe(&blk)
406
+ backend_instance.global_subscribe(&blk)
416
407
  end
417
408
  end
418
409
 
@@ -491,9 +482,9 @@ module MessageBus::Implementation
491
482
  def backlog(channel = nil, last_id = nil, site_id = nil)
492
483
  old =
493
484
  if channel
494
- reliable_pub_sub.backlog(encode_channel_name(channel, site_id), last_id)
485
+ backend_instance.backlog(encode_channel_name(channel, site_id), last_id)
495
486
  else
496
- reliable_pub_sub.global_backlog(last_id)
487
+ backend_instance.global_backlog(last_id)
497
488
  end
498
489
 
499
490
  old.each do |m|
@@ -509,7 +500,19 @@ module MessageBus::Implementation
509
500
  #
510
501
  # @return [Integer] the channel-specific ID of the last message published to the given channel
511
502
  def last_id(channel, site_id = nil)
512
- reliable_pub_sub.last_id(encode_channel_name(channel, site_id))
503
+ backend_instance.last_id(encode_channel_name(channel, site_id))
504
+ end
505
+
506
+ # Get the ID of the last message published on multiple channels
507
+ #
508
+ # @param [Array<String>] channels - array of channels to fetch
509
+ # @param [String] site_id - the ID of the site by which to filter
510
+ #
511
+ # @return [Hash] the channel-specific IDs of the last message published to each requested channel
512
+ def last_ids(*channels, site_id: nil)
513
+ encoded_channel_names = channels.map { |c| encode_channel_name(c, site_id) }
514
+ ids = backend_instance.last_ids(*encoded_channel_names)
515
+ channels.zip(ids).to_h
513
516
  end
514
517
 
515
518
  # Get the last message published on a channel
@@ -532,7 +535,8 @@ module MessageBus::Implementation
532
535
  def destroy
533
536
  return if @destroyed
534
537
 
535
- reliable_pub_sub.global_unsubscribe
538
+ backend_instance.global_unsubscribe
539
+ backend_instance.destroy
536
540
 
537
541
  @mutex.synchronize do
538
542
  return if @destroyed
@@ -550,7 +554,7 @@ module MessageBus::Implementation
550
554
  # scheduled tasks.
551
555
  # @return [void]
552
556
  def after_fork
553
- reliable_pub_sub.after_fork
557
+ backend_instance.after_fork
554
558
  ensure_subscriber_thread
555
559
  # will ensure timer is running
556
560
  timer.queue {}
@@ -559,12 +563,12 @@ module MessageBus::Implementation
559
563
  # @return [Boolean] whether or not the server is actively listening for
560
564
  # publications on the bus
561
565
  def listening?
562
- @subscriber_thread && @subscriber_thread.alive?
566
+ @subscriber_thread&.alive?
563
567
  end
564
568
 
565
569
  # (see MessageBus::Backend::Base#reset!)
566
570
  def reset!
567
- reliable_pub_sub.reset! if reliable_pub_sub
571
+ backend_instance.reset! if backend_instance
568
572
  end
569
573
 
570
574
  # @return [MessageBus::TimerThread] the timer thread used for triggering
@@ -692,12 +696,6 @@ module MessageBus::Implementation
692
696
  @subscriptions[site_id][channel] << blk
693
697
  ensure_subscriber_thread
694
698
 
695
- attempts = 100
696
- while attempts > 0 && !reliable_pub_sub.subscribed
697
- sleep 0.001
698
- attempts -= 1
699
- end
700
-
701
699
  raise MessageBus::BusDestroyed if @destroyed
702
700
 
703
701
  blk
@@ -715,10 +713,17 @@ module MessageBus::Implementation
715
713
 
716
714
  def ensure_subscriber_thread
717
715
  @mutex.synchronize do
718
- return if (@subscriber_thread && @subscriber_thread.alive?) || @destroyed
716
+ return if @destroyed
717
+ next if @subscriber_thread&.alive?
719
718
 
720
719
  @subscriber_thread = new_subscriber_thread
721
720
  end
721
+
722
+ attempts = 100
723
+ while attempts > 0 && !backend_instance.subscribed
724
+ sleep 0.001
725
+ attempts -= 1
726
+ end
722
727
  end
723
728
 
724
729
  MIN_KEEPALIVE = 20
@@ -739,34 +744,11 @@ module MessageBus::Implementation
739
744
  if !@destroyed && thread.alive? && keepalive_interval > MIN_KEEPALIVE
740
745
 
741
746
  publish("/__mb_keepalive__/", Process.pid, user_ids: [-1])
742
- # going for x3 keepalives missed for a restart, need to ensure this only very rarely happens
743
- # note: after_fork will sort out a bad @last_message date, but thread will be dead anyway
744
747
  if (Time.now - (@last_message || Time.now)) > keepalive_interval * 3
745
- logger.warn "Global messages on #{Process.pid} timed out, restarting process"
746
- # No other clean way to remove this thread, its listening on a socket
747
- # no data is arriving
748
- #
749
- # In production we see this kind of situation ... sometimes ... when there is
750
- # a VRRP failover, or weird networking condition
751
- pid = Process.pid
752
-
753
- # do the best we can to terminate self cleanly
754
- fork do
755
- Process.kill('TERM', pid)
756
- sleep 10
757
- begin
758
- Process.kill('KILL', pid)
759
- rescue Errno::ESRCH
760
- logger.warn "#{Process.pid} successfully terminated by `TERM` signal."
761
- end
762
- end
763
-
764
- sleep 10
765
- Process.kill('KILL', pid)
766
-
767
- else
768
- timer.queue(keepalive_interval, &blk) if keepalive_interval > MIN_KEEPALIVE
748
+ logger.warn "Global messages on #{Process.pid} timed out, message bus is no longer functioning correctly"
769
749
  end
750
+
751
+ timer.queue(keepalive_interval, &blk) if keepalive_interval > MIN_KEEPALIVE
770
752
  end
771
753
  end
772
754
 
@@ -778,7 +760,7 @@ module MessageBus::Implementation
778
760
  def global_subscribe_thread
779
761
  # pretend we just got a message
780
762
  @last_message = Time.now
781
- reliable_pub_sub.global_subscribe do |msg|
763
+ backend_instance.global_subscribe do |msg|
782
764
  begin
783
765
  @last_message = Time.now
784
766
  decode_message!(msg)
data/message_bus.gemspec CHANGED
@@ -9,9 +9,8 @@ Gem::Specification.new do |gem|
9
9
  gem.summary = %q{}
10
10
  gem.homepage = "https://github.com/discourse/message_bus"
11
11
  gem.license = "MIT"
12
- gem.files = `git ls-files`.split($\)
13
- gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
14
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
12
+ gem.files = `git ls-files`.split($\) +
13
+ ["vendor/assets/javascripts/message-bus.js", "vendor/assets/javascripts/message-bus-ajax.js"]
15
14
  gem.name = "message_bus"
16
15
  gem.require_paths = ["lib"]
17
16
  gem.version = MessageBus::VERSION
@@ -19,9 +18,11 @@ Gem::Specification.new do |gem|
19
18
 
20
19
  gem.add_runtime_dependency 'rack', '>= 1.1.3'
21
20
 
21
+ # Optional runtime dependencies
22
22
  gem.add_development_dependency 'redis'
23
23
  gem.add_development_dependency 'pg'
24
24
  gem.add_development_dependency 'concurrent-ruby' # for distributed-cache
25
+
25
26
  gem.add_development_dependency 'minitest'
26
27
  gem.add_development_dependency 'minitest-hooks'
27
28
  gem.add_development_dependency 'minitest-global_expectations'
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "message-bus-client",
3
- "version": "3.3.5",
3
+ "version": "0.0.0-version-placeholder",
4
4
  "description": "A message bus client in Javascript",
5
5
  "main": "assets/message-bus.js",
6
6
  "keywords": [
@@ -12,10 +12,7 @@
12
12
  ],
13
13
  "jsnext:main": "assets/message-bus.js",
14
14
  "module": "assets/message-bus.js",
15
- "repository": {
16
- "type": "git",
17
- "url": "git+https://github.com/discourse/message_bus.git"
18
- },
15
+ "repository": "https://github.com/discourse/message_bus",
19
16
  "author": "Sam Saffron, Robin Ward",
20
17
  "license": "MIT",
21
18
  "bugs": {
data/spec/helpers.rb CHANGED
@@ -25,7 +25,12 @@ def test_config_for_backend(backend)
25
25
  when :redis
26
26
  config[:url] = ENV['REDISURL']
27
27
  when :postgres
28
- config[:backend_options] = { host: ENV['PGHOST'], user: ENV['PGUSER'] || ENV['USER'], password: ENV['PGPASSWORD'], dbname: ENV['PGDATABASE'] || 'message_bus_test' }
28
+ config[:backend_options] = {
29
+ host: ENV['PGHOST'],
30
+ user: ENV['PGUSER'] || ENV['USER'],
31
+ password: ENV['PGPASSWORD'],
32
+ dbname: ENV['PGDATABASE'] || 'message_bus_test'
33
+ }
29
34
  end
30
35
  config
31
36
  end
@@ -7,6 +7,7 @@ class FakeAsyncMiddleware
7
7
  @simulate_thin_async = false
8
8
  @simulate_hijack = false
9
9
  @in_async = false
10
+ @allow_chunked = false
10
11
  end
11
12
 
12
13
  def app
@@ -3,13 +3,14 @@
3
3
  require_relative '../../spec_helper'
4
4
  require 'message_bus'
5
5
 
6
- describe PUB_SUB_CLASS do
6
+ describe BACKEND_CLASS do
7
7
  before do
8
- @bus = PUB_SUB_CLASS.new(test_config_for_backend(CURRENT_BACKEND))
8
+ @bus = BACKEND_CLASS.new(test_config_for_backend(CURRENT_BACKEND))
9
9
  end
10
10
 
11
11
  after do
12
12
  @bus.reset!
13
+ @bus.destroy
13
14
  end
14
15
 
15
16
  describe "API parity" do
@@ -29,7 +30,7 @@ describe PUB_SUB_CLASS do
29
30
  end
30
31
 
31
32
  it "should initialize with max_backlog_size" do
32
- PUB_SUB_CLASS.new({}, 2000).max_backlog_size.must_equal 2000
33
+ BACKEND_CLASS.new({}, 2000).max_backlog_size.must_equal 2000
33
34
  end
34
35
 
35
36
  it "should truncate channels correctly" do
@@ -90,6 +91,22 @@ describe PUB_SUB_CLASS do
90
91
  @bus.last_id("/foo").must_equal 1
91
92
  end
92
93
 
94
+ it "should allow us to get multiple last_ids" do
95
+ @bus.last_ids("/foo", "/bar", "/foobar").must_equal [0, 0, 0]
96
+
97
+ @bus.publish("/foo", "one")
98
+ @bus.publish("/foo", "two")
99
+ @bus.publish("/foobar", "three")
100
+
101
+ @bus.last_ids("/foo", "/bar", "/foobar").must_equal(
102
+ [
103
+ @bus.last_id("/foo"),
104
+ @bus.last_id("/bar"),
105
+ @bus.last_id("/foobar")
106
+ ]
107
+ )
108
+ end
109
+
93
110
  it "can set backlog age" do
94
111
  @bus.max_backlog_age = 1
95
112
 
@@ -16,6 +16,7 @@ describe MessageBus::Client do
16
16
  end
17
17
 
18
18
  after do
19
+ @client.close
19
20
  @bus.reset!
20
21
  @bus.destroy
21
22
  end
@@ -6,6 +6,10 @@ require 'message_bus'
6
6
  class FakeAsync
7
7
  attr_accessor :cleanup_timer
8
8
 
9
+ def initialize
10
+ @sent = nil
11
+ end
12
+
9
13
  def <<(val)
10
14
  sleep 0.01 # simulate IO
11
15
  @sent ||= +""
@@ -3,7 +3,7 @@
3
3
  require_relative '../../spec_helper'
4
4
  require 'message_bus'
5
5
 
6
- describe PUB_SUB_CLASS do
6
+ describe BACKEND_CLASS do
7
7
  def self.error!
8
8
  @error = true
9
9
  end
@@ -13,18 +13,20 @@ describe PUB_SUB_CLASS do
13
13
  end
14
14
 
15
15
  def new_bus
16
- PUB_SUB_CLASS.new(test_config_for_backend(CURRENT_BACKEND).merge(db: 10))
16
+ BACKEND_CLASS.new(test_config_for_backend(CURRENT_BACKEND).merge(db: 10))
17
17
  end
18
18
 
19
19
  def work_it
20
20
  bus = new_bus
21
- $stdout.reopen("/dev/null", "w")
22
- $stderr.reopen("/dev/null", "w")
23
- # subscribe blocks, so we need a new bus to transmit
24
- new_bus.subscribe("/echo", 0) do |msg|
25
- bus.publish("/response", "#{msg.data}-#{Process.pid.to_s}")
21
+ bus.subscribe("/echo", 0) do |msg|
22
+ if msg.data == "done"
23
+ bus.global_unsubscribe
24
+ else
25
+ bus.publish("/response", "#{msg.data}-#{Process.pid.to_s}")
26
+ end
26
27
  end
27
28
  ensure
29
+ bus.destroy
28
30
  exit!(0)
29
31
  end
30
32
 
@@ -44,12 +46,14 @@ describe PUB_SUB_CLASS do
44
46
  test_never :memory
45
47
  skip("previous error") if self.class.error?
46
48
  GC.start
47
- new_bus.reset!
49
+ bus = new_bus
50
+ bus.reset!
51
+
48
52
  begin
49
53
  pids = (1..10).map { spawn_child }
50
54
  expected_responses = pids.map { |x| (0...10).map { |i| "0#{i}-#{x}" } }.flatten
51
55
  unexpected_responses = []
52
- bus = new_bus
56
+
53
57
  t = Thread.new do
54
58
  bus.subscribe("/response", 0) do |msg|
55
59
  if expected_responses.include?(msg.data)
@@ -59,10 +63,14 @@ describe PUB_SUB_CLASS do
59
63
  end
60
64
  end
61
65
  end
66
+
62
67
  10.times { |i| bus.publish("/echo", "0#{i}") }
63
- wait_for 4000 do
68
+
69
+ wait_for(2000) do
64
70
  expected_responses.empty?
65
71
  end
72
+
73
+ bus.publish("/echo", "done")
66
74
  bus.global_unsubscribe
67
75
  t.join
68
76
 
@@ -81,7 +89,10 @@ describe PUB_SUB_CLASS do
81
89
  Process.wait(pid)
82
90
  end
83
91
  end
92
+
84
93
  bus.global_unsubscribe
94
+ bus.reset!
95
+ bus.destroy
85
96
  end
86
97
  end
87
98
  end
@@ -83,7 +83,7 @@ describe MessageBus::Rack::Middleware do
83
83
  { "FOO" => "BAR" }
84
84
  end
85
85
 
86
- Thread.new do
86
+ t = Thread.new do
87
87
  wait_for(2000) { middleware.in_async? }
88
88
  bus.publish "/foo", "םוֹלשָׁ"
89
89
  end
@@ -96,6 +96,7 @@ describe MessageBus::Rack::Middleware do
96
96
  parsed[0]["data"].must_equal "םוֹלשָׁ"
97
97
 
98
98
  last_response.headers["FOO"].must_equal "BAR"
99
+ t.join
99
100
  end
100
101
 
101
102
  it "should timeout within its alloted slot" do
@@ -141,54 +142,6 @@ describe MessageBus::Rack::Middleware do
141
142
  end
142
143
  end
143
144
 
144
- describe "diagnostics" do
145
- it "should return a 403 if an unauthorized user attempts to get at the _diagnostics path" do
146
- get "/message-bus/_diagnostics"
147
- last_response.status.must_equal 403
148
- end
149
-
150
- it "should get a 200 with html for an authorized user" do
151
- def @bus.is_admin_lookup
152
- proc { |_| true }
153
- end
154
-
155
- get "/message-bus/_diagnostics"
156
- last_response.status.must_equal 200
157
- end
158
-
159
- describe "with an altered base_route" do
160
- let(:base_route) { "/base/route/" }
161
-
162
- it "should get a 200 with html for an authorized user" do
163
- def @bus.is_admin_lookup
164
- proc { |_| true }
165
- end
166
-
167
- get "/base/route/message-bus/_diagnostics"
168
- last_response.status.must_equal 200
169
- end
170
- end
171
-
172
- it "should get the script it asks for" do
173
- def @bus.is_admin_lookup
174
- proc { |_| true }
175
- end
176
-
177
- get "/message-bus/_diagnostics/assets/message-bus.js"
178
- last_response.status.must_equal 200
179
- last_response.content_type.must_equal "application/javascript;charset=UTF-8"
180
- end
181
-
182
- it "should return 404 for invalid assets path" do
183
- def @bus.is_admin_lookup
184
- proc { |_| true }
185
- end
186
-
187
- get "/message-bus/_diagnostics/assets/../Gemfile"
188
- last_response.status.must_equal 404
189
- end
190
- end
191
-
192
145
  describe "polling" do
193
146
  before do
194
147
  @bus.long_polling_enabled = false
@@ -52,10 +52,6 @@ describe MessageBus::TimerThread do
52
52
  it "should call the error callback if something goes wrong" do
53
53
  error = nil
54
54
 
55
- @timer.queue do
56
- boom
57
- end
58
-
59
55
  @timer.on_error do |e|
60
56
  error = e
61
57
  end
@@ -64,7 +60,7 @@ describe MessageBus::TimerThread do
64
60
  boom
65
61
  end
66
62
 
67
- wait_for(10) do
63
+ wait_for(100) do
68
64
  error
69
65
  end
70
66
 
@@ -23,7 +23,8 @@ describe MessageBus do
23
23
  @bus.off?.must_equal true
24
24
  end
25
25
 
26
- it "can call destroy twice" do
26
+ it "can call destroy multiple times" do
27
+ @bus.destroy
27
28
  @bus.destroy
28
29
  @bus.destroy
29
30
  end
@@ -36,6 +37,14 @@ describe MessageBus do
36
37
  @bus.after_fork
37
38
  end
38
39
 
40
+ it "destroying immediately after `after_fork` does not lock" do
41
+ 10.times do
42
+ @bus.on
43
+ @bus.after_fork
44
+ @bus.destroy
45
+ end
46
+ end
47
+
39
48
  describe "#base_route=" do
40
49
  it "adds leading and trailing slashes" do
41
50
  @bus.base_route = "my/base/route"
@@ -106,7 +115,7 @@ describe MessageBus do
106
115
  @bus.publish("/chuck", norris: true)
107
116
  @bus.publish("/chuck", norris: true)
108
117
 
109
- @bus.reliable_pub_sub.reset!
118
+ @bus.backend_instance.reset!
110
119
 
111
120
  @bus.publish("/chuck", yeager: true)
112
121
 
@@ -124,7 +133,7 @@ describe MessageBus do
124
133
  @bus.publish("/chuck", norris: true)
125
134
  @bus.publish("/chuck", norris: true)
126
135
 
127
- @bus.reliable_pub_sub.expire_all_backlogs!
136
+ @bus.backend_instance.expire_all_backlogs!
128
137
 
129
138
  @bus.publish("/chuck", yeager: true)
130
139
 
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', '..', 'lib')
4
+ require 'logger'
5
+ require 'benchmark'
6
+ require 'message_bus'
7
+
8
+ require_relative "../helpers"
9
+
10
+ backends = ENV['MESSAGE_BUS_BACKENDS'].split(",").map(&:to_sym)
11
+ channel = "/foo"
12
+ iterations = 10_000
13
+ results = []
14
+
15
+ puts "Running backlog benchmark with #{iterations} iterations on backends: #{backends.inspect}"
16
+
17
+ run_benchmark = lambda do |bm, backend|
18
+ bus = MessageBus::Instance.new
19
+ bus.configure(test_config_for_backend(backend))
20
+
21
+ bus.backend_instance.max_backlog_size = 100
22
+ bus.backend_instance.max_global_backlog_size = 1000
23
+
24
+ channel_names = 10.times.map { |i| "channel#{i}" }
25
+
26
+ 100.times do |i|
27
+ channel_names.each do |ch|
28
+ bus.publish(ch, { message_number_is: i })
29
+ end
30
+ end
31
+
32
+ last_ids = channel_names.map { |ch| [ch, bus.last_id(ch)] }.to_h
33
+
34
+ 1000.times do
35
+ # Warmup
36
+ client = MessageBus::Client.new(message_bus: bus)
37
+ channel_names.each { |ch| client.subscribe(ch, -1) }
38
+ client.backlog
39
+ end
40
+
41
+ bm.report("#{backend} - #backlog with no backlogs requested") do
42
+ iterations.times do
43
+ client = MessageBus::Client.new(message_bus: bus)
44
+ channel_names.each { |ch| client.subscribe(ch, -1) }
45
+ client.backlog
46
+ end
47
+ end
48
+
49
+ (0..5).each do |ch_i|
50
+ channels_with_messages = (ch_i) * 2
51
+
52
+ bm.report("#{backend} - #backlog when #{channels_with_messages}/10 channels have new messages") do
53
+ iterations.times do
54
+ client = MessageBus::Client.new(message_bus: bus)
55
+ channel_names.each_with_index do |ch, i|
56
+ client.subscribe(ch, last_ids[ch] + ((i < channels_with_messages) ? -1 : 0))
57
+ end
58
+ result = client.backlog
59
+ if result.length != channels_with_messages
60
+ raise "Result has #{result.length} messages. Expected #{channels_with_messages}"
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ bus.reset!
67
+ bus.destroy
68
+ end
69
+
70
+ puts
71
+
72
+ Benchmark.benchmark(" duration\n", 60, "%10.2rs\n", "") do |bm|
73
+ backends.each do |backend|
74
+ run_benchmark.call(bm, backend)
75
+ end
76
+ end
77
+ puts
78
+ results.each do |result|
79
+ puts result
80
+ end