message_bus 3.3.5 → 3.3.6
Sign up to get free protection for your applications and to get access to all the features.
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
|
+
});
|