message_bus 3.3.5 → 3.3.6
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.
Potentially problematic release.
This version of message_bus might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.eslintrc.js +13 -0
- data/.gitignore +2 -0
- data/CHANGELOG +8 -0
- data/Gemfile +2 -0
- data/README.md +25 -0
- data/Rakefile +1 -1
- data/assets/message-bus.js +69 -54
- data/bench/codecs/all_codecs.rb +39 -0
- data/bench/codecs/marshal.rb +11 -0
- data/bench/codecs/packed_string.rb +67 -0
- data/bench/codecs/string_hack.rb +47 -0
- data/bench/codecs_large_user_list.rb +29 -0
- data/bench/codecs_standard_message.rb +29 -0
- data/lib/message_bus.rb +32 -17
- data/lib/message_bus/backends/base.rb +0 -2
- data/lib/message_bus/backends/memory.rb +0 -2
- data/lib/message_bus/backends/postgres.rb +0 -2
- data/lib/message_bus/backends/redis.rb +2 -4
- data/lib/message_bus/codec/base.rb +18 -0
- data/lib/message_bus/codec/json.rb +15 -0
- data/lib/message_bus/codec/oj.rb +21 -0
- data/lib/message_bus/version.rb +1 -1
- data/package-lock.json +2192 -0
- data/package.json +7 -2
- data/spec/lib/message_bus_spec.rb +1 -1
- metadata +14 -4
- data/lib/message_bus/em_ext.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52ac95c63b3775df984e4900aff333c2ea9f9bab7e79d16b85909048247fb709
|
4
|
+
data.tar.gz: 879fd243f8947150fe8a31307d023b6ca719192568e0def45255dfdddaf1e34f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe7252d6965628dd75027af80f775126394e6b3a81be01fd03f538f17fe5e7874e997cea552ee2e92d91b7ee47d17059755e7df995c5b8bab24049ce2746e0a8
|
7
|
+
data.tar.gz: 2c4ef234bab5ee1701b5bb2d7362929128855dda113a2531dd35f025b9d2be11a2c18a287aa25731e73a9e8f82af898e2f7725a7c9ea4200a390eaff633b4098
|
data/.eslintrc.js
ADDED
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
31-05-2021
|
2
|
+
|
3
|
+
- Version 3.3.6
|
4
|
+
|
5
|
+
- FEATURE: Introduce support for transport codecs
|
6
|
+
- FIX: event subscription leak in JS after start/stop/start sequence
|
7
|
+
- FEATURE: MessageBus.onVisibilityChange() can be used to trigger a visiblity change check by hand
|
8
|
+
|
1
9
|
28-04-2021
|
2
10
|
|
3
11
|
- Version 3.3.5
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -435,6 +435,31 @@ MessageBus.configure(backend: :memory)
|
|
435
435
|
|
436
436
|
The `:clear_every` option supported by the PostgreSQL backend is also supported by the in-memory backend.
|
437
437
|
|
438
|
+
|
439
|
+
### Transport codecs
|
440
|
+
|
441
|
+
By default MessageBus serializes messages to the backend using JSON. Under most situation this performs extremely well.
|
442
|
+
|
443
|
+
In some exceptional cases you may consider a different transport codec. To configure a custom codec use:
|
444
|
+
|
445
|
+
```ruby
|
446
|
+
MessageBus.configure(transport_codec: codec)
|
447
|
+
```
|
448
|
+
|
449
|
+
A codec class must implement MessageBus::Codec::Base. Specifically an `encode` and `decode` method.
|
450
|
+
|
451
|
+
See the `bench` directory for examples where the default JSON codec can perform poorly. A specific examples may be
|
452
|
+
attempting to distribute a message to a restricted list of thousands of users. In cases like this you may consider
|
453
|
+
using a packed string encoder.
|
454
|
+
|
455
|
+
Keep in mind, much of MessageBus internals and supporting tools expect data to be converted to JSON and back, if you use a naive (and fast) `Marshal` based codec you may need to limit the features you use. Specifically the Postgresql backend expects the codec never to return a string with `\u0000`, additionally some classes like DistributedCache expect keys to be converted to Strings.
|
456
|
+
|
457
|
+
Another example may be very large and complicated messages where Oj in compatability mode outperforms JSON. To opt for the Oj codec use:
|
458
|
+
|
459
|
+
```
|
460
|
+
MessageBus.configure(transport_codec: MessageBus::Codec::Oj.new)
|
461
|
+
```
|
462
|
+
|
438
463
|
### Forking/threading app servers
|
439
464
|
|
440
465
|
If you're using a forking or threading app server and you're not getting immediate delivery of published messages, you might need to configure your web server to re-connect to the message_bus backend
|
data/Rakefile
CHANGED
data/assets/message-bus.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
/*
|
1
|
+
/*global define, jQuery*/
|
2
2
|
|
3
3
|
(function (root, factory) {
|
4
|
-
if (typeof define ===
|
4
|
+
if (typeof define === "function" && define.amd) {
|
5
5
|
// AMD. Register as an anonymous module.
|
6
|
-
define([], function (
|
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 !==
|
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);
|
@@ -42,7 +42,7 @@
|
|
42
42
|
var totalAjaxCalls = 0;
|
43
43
|
var lastAjax;
|
44
44
|
|
45
|
-
var isHidden = (function() {
|
45
|
+
var isHidden = (function () {
|
46
46
|
var prefixes = ["", "webkit", "ms", "moz"];
|
47
47
|
var hiddenProperty;
|
48
48
|
for (var i = 0; i < prefixes.length; i++) {
|
@@ -53,7 +53,7 @@
|
|
53
53
|
}
|
54
54
|
}
|
55
55
|
|
56
|
-
return function() {
|
56
|
+
return function () {
|
57
57
|
if (hiddenProperty !== undefined) {
|
58
58
|
return document[hiddenProperty];
|
59
59
|
} else {
|
@@ -62,7 +62,7 @@
|
|
62
62
|
};
|
63
63
|
})();
|
64
64
|
|
65
|
-
var hasLocalStorage = (function() {
|
65
|
+
var hasLocalStorage = (function () {
|
66
66
|
try {
|
67
67
|
localStorage.setItem("mbTestLocalStorage", Date.now());
|
68
68
|
localStorage.removeItem("mbTestLocalStorage");
|
@@ -72,13 +72,13 @@
|
|
72
72
|
}
|
73
73
|
})();
|
74
74
|
|
75
|
-
var updateLastAjax = function() {
|
75
|
+
var updateLastAjax = function () {
|
76
76
|
if (hasLocalStorage) {
|
77
77
|
localStorage.setItem("__mbLastAjax", Date.now());
|
78
78
|
}
|
79
79
|
};
|
80
80
|
|
81
|
-
var hiddenTabShouldWait = function() {
|
81
|
+
var hiddenTabShouldWait = function () {
|
82
82
|
if (hasLocalStorage && isHidden()) {
|
83
83
|
var lastAjaxCall = parseInt(localStorage.getItem("__mbLastAjax"), 10);
|
84
84
|
var deltaAjax = Date.now() - lastAjaxCall;
|
@@ -89,19 +89,21 @@
|
|
89
89
|
};
|
90
90
|
|
91
91
|
var hasonprogress = new XMLHttpRequest().onprogress === null;
|
92
|
-
var allowChunked = function() {
|
92
|
+
var allowChunked = function () {
|
93
93
|
return me.enableChunkedEncoding && hasonprogress;
|
94
94
|
};
|
95
95
|
|
96
|
-
var shouldLongPoll = function() {
|
96
|
+
var shouldLongPoll = function () {
|
97
97
|
return (
|
98
98
|
me.alwaysLongPoll ||
|
99
99
|
(me.shouldLongPollCallback ? me.shouldLongPollCallback() : !isHidden())
|
100
100
|
);
|
101
101
|
};
|
102
102
|
|
103
|
-
var processMessages = function(messages) {
|
104
|
-
if (
|
103
|
+
var processMessages = function (messages) {
|
104
|
+
if (!messages || messages.length === 0) {
|
105
|
+
return false;
|
106
|
+
}
|
105
107
|
|
106
108
|
for (var i = 0; i < messages.length; i++) {
|
107
109
|
var message = messages[i];
|
@@ -133,7 +135,7 @@
|
|
133
135
|
return true;
|
134
136
|
};
|
135
137
|
|
136
|
-
var reqSuccess = function(messages) {
|
138
|
+
var reqSuccess = function (messages) {
|
137
139
|
failCount = 0;
|
138
140
|
if (paused) {
|
139
141
|
if (messages) {
|
@@ -147,7 +149,7 @@
|
|
147
149
|
return false;
|
148
150
|
};
|
149
151
|
|
150
|
-
var longPoller = function(poll, data) {
|
152
|
+
var longPoller = function (poll, data) {
|
151
153
|
if (ajaxInProgress) {
|
152
154
|
// never allow concurrent ajax reqs
|
153
155
|
return;
|
@@ -180,7 +182,7 @@
|
|
180
182
|
|
181
183
|
var dataType = chunked ? "text" : "json";
|
182
184
|
|
183
|
-
var handle_progress = function(payload, position) {
|
185
|
+
var handle_progress = function (payload, position) {
|
184
186
|
var separator = "\r\n|\r\n";
|
185
187
|
var endChunk = payload.indexOf(separator, position);
|
186
188
|
|
@@ -203,7 +205,7 @@
|
|
203
205
|
return handle_progress(payload, endChunk + separator.length);
|
204
206
|
};
|
205
207
|
|
206
|
-
var disableChunked = function() {
|
208
|
+
var disableChunked = function () {
|
207
209
|
if (me.longPoll) {
|
208
210
|
me.longPoll.abort();
|
209
211
|
chunkedBackoff = 30;
|
@@ -231,12 +233,12 @@
|
|
231
233
|
headers: headers,
|
232
234
|
messageBus: {
|
233
235
|
chunked: chunked,
|
234
|
-
onProgressListener: function(xhr) {
|
236
|
+
onProgressListener: function (xhr) {
|
235
237
|
var position = 0;
|
236
238
|
// if it takes longer than 3000 ms to get first chunk, we have some proxy
|
237
239
|
// this is messing with us, so just backoff from using chunked for now
|
238
240
|
var chunkedTimeout = setTimeout(disableChunked, 3000);
|
239
|
-
return (xhr.onprogress = function() {
|
241
|
+
return (xhr.onprogress = function () {
|
240
242
|
clearTimeout(chunkedTimeout);
|
241
243
|
if (
|
242
244
|
xhr.getResponseHeader("Content-Type") ===
|
@@ -247,9 +249,9 @@
|
|
247
249
|
position = handle_progress(xhr.responseText, position);
|
248
250
|
}
|
249
251
|
});
|
250
|
-
}
|
252
|
+
},
|
251
253
|
},
|
252
|
-
xhr: function() {
|
254
|
+
xhr: function () {
|
253
255
|
var xhr = jQuery.ajaxSettings.xhr();
|
254
256
|
if (!chunked) {
|
255
257
|
return xhr;
|
@@ -257,7 +259,7 @@
|
|
257
259
|
this.messageBus.onProgressListener(xhr);
|
258
260
|
return xhr;
|
259
261
|
},
|
260
|
-
success: function(messages) {
|
262
|
+
success: function (messages) {
|
261
263
|
if (!chunked) {
|
262
264
|
// we may have requested text so jQuery will not parse
|
263
265
|
if (typeof messages === "string") {
|
@@ -266,7 +268,7 @@
|
|
266
268
|
gotData = reqSuccess(messages);
|
267
269
|
}
|
268
270
|
},
|
269
|
-
error: function(xhr, textStatus
|
271
|
+
error: function (xhr, textStatus) {
|
270
272
|
if (xhr.status === 429) {
|
271
273
|
var tryAfter =
|
272
274
|
parseInt(
|
@@ -285,7 +287,7 @@
|
|
285
287
|
totalAjaxFailures += 1;
|
286
288
|
}
|
287
289
|
},
|
288
|
-
complete: function() {
|
290
|
+
complete: function () {
|
289
291
|
ajaxInProgress = false;
|
290
292
|
|
291
293
|
var interval;
|
@@ -323,14 +325,14 @@
|
|
323
325
|
}
|
324
326
|
|
325
327
|
if (started) {
|
326
|
-
pollTimeout = setTimeout(function() {
|
328
|
+
pollTimeout = setTimeout(function () {
|
327
329
|
pollTimeout = null;
|
328
330
|
poll();
|
329
331
|
}, interval);
|
330
332
|
}
|
331
333
|
|
332
334
|
me.longPoll = null;
|
333
|
-
}
|
335
|
+
},
|
334
336
|
});
|
335
337
|
|
336
338
|
return req;
|
@@ -352,7 +354,7 @@
|
|
352
354
|
baseUrl: baseUrl,
|
353
355
|
headers: {},
|
354
356
|
ajax: typeof jQuery !== "undefined" && jQuery.ajax,
|
355
|
-
diagnostics: function() {
|
357
|
+
diagnostics: function () {
|
356
358
|
console.log("Stopped: " + stopped + " Started: " + started);
|
357
359
|
console.log("Current callbacks");
|
358
360
|
console.log(callbacks);
|
@@ -369,42 +371,50 @@
|
|
369
371
|
);
|
370
372
|
},
|
371
373
|
|
372
|
-
pause: function() {
|
374
|
+
pause: function () {
|
373
375
|
paused = true;
|
374
376
|
},
|
375
377
|
|
376
|
-
resume: function() {
|
378
|
+
resume: function () {
|
377
379
|
paused = false;
|
378
380
|
processMessages(later);
|
379
381
|
later = [];
|
380
382
|
},
|
381
383
|
|
382
|
-
stop: function() {
|
384
|
+
stop: function () {
|
383
385
|
stopped = true;
|
384
386
|
started = false;
|
385
387
|
if (delayPollTimeout) {
|
386
388
|
clearTimeout(delayPollTimeout);
|
387
389
|
delayPollTimeout = null;
|
388
390
|
}
|
391
|
+
if (pollTimeout) {
|
392
|
+
clearTimeout(pollTimeout);
|
393
|
+
pollTimeout = null;
|
394
|
+
}
|
389
395
|
if (me.longPoll) {
|
390
396
|
me.longPoll.abort();
|
391
397
|
}
|
398
|
+
if (me.onVisibilityChange) {
|
399
|
+
document.removeEventListener("visibilitychange", me.onVisibilityChange);
|
400
|
+
me.onVisibilityChange = null;
|
401
|
+
}
|
392
402
|
},
|
393
403
|
|
394
404
|
// Start polling
|
395
|
-
start: function() {
|
405
|
+
start: function () {
|
396
406
|
if (started) return;
|
397
407
|
started = true;
|
398
408
|
stopped = false;
|
399
409
|
|
400
|
-
var poll = function() {
|
410
|
+
var poll = function () {
|
401
411
|
if (stopped) {
|
402
412
|
return;
|
403
413
|
}
|
404
414
|
|
405
415
|
if (callbacks.length === 0 || hiddenTabShouldWait()) {
|
406
416
|
if (!delayPollTimeout) {
|
407
|
-
delayPollTimeout = setTimeout(function() {
|
417
|
+
delayPollTimeout = setTimeout(function () {
|
408
418
|
delayPollTimeout = null;
|
409
419
|
poll();
|
410
420
|
}, parseInt(500 + Math.random() * 500));
|
@@ -426,25 +436,28 @@
|
|
426
436
|
|
427
437
|
// monitor visibility, issue a new long poll when the page shows
|
428
438
|
if (document.addEventListener && "hidden" in document) {
|
429
|
-
me.
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
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();
|
440
451
|
}
|
441
|
-
|
452
|
+
};
|
453
|
+
|
454
|
+
document.addEventListener("visibilitychange", me.onVisibilityChange);
|
442
455
|
}
|
443
456
|
|
444
457
|
poll();
|
445
458
|
},
|
446
459
|
|
447
|
-
status: function() {
|
460
|
+
status: function () {
|
448
461
|
if (paused) {
|
449
462
|
return "paused";
|
450
463
|
} else if (started) {
|
@@ -463,7 +476,7 @@
|
|
463
476
|
// -2 will recieve last message + all new messages
|
464
477
|
// -3 will recieve last 2 messages + all new messages
|
465
478
|
// if undefined will default to -1
|
466
|
-
subscribe: function(channel, func, lastId) {
|
479
|
+
subscribe: function (channel, func, lastId) {
|
467
480
|
if (!started && !stopped) {
|
468
481
|
me.start();
|
469
482
|
}
|
@@ -471,7 +484,9 @@
|
|
471
484
|
if (lastId === null || typeof lastId === "undefined") {
|
472
485
|
lastId = -1;
|
473
486
|
} else if (typeof lastId !== "number") {
|
474
|
-
throw
|
487
|
+
throw (
|
488
|
+
"lastId has type " + typeof lastId + " but a number was expected."
|
489
|
+
);
|
475
490
|
}
|
476
491
|
|
477
492
|
if (typeof channel !== "string") {
|
@@ -481,7 +496,7 @@
|
|
481
496
|
callbacks.push({
|
482
497
|
channel: channel,
|
483
498
|
func: func,
|
484
|
-
last_id: lastId
|
499
|
+
last_id: lastId,
|
485
500
|
});
|
486
501
|
if (me.longPoll) {
|
487
502
|
me.longPoll.abort();
|
@@ -491,7 +506,7 @@
|
|
491
506
|
},
|
492
507
|
|
493
508
|
// Unsubscribe from a channel
|
494
|
-
unsubscribe: function(channel, func) {
|
509
|
+
unsubscribe: function (channel, func) {
|
495
510
|
// TODO allow for globbing in the middle of a channel name
|
496
511
|
// like /something/*/something
|
497
512
|
// at the moment we only support globbing /something/*
|
@@ -528,7 +543,7 @@
|
|
528
543
|
}
|
529
544
|
|
530
545
|
return removed;
|
531
|
-
}
|
546
|
+
},
|
532
547
|
};
|
533
548
|
return me;
|
534
|
-
})
|
549
|
+
});
|