message_bus 3.3.4 → 3.3.8

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +21 -0
  3. data/.github/workflows/ci.yml +99 -0
  4. data/.gitignore +2 -0
  5. data/.prettierrc +1 -0
  6. data/.rubocop.yml +3 -1
  7. data/CHANGELOG +30 -8
  8. data/DEV.md +7 -0
  9. data/Gemfile +0 -25
  10. data/LICENSE +1 -1
  11. data/README.md +34 -15
  12. data/Rakefile +13 -8
  13. data/assets/message-bus-ajax.js +4 -10
  14. data/assets/message-bus.js +69 -76
  15. data/bench/codecs/all_codecs.rb +39 -0
  16. data/bench/codecs/marshal.rb +11 -0
  17. data/bench/codecs/packed_string.rb +67 -0
  18. data/bench/codecs/string_hack.rb +47 -0
  19. data/bench/codecs_large_user_list.rb +29 -0
  20. data/bench/codecs_standard_message.rb +29 -0
  21. data/examples/bench/bench.lua +2 -2
  22. data/lib/message_bus/backends/base.rb +8 -5
  23. data/lib/message_bus/backends/memory.rb +6 -2
  24. data/lib/message_bus/backends/postgres.rb +27 -18
  25. data/lib/message_bus/backends/redis.rb +9 -6
  26. data/lib/message_bus/client.rb +6 -7
  27. data/lib/message_bus/codec/base.rb +18 -0
  28. data/lib/message_bus/codec/json.rb +15 -0
  29. data/lib/message_bus/codec/oj.rb +21 -0
  30. data/lib/message_bus/connection_manager.rb +1 -1
  31. data/lib/message_bus/distributed_cache.rb +3 -1
  32. data/lib/message_bus/http_client.rb +2 -2
  33. data/lib/message_bus/rack/diagnostics.rb +30 -8
  34. data/lib/message_bus/rack/middleware.rb +22 -16
  35. data/lib/message_bus/rack/thin_ext.rb +2 -1
  36. data/lib/message_bus/version.rb +1 -1
  37. data/lib/message_bus.rb +42 -22
  38. data/message_bus.gemspec +21 -3
  39. data/package-lock.json +3744 -0
  40. data/package.json +15 -8
  41. data/spec/assets/SpecHelper.js +6 -5
  42. data/spec/assets/message-bus.spec.js +9 -6
  43. data/spec/helpers.rb +23 -7
  44. data/spec/integration/http_client_spec.rb +1 -1
  45. data/spec/lib/fake_async_middleware.rb +1 -0
  46. data/spec/lib/message_bus/backend_spec.rb +13 -44
  47. data/spec/lib/message_bus/client_spec.rb +7 -6
  48. data/spec/lib/message_bus/connection_manager_spec.rb +4 -0
  49. data/spec/lib/message_bus/distributed_cache_spec.rb +5 -7
  50. data/spec/lib/message_bus/multi_process_spec.rb +19 -9
  51. data/spec/lib/message_bus/rack/middleware_spec.rb +18 -6
  52. data/spec/lib/message_bus_spec.rb +13 -8
  53. data/spec/spec_helper.rb +8 -9
  54. data/spec/support/jasmine-browser.json +16 -0
  55. data/vendor/assets/javascripts/message-bus-ajax.js +4 -10
  56. data/vendor/assets/javascripts/message-bus.js +69 -76
  57. metadata +231 -11
  58. data/.travis.yml +0 -17
  59. data/lib/message_bus/em_ext.rb +0 -6
  60. data/spec/assets/support/jasmine.yml +0 -126
  61. data/spec/assets/support/jasmine_helper.rb +0 -11
@@ -1,9 +1,9 @@
1
- /*jshint bitwise: false*/
1
+ /*global define, jQuery*/
2
2
 
3
3
  (function (root, factory) {
4
- if (typeof define === 'function' && define.amd) {
4
+ if (typeof define === "function" && define.amd) {
5
5
  // AMD. Register as an anonymous module.
6
- define([], function (b) {
6
+ define([], function () {
7
7
  // Also create a global in case some scripts
8
8
  // that are loaded still are looking for
9
9
  // a global even when an AMD loader is in use.
@@ -13,12 +13,12 @@
13
13
  // Browser globals
14
14
  root.MessageBus = factory();
15
15
  }
16
- }(typeof self !== 'undefined' ? self : this, function () {
16
+ })(typeof self !== "undefined" ? self : this, function () {
17
17
  "use strict";
18
18
 
19
19
  // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
20
- var uniqueId = function() {
21
- return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function(c) {
20
+ var uniqueId = function () {
21
+ return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
22
22
  var r = (Math.random() * 16) | 0;
23
23
  var v = c === "x" ? r : (r & 0x3) | 0x8;
24
24
  return v.toString(16);
@@ -31,8 +31,6 @@
31
31
  var started = false;
32
32
  var clientId = uniqueId();
33
33
  var callbacks = [];
34
- var queue = [];
35
- var interval = null;
36
34
  var failCount = 0;
37
35
  var baseUrl = "/";
38
36
  var paused = false;
@@ -44,7 +42,7 @@
44
42
  var totalAjaxCalls = 0;
45
43
  var lastAjax;
46
44
 
47
- var isHidden = (function() {
45
+ var isHidden = (function () {
48
46
  var prefixes = ["", "webkit", "ms", "moz"];
49
47
  var hiddenProperty;
50
48
  for (var i = 0; i < prefixes.length; i++) {
@@ -55,7 +53,7 @@
55
53
  }
56
54
  }
57
55
 
58
- return function() {
56
+ return function () {
59
57
  if (hiddenProperty !== undefined) {
60
58
  return document[hiddenProperty];
61
59
  } else {
@@ -64,7 +62,7 @@
64
62
  };
65
63
  })();
66
64
 
67
- var hasLocalStorage = (function() {
65
+ var hasLocalStorage = (function () {
68
66
  try {
69
67
  localStorage.setItem("mbTestLocalStorage", Date.now());
70
68
  localStorage.removeItem("mbTestLocalStorage");
@@ -74,13 +72,13 @@
74
72
  }
75
73
  })();
76
74
 
77
- var updateLastAjax = function() {
75
+ var updateLastAjax = function () {
78
76
  if (hasLocalStorage) {
79
77
  localStorage.setItem("__mbLastAjax", Date.now());
80
78
  }
81
79
  };
82
80
 
83
- var hiddenTabShouldWait = function() {
81
+ var hiddenTabShouldWait = function () {
84
82
  if (hasLocalStorage && isHidden()) {
85
83
  var lastAjaxCall = parseInt(localStorage.getItem("__mbLastAjax"), 10);
86
84
  var deltaAjax = Date.now() - lastAjaxCall;
@@ -91,20 +89,21 @@
91
89
  };
92
90
 
93
91
  var hasonprogress = new XMLHttpRequest().onprogress === null;
94
- var allowChunked = function() {
92
+ var allowChunked = function () {
95
93
  return me.enableChunkedEncoding && hasonprogress;
96
94
  };
97
95
 
98
- var shouldLongPoll = function() {
96
+ var shouldLongPoll = function () {
99
97
  return (
100
98
  me.alwaysLongPoll ||
101
99
  (me.shouldLongPollCallback ? me.shouldLongPollCallback() : !isHidden())
102
100
  );
103
101
  };
104
102
 
105
- var processMessages = function(messages) {
106
- var gotData = false;
107
- if ((!messages) || (messages.length === 0)) { return false; }
103
+ var processMessages = function (messages) {
104
+ if (!messages || messages.length === 0) {
105
+ return false;
106
+ }
108
107
 
109
108
  for (var i = 0; i < messages.length; i++) {
110
109
  var message = messages[i];
@@ -136,7 +135,7 @@
136
135
  return true;
137
136
  };
138
137
 
139
- var reqSuccess = function(messages) {
138
+ var reqSuccess = function (messages) {
140
139
  failCount = 0;
141
140
  if (paused) {
142
141
  if (messages) {
@@ -150,7 +149,7 @@
150
149
  return false;
151
150
  };
152
151
 
153
- var longPoller = function(poll, data) {
152
+ var longPoller = function (poll, data) {
154
153
  if (ajaxInProgress) {
155
154
  // never allow concurrent ajax reqs
156
155
  return;
@@ -183,7 +182,7 @@
183
182
 
184
183
  var dataType = chunked ? "text" : "json";
185
184
 
186
- var handle_progress = function(payload, position) {
185
+ var handle_progress = function (payload, position) {
187
186
  var separator = "\r\n|\r\n";
188
187
  var endChunk = payload.indexOf(separator, position);
189
188
 
@@ -206,31 +205,13 @@
206
205
  return handle_progress(payload, endChunk + separator.length);
207
206
  };
208
207
 
209
- var disableChunked = function() {
208
+ var disableChunked = function () {
210
209
  if (me.longPoll) {
211
210
  me.longPoll.abort();
212
211
  chunkedBackoff = 30;
213
212
  }
214
213
  };
215
214
 
216
- var setOnProgressListener = function(xhr) {
217
- var position = 0;
218
- // if it takes longer than 3000 ms to get first chunk, we have some proxy
219
- // this is messing with us, so just backoff from using chunked for now
220
- var chunkedTimeout = setTimeout(disableChunked, 3000);
221
- xhr.onprogress = function() {
222
- clearTimeout(chunkedTimeout);
223
- if (
224
- xhr.getResponseHeader("Content-Type") ===
225
- "application/json; charset=utf-8"
226
- ) {
227
- // not chunked we are sending json back
228
- chunked = false;
229
- return;
230
- }
231
- position = handle_progress(xhr.responseText, position);
232
- };
233
- };
234
215
  if (!me.ajax) {
235
216
  throw new Error("Either jQuery or the ajax adapter must be loaded");
236
217
  }
@@ -246,19 +227,18 @@
246
227
  "/poll" +
247
228
  (!longPoll ? "?dlp=t" : ""),
248
229
  data: data,
249
- cache: false,
250
230
  async: true,
251
231
  dataType: dataType,
252
232
  type: "POST",
253
233
  headers: headers,
254
234
  messageBus: {
255
235
  chunked: chunked,
256
- onProgressListener: function(xhr) {
236
+ onProgressListener: function (xhr) {
257
237
  var position = 0;
258
238
  // if it takes longer than 3000 ms to get first chunk, we have some proxy
259
239
  // this is messing with us, so just backoff from using chunked for now
260
240
  var chunkedTimeout = setTimeout(disableChunked, 3000);
261
- return (xhr.onprogress = function() {
241
+ return (xhr.onprogress = function () {
262
242
  clearTimeout(chunkedTimeout);
263
243
  if (
264
244
  xhr.getResponseHeader("Content-Type") ===
@@ -269,9 +249,9 @@
269
249
  position = handle_progress(xhr.responseText, position);
270
250
  }
271
251
  });
272
- }
252
+ },
273
253
  },
274
- xhr: function() {
254
+ xhr: function () {
275
255
  var xhr = jQuery.ajaxSettings.xhr();
276
256
  if (!chunked) {
277
257
  return xhr;
@@ -279,7 +259,7 @@
279
259
  this.messageBus.onProgressListener(xhr);
280
260
  return xhr;
281
261
  },
282
- success: function(messages) {
262
+ success: function (messages) {
283
263
  if (!chunked) {
284
264
  // we may have requested text so jQuery will not parse
285
265
  if (typeof messages === "string") {
@@ -288,7 +268,7 @@
288
268
  gotData = reqSuccess(messages);
289
269
  }
290
270
  },
291
- error: function(xhr, textStatus, err) {
271
+ error: function (xhr, textStatus) {
292
272
  if (xhr.status === 429) {
293
273
  var tryAfter =
294
274
  parseInt(
@@ -307,7 +287,7 @@
307
287
  totalAjaxFailures += 1;
308
288
  }
309
289
  },
310
- complete: function() {
290
+ complete: function () {
311
291
  ajaxInProgress = false;
312
292
 
313
293
  var interval;
@@ -345,14 +325,14 @@
345
325
  }
346
326
 
347
327
  if (started) {
348
- pollTimeout = setTimeout(function() {
328
+ pollTimeout = setTimeout(function () {
349
329
  pollTimeout = null;
350
330
  poll();
351
331
  }, interval);
352
332
  }
353
333
 
354
334
  me.longPoll = null;
355
- }
335
+ },
356
336
  });
357
337
 
358
338
  return req;
@@ -374,7 +354,7 @@
374
354
  baseUrl: baseUrl,
375
355
  headers: {},
376
356
  ajax: typeof jQuery !== "undefined" && jQuery.ajax,
377
- diagnostics: function() {
357
+ diagnostics: function () {
378
358
  console.log("Stopped: " + stopped + " Started: " + started);
379
359
  console.log("Current callbacks");
380
360
  console.log(callbacks);
@@ -391,42 +371,50 @@
391
371
  );
392
372
  },
393
373
 
394
- pause: function() {
374
+ pause: function () {
395
375
  paused = true;
396
376
  },
397
377
 
398
- resume: function() {
378
+ resume: function () {
399
379
  paused = false;
400
380
  processMessages(later);
401
381
  later = [];
402
382
  },
403
383
 
404
- stop: function() {
384
+ stop: function () {
405
385
  stopped = true;
406
386
  started = false;
407
387
  if (delayPollTimeout) {
408
388
  clearTimeout(delayPollTimeout);
409
389
  delayPollTimeout = null;
410
390
  }
391
+ if (pollTimeout) {
392
+ clearTimeout(pollTimeout);
393
+ pollTimeout = null;
394
+ }
411
395
  if (me.longPoll) {
412
396
  me.longPoll.abort();
413
397
  }
398
+ if (me.onVisibilityChange) {
399
+ document.removeEventListener("visibilitychange", me.onVisibilityChange);
400
+ me.onVisibilityChange = null;
401
+ }
414
402
  },
415
403
 
416
404
  // Start polling
417
- start: function() {
405
+ start: function () {
418
406
  if (started) return;
419
407
  started = true;
420
408
  stopped = false;
421
409
 
422
- var poll = function() {
410
+ var poll = function () {
423
411
  if (stopped) {
424
412
  return;
425
413
  }
426
414
 
427
415
  if (callbacks.length === 0 || hiddenTabShouldWait()) {
428
416
  if (!delayPollTimeout) {
429
- delayPollTimeout = setTimeout(function() {
417
+ delayPollTimeout = setTimeout(function () {
430
418
  delayPollTimeout = null;
431
419
  poll();
432
420
  }, parseInt(500 + Math.random() * 500));
@@ -448,25 +436,28 @@
448
436
 
449
437
  // monitor visibility, issue a new long poll when the page shows
450
438
  if (document.addEventListener && "hidden" in document) {
451
- me.visibilityEvent = document.addEventListener(
452
- "visibilitychange",
453
- function() {
454
- if (!document.hidden && !me.longPoll && pollTimeout) {
455
- clearTimeout(pollTimeout);
456
- clearTimeout(delayPollTimeout);
457
-
458
- delayPollTimeout = null;
459
- pollTimeout = null;
460
- poll();
461
- }
439
+ me.onVisibilityChange = function () {
440
+ if (
441
+ !document.hidden &&
442
+ !me.longPoll &&
443
+ (pollTimeout || delayPollTimeout)
444
+ ) {
445
+ clearTimeout(pollTimeout);
446
+ clearTimeout(delayPollTimeout);
447
+
448
+ delayPollTimeout = null;
449
+ pollTimeout = null;
450
+ poll();
462
451
  }
463
- );
452
+ };
453
+
454
+ document.addEventListener("visibilitychange", me.onVisibilityChange);
464
455
  }
465
456
 
466
457
  poll();
467
458
  },
468
459
 
469
- status: function() {
460
+ status: function () {
470
461
  if (paused) {
471
462
  return "paused";
472
463
  } else if (started) {
@@ -485,7 +476,7 @@
485
476
  // -2 will recieve last message + all new messages
486
477
  // -3 will recieve last 2 messages + all new messages
487
478
  // if undefined will default to -1
488
- subscribe: function(channel, func, lastId) {
479
+ subscribe: function (channel, func, lastId) {
489
480
  if (!started && !stopped) {
490
481
  me.start();
491
482
  }
@@ -493,7 +484,9 @@
493
484
  if (lastId === null || typeof lastId === "undefined") {
494
485
  lastId = -1;
495
486
  } else if (typeof lastId !== "number") {
496
- throw "lastId has type " + typeof lastId + " but a number was expected.";
487
+ throw (
488
+ "lastId has type " + typeof lastId + " but a number was expected."
489
+ );
497
490
  }
498
491
 
499
492
  if (typeof channel !== "string") {
@@ -503,7 +496,7 @@
503
496
  callbacks.push({
504
497
  channel: channel,
505
498
  func: func,
506
- last_id: lastId
499
+ last_id: lastId,
507
500
  });
508
501
  if (me.longPoll) {
509
502
  me.longPoll.abort();
@@ -513,7 +506,7 @@
513
506
  },
514
507
 
515
508
  // Unsubscribe from a channel
516
- unsubscribe: function(channel, func) {
509
+ unsubscribe: function (channel, func) {
517
510
  // TODO allow for globbing in the middle of a channel name
518
511
  // like /something/*/something
519
512
  // at the moment we only support globbing /something/*
@@ -550,7 +543,7 @@
550
543
  }
551
544
 
552
545
  return removed;
553
- }
546
+ },
554
547
  };
555
548
  return me;
556
- }));
549
+ });
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './packed_string'
4
+ require_relative './string_hack'
5
+ require_relative './marshal'
6
+
7
+ def all_codecs
8
+ {
9
+ json: MessageBus::Codec::Json.new,
10
+ oj: MessageBus::Codec::Oj.new,
11
+ marshal: MarshalCodec.new,
12
+ packed_string_4_bytes: PackedString.new("V"),
13
+ packed_string_8_bytes: PackedString.new("Q"),
14
+ string_hack: StringHack.new
15
+ }
16
+ end
17
+
18
+ def bench_decode(hash, user_needle)
19
+ encoded_data = all_codecs.map do |name, codec|
20
+ [
21
+ name, codec, codec.encode(hash.dup)
22
+ ]
23
+ end
24
+
25
+ Benchmark.ips do |x|
26
+
27
+ encoded_data.each do |name, codec, encoded|
28
+ x.report(name) do |n|
29
+ while n > 0
30
+ decoded = codec.decode(encoded)
31
+ decoded["user_ids"].include?(user_needle)
32
+ n -= 1
33
+ end
34
+ end
35
+ end
36
+
37
+ x.compare!
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MarshalCodec
4
+ def encode(hash)
5
+ ::Marshal.dump(hash)
6
+ end
7
+
8
+ def decode(payload)
9
+ ::Marshal.load(payload) # rubocop:disable Security/MarshalLoad
10
+ end
11
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PackedString
4
+ class FastIdList
5
+ def self.from_array(array, pack_with)
6
+ new(array.sort.pack("#{pack_with}*"), pack_with)
7
+ end
8
+
9
+ def self.from_string(string, pack_with)
10
+ new(string, pack_with)
11
+ end
12
+
13
+ def initialize(packed, pack_with)
14
+ raise "unknown pack format, expecting Q or V" if pack_with != "V" && pack_with != "Q"
15
+ @packed = packed
16
+ @pack_with = pack_with
17
+ @slot_size = pack_with == "V" ? 4 : 8
18
+ end
19
+
20
+ def include?(id)
21
+ found = (0...length).bsearch do |index|
22
+ @packed.byteslice(index * @slot_size, @slot_size).unpack1(@pack_with) >= id
23
+ end
24
+
25
+ found && @packed.byteslice(found * @slot_size, @slot_size).unpack1(@pack_with) == id
26
+ end
27
+
28
+ def length
29
+ @length ||= @packed.bytesize / @slot_size
30
+ end
31
+
32
+ def to_a
33
+ @packed.unpack("#{@pack_with}*")
34
+ end
35
+
36
+ def to_s
37
+ @packed
38
+ end
39
+ end
40
+
41
+ def initialize(pack_with = "V")
42
+ @pack_with = pack_with
43
+ @oj_options = { mode: :compat }
44
+ end
45
+
46
+ def encode(hash)
47
+
48
+ if user_ids = hash["user_ids"]
49
+ hash["user_ids"] = FastIdList.from_array(hash["user_ids"], @pack_with).to_s
50
+ end
51
+
52
+ hash["data"] = ::Oj.dump(hash["data"], @oj_options)
53
+
54
+ Marshal.dump(hash)
55
+ end
56
+
57
+ def decode(payload)
58
+ result = Marshal.load(payload) # rubocop:disable Security/MarshalLoad
59
+ result["data"] = ::Oj.load(result["data"], @oj_options)
60
+
61
+ if str = result["user_ids"]
62
+ result["user_ids"] = FastIdList.from_string(str, @pack_with)
63
+ end
64
+
65
+ result
66
+ end
67
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StringHack
4
+ class FastIdList
5
+ def self.from_array(array)
6
+ new(",#{array.join(",")},")
7
+ end
8
+
9
+ def self.from_string(string)
10
+ new(string)
11
+ end
12
+
13
+ def initialize(packed)
14
+ @packed = packed
15
+ end
16
+
17
+ def include?(id)
18
+ @packed.include?(",#{id},")
19
+ end
20
+
21
+ def to_s
22
+ @packed
23
+ end
24
+ end
25
+
26
+ def initialize
27
+ @oj_options = { mode: :compat }
28
+ end
29
+
30
+ def encode(hash)
31
+ if user_ids = hash["user_ids"]
32
+ hash["user_ids"] = FastIdList.from_array(user_ids).to_s
33
+ end
34
+
35
+ ::Oj.dump(hash, @oj_options)
36
+ end
37
+
38
+ def decode(payload)
39
+ result = ::Oj.load(payload, @oj_options)
40
+
41
+ if str = result["user_ids"]
42
+ result["user_ids"] = FastIdList.from_string(str)
43
+ end
44
+
45
+ result
46
+ end
47
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+ gem 'message_bus', path: '../'
8
+ gem 'benchmark-ips'
9
+ gem 'oj'
10
+ end
11
+
12
+ require 'benchmark/ips'
13
+ require 'message_bus'
14
+ require_relative 'codecs/all_codecs'
15
+
16
+ bench_decode({
17
+ "data" => "hello world",
18
+ "user_ids" => (1..10000).to_a,
19
+ "group_ids" => nil,
20
+ "client_ids" => nil
21
+ }, 5000
22
+ )
23
+
24
+ # packed_string_4_bytes: 127176.1 i/s
25
+ # packed_string_8_bytes: 94494.6 i/s - 1.35x (± 0.00) slower
26
+ # string_hack: 26403.4 i/s - 4.82x (± 0.00) slower
27
+ # marshal: 4985.5 i/s - 25.51x (± 0.00) slower
28
+ # oj: 3072.9 i/s - 41.39x (± 0.00) slower
29
+ # json: 2222.7 i/s - 57.22x (± 0.00) slower
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+ gem 'message_bus', path: '../'
8
+ gem 'benchmark-ips'
9
+ gem 'oj'
10
+ end
11
+
12
+ require 'benchmark/ips'
13
+ require 'message_bus'
14
+ require_relative 'codecs/all_codecs'
15
+
16
+ bench_decode({
17
+ "data" => { amazing: "hello world this is an amazing message hello there!!!", another_key: [2, 3, 4] },
18
+ "user_ids" => [1, 2, 3],
19
+ "group_ids" => [1],
20
+ "client_ids" => nil
21
+ }, 2
22
+ )
23
+
24
+ # marshal: 504885.6 i/s
25
+ # json: 401050.9 i/s - 1.26x (± 0.00) slower
26
+ # oj: 340847.4 i/s - 1.48x (± 0.00) slower
27
+ # string_hack: 296741.6 i/s - 1.70x (± 0.00) slower
28
+ # packed_string_4_bytes: 207942.6 i/s - 2.43x (± 0.00) slower
29
+ # packed_string_8_bytes: 206093.0 i/s - 2.45x (± 0.00) slower
@@ -1,9 +1,9 @@
1
1
  -- wrk returns lots of read errors, this is unavoidable cause
2
2
  --
3
- -- 1. There is no internal implmentation of chunked encoding in wrk (which would be ideal)
3
+ -- 1. There is no internal implementation of chunked encoding in wrk (which would be ideal)
4
4
  --
5
5
  -- 2. MessageBus gem does not provide http keepalive (by design), and can not provide content length
6
- -- if MessageBus provided keepalive it would have to be able to redispatch the reqs to rack, something
6
+ -- if MessageBus provided keepalive it would have to be able to re-dispatch requests to rack, something
7
7
  -- that is not supported by the underlying rack hijack protocol, once a req is hijacked it can not be
8
8
  -- returned
9
9
  --
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "message_bus/backends"
4
-
5
3
  module MessageBus
6
4
  module Backends
7
5
  # Backends provide a consistent API over a variety of options for persisting
@@ -64,7 +62,7 @@ module MessageBus
64
62
  attr_accessor :max_backlog_size
65
63
  # @return [Integer] the largest permitted size (number of messages) for the global backlog; beyond this capacity, old messages will be dropped.
66
64
  attr_accessor :max_global_backlog_size
67
- # @return [Integer] the longest amount of time a message may live in a backlog before beging removed, in seconds.
65
+ # @return [Integer] the longest amount of time a message may live in a backlog before being removed, in seconds.
68
66
  attr_accessor :max_backlog_age
69
67
  # Typically, backlogs are trimmed whenever we publish to them. This setting allows some tolerance in order to improve performance.
70
68
  # @return [Integer] the interval of publications between which the backlog will not be cleared.
@@ -76,7 +74,7 @@ module MessageBus
76
74
  # @param [Integer] max_backlog_size the largest permitted size (number of messages) for per-channel backlogs; beyond this capacity, old messages will be dropped.
77
75
  def initialize(config = {}, max_backlog_size = 1000); end
78
76
 
79
- # Performs routines specific to the backend that are necessary after a process fork, typically triggerd by a forking webserver. Typically this re-opens sockets to the backend.
77
+ # Performs routines specific to the backend that are necessary after a process fork, typically triggered by a forking webserver. Typically this re-opens sockets to the backend.
80
78
  def after_fork
81
79
  raise ConcreteClassMustImplementError
82
80
  end
@@ -86,6 +84,11 @@ module MessageBus
86
84
  raise ConcreteClassMustImplementError
87
85
  end
88
86
 
87
+ # Closes all open connections to the storage.
88
+ def destroy
89
+ raise ConcreteClassMustImplementError
90
+ end
91
+
89
92
  # Deletes all backlogs and their data. Does not delete non-backlog data that message_bus may persist, depending on the concrete backend implementation. Use with extreme caution.
90
93
  # @abstract
91
94
  def expire_all_backlogs!
@@ -98,7 +101,7 @@ module MessageBus
98
101
  # @param [JSON] data some data to publish to the channel. Must be an object that can be encoded as JSON
99
102
  # @param [Hash] opts
100
103
  # @option opts [Boolean] :queue_in_memory (true) whether or not to hold the message in an in-memory buffer if publication fails, to be re-tried later
101
- # @option opts [Integer] :max_backlog_age (`self.max_backlog_age`) the longest amount of time a message may live in a backlog before beging removed, in seconds
104
+ # @option opts [Integer] :max_backlog_age (`self.max_backlog_age`) the longest amount of time a message may live in a backlog before being removed, in seconds
102
105
  # @option opts [Integer] :max_backlog_size (`self.max_backlog_size`) the largest permitted size (number of messages) for the channel backlog; beyond this capacity, old messages will be dropped
103
106
  #
104
107
  # @return [Integer] the channel-specific ID the message was given