message_bus 2.1.5 → 2.1.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/CHANGELOG +9 -0
- data/Gemfile +2 -0
- data/README.md +69 -5
- data/assets/message-bus.js +20 -6
- data/lib/message_bus.rb +4 -1
- data/lib/message_bus/distributed_cache.rb +165 -0
- data/lib/message_bus/timer_thread.rb +12 -5
- data/lib/message_bus/version.rb +1 -1
- data/spec/assets/SpecHelper.js +20 -9
- data/spec/assets/message-bus.spec.js +6 -0
- data/spec/lib/message_bus/distributed_cache_spec.rb +133 -0
- data/spec/lib/message_bus/timer_thread_spec.rb +3 -3
- data/spec/lib/message_bus_spec.rb +20 -0
- data/spec/spec_helper.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41c92c01f6f8f8b478877cd97213ceaa9517b28eddb42edc6e421a39d8e6da50
|
4
|
+
data.tar.gz: 00bab454cbce2f3f264eda6470ade5d61bfde42ddbc8709d288da6ee2c204dd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e009850b2ecd51ed40978993297b67f70e2cb50939e46f165630ce5aa3c1b42170e7ca70f753888218c00ec7fb33dd6ece38900d263f683e2183a66a7cbf0e7a
|
7
|
+
data.tar.gz: 435044844fc8f8620d3ddbac25c5cae390e4ee2988cca28f37fa41da18f12b57293ef7686ecd412af3be23a5a9787e29c459d7bd52548fd1a7bf5378956d5a80
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
15-10-2018
|
2
|
+
|
3
|
+
- Version 2.1.6
|
4
|
+
|
5
|
+
- FEATURE: `MesssageBus.publish` accepts option `site_id` to publish to a site
|
6
|
+
- FEATURE: Added MessageBus::DistributedCache for cross process caching
|
7
|
+
- PERF: Use monotonic times in timer thread
|
8
|
+
- FEATURE: min poll interval is now configurable client side
|
9
|
+
|
1
10
|
16-05-2018
|
2
11
|
|
3
12
|
- Version 2.1.5
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -14,7 +14,6 @@ Live chat demo per [examples/chat](https://github.com/SamSaffron/message_bus/tre
|
|
14
14
|
|
15
15
|
### http://chat.samsaffron.com
|
16
16
|
|
17
|
-
|
18
17
|
## Want to help?
|
19
18
|
|
20
19
|
If you are looking to contribute to this project here are some ideas
|
@@ -119,6 +118,18 @@ MessageBus.user_id_lookup do |env|
|
|
119
118
|
end
|
120
119
|
```
|
121
120
|
|
121
|
+
### Debugging
|
122
|
+
|
123
|
+
When setting up MessageBus, it's good to manually check the channels before integrating the client.
|
124
|
+
|
125
|
+
You can `curl` MessageBus. This is helpful when trying to debug what may be doing wrong. This uses https://chat.samsaffron.com.
|
126
|
+
|
127
|
+
```
|
128
|
+
curl -H "Content-Type: application/x-www-form-urlencoded" -X POST --data "/message=0" https://chat.samsaffron.com/message-bus/client-id/poll\?dlp\=t
|
129
|
+
```
|
130
|
+
|
131
|
+
You should see a reply with the messages of that channel you requested for (`/message`) starting at the message ID (`0`). `dlp=t` disables long-polling: we do not want this request to stay open.
|
132
|
+
|
122
133
|
### Transport
|
123
134
|
|
124
135
|
MessageBus ships with 3 transport mechanisms.
|
@@ -165,7 +176,8 @@ Polling also requires no special setup, MessageBus will fallback to polling afte
|
|
165
176
|
MessageBus can be used in an environment that hosts multiple sites by multiplexing channels. To use this mode
|
166
177
|
|
167
178
|
```ruby
|
168
|
-
# define a site_id lookup method
|
179
|
+
# define a site_id lookup method, which is executed
|
180
|
+
# when `MessageBus.publish` is called
|
169
181
|
MessageBus.configure(site_id_lookup: proc do
|
170
182
|
some_method_that_returns_site_id_string
|
171
183
|
end)
|
@@ -173,6 +185,11 @@ end)
|
|
173
185
|
# you may post messages just to this site
|
174
186
|
MessageBus.publish "/channel", "some message"
|
175
187
|
|
188
|
+
# you can also choose to pass the `site_id`.
|
189
|
+
# This takes precendence over whatever `site_id_lookup`
|
190
|
+
# returns
|
191
|
+
MessageBus.publish "/channel", "some message", site_id: "site-id"
|
192
|
+
|
176
193
|
# you may publish messages to ALL sites using the /global/ prefix
|
177
194
|
MessageBus.publish "/global/channel", "will go to all sites"
|
178
195
|
|
@@ -238,6 +255,7 @@ Setting|Default|Info
|
|
238
255
|
enableLongPolling|true|Allow long-polling (provided it is enable by the server)
|
239
256
|
callbackInterval|15000|Safeguard to ensure background polling does not exceed this interval (in milliseconds)
|
240
257
|
backgroundCallbackInterval|60000|Interval to poll when long polling is disabled (either explicitly or due to browser being in background)
|
258
|
+
minPollInterval|100|When polling requests succeed, this is the minimum amount of time to wait before making the next request.
|
241
259
|
maxPollInterval|180000|If request to the server start failing, MessageBus will backoff, this is the upper limit of the backoff.
|
242
260
|
alwaysLongPoll|false|For debugging you may want to disable the "is browser in background" check and always long-poll
|
243
261
|
baseUrl|/|If message bus is mounted in a subdirectory of different domain, you may configure it to perform requests there
|
@@ -294,13 +312,13 @@ This is configurable via accessors on the ReliablePubSub instance.
|
|
294
312
|
|
295
313
|
```ruby
|
296
314
|
# only store 100 messages per channel
|
297
|
-
MessageBus.
|
315
|
+
MessageBus.reliable_pub_sub.max_backlog_size = 100
|
298
316
|
|
299
317
|
# only store 100 global messages
|
300
|
-
MessageBus.
|
318
|
+
MessageBus.reliable_pub_sub.max_global_backlog_size = 100
|
301
319
|
|
302
320
|
# flush per-channel backlog after 100 seconds of inactivity
|
303
|
-
MessageBus.
|
321
|
+
MessageBus.reliable_pub_sub.max_backlog_age = 100
|
304
322
|
|
305
323
|
```
|
306
324
|
|
@@ -395,6 +413,52 @@ Rails.application.config do |config|
|
|
395
413
|
end
|
396
414
|
```
|
397
415
|
|
416
|
+
### A Distributed Cache
|
417
|
+
|
418
|
+
MessageBus ships with an optional DistributedCache object you can use to synchronize a cache between processes.
|
419
|
+
It allows you a simple and efficient way of synchronizing a cache between processes.
|
420
|
+
|
421
|
+
```ruby
|
422
|
+
require 'message_bus/distributed_cache'
|
423
|
+
|
424
|
+
# process 1
|
425
|
+
|
426
|
+
cache = MessageBus::DistributedCache.new("animals")
|
427
|
+
|
428
|
+
# process 2
|
429
|
+
|
430
|
+
cache = MessageBus::DistributedCache.new("animals")
|
431
|
+
|
432
|
+
# process 1
|
433
|
+
|
434
|
+
cache["frogs"] = 5
|
435
|
+
|
436
|
+
# process 2
|
437
|
+
|
438
|
+
puts cache["frogs"]
|
439
|
+
# => 5
|
440
|
+
|
441
|
+
cache["frogs"] = nil
|
442
|
+
|
443
|
+
# process 1
|
444
|
+
|
445
|
+
puts cache["frogs"]
|
446
|
+
# => nil
|
447
|
+
|
448
|
+
```
|
449
|
+
|
450
|
+
Automatically expiring the cache on app update:
|
451
|
+
|
452
|
+
```ruby
|
453
|
+
cache = MessageBus::DistributedCache.new("cache name", app_version: "12.1.7.ABDEB")
|
454
|
+
cache["a"] = 77
|
455
|
+
|
456
|
+
cache = MessageBus::DistributedCache.new("cache name", app_version: "12.1.7.ABDEF")
|
457
|
+
|
458
|
+
puts cache["a"]
|
459
|
+
# => nil
|
460
|
+
```
|
461
|
+
|
398
462
|
#### Error Handling
|
399
463
|
|
400
464
|
The internet is a chaotic environment and clients can drop off for a variety of reasons. If this happens while MessageBus is trying to write a message to the client you may see something like this in your logs:
|
data/assets/message-bus.js
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
|
7
7
|
var callbacks, clientId, failCount, shouldLongPoll, queue, responseCallbacks, uniqueId, baseUrl;
|
8
8
|
var me, started, stopped, longPoller, pollTimeout, paused, later, jQuery, interval, chunkedBackoff;
|
9
|
+
var delayPollTimeout;
|
9
10
|
|
10
11
|
var ajaxInProgress = false;
|
11
12
|
|
@@ -277,7 +278,7 @@
|
|
277
278
|
var interval;
|
278
279
|
try {
|
279
280
|
if (gotData || aborted) {
|
280
|
-
interval =
|
281
|
+
interval = me.minPollInterval;
|
281
282
|
} else {
|
282
283
|
interval = me.callbackInterval;
|
283
284
|
if (failCount > 2) {
|
@@ -303,11 +304,16 @@
|
|
303
304
|
|
304
305
|
if (pollTimeout) {
|
305
306
|
clearTimeout(pollTimeout);
|
306
|
-
}
|
307
|
-
pollTimeout = setTimeout(function(){
|
308
307
|
pollTimeout = null;
|
309
|
-
|
310
|
-
|
308
|
+
}
|
309
|
+
|
310
|
+
if (started) {
|
311
|
+
pollTimeout = setTimeout(function(){
|
312
|
+
pollTimeout = null;
|
313
|
+
poll();
|
314
|
+
}, interval);
|
315
|
+
}
|
316
|
+
|
311
317
|
me.longPoll = null;
|
312
318
|
}
|
313
319
|
});
|
@@ -322,6 +328,7 @@
|
|
322
328
|
enableLongPolling: true,
|
323
329
|
callbackInterval: 15000,
|
324
330
|
backgroundCallbackInterval: 60000,
|
331
|
+
minPollInterval: 100,
|
325
332
|
maxPollInterval: 3 * 60 * 1000,
|
326
333
|
callbacks: callbacks,
|
327
334
|
clientId: clientId,
|
@@ -354,11 +361,18 @@
|
|
354
361
|
stop: function() {
|
355
362
|
stopped = true;
|
356
363
|
started = false;
|
364
|
+
if (delayPollTimeout) {
|
365
|
+
clearTimeout(delayPollTimeout);
|
366
|
+
delayPollTimeout = null;
|
367
|
+
}
|
368
|
+
if (me.longPoll) {
|
369
|
+
me.longPoll.abort();
|
370
|
+
}
|
357
371
|
},
|
358
372
|
|
359
373
|
// Start polling
|
360
374
|
start: function() {
|
361
|
-
var poll
|
375
|
+
var poll;
|
362
376
|
|
363
377
|
if (started) return;
|
364
378
|
started = true;
|
data/lib/message_bus.rb
CHANGED
@@ -226,10 +226,12 @@ module MessageBus::Implementation
|
|
226
226
|
group_ids = nil
|
227
227
|
client_ids = nil
|
228
228
|
|
229
|
+
site_id = nil
|
229
230
|
if opts
|
230
231
|
user_ids = opts[:user_ids]
|
231
232
|
group_ids = opts[:group_ids]
|
232
233
|
client_ids = opts[:client_ids]
|
234
|
+
site_id = opts[:site_id]
|
233
235
|
end
|
234
236
|
|
235
237
|
raise ::MessageBus::InvalidMessage if (user_ids || group_ids) && global?(channel)
|
@@ -250,7 +252,8 @@ module MessageBus::Implementation
|
|
250
252
|
}
|
251
253
|
end
|
252
254
|
|
253
|
-
|
255
|
+
encoded_channel_name = encode_channel_name(channel, site_id)
|
256
|
+
reliable_pub_sub.publish(encoded_channel_name, encoded_data, channel_opts)
|
254
257
|
end
|
255
258
|
|
256
259
|
def blocking_subscribe(channel = nil, &blk)
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Like a hash, just does its best to stay in sync across the farm.
|
4
|
+
# On boot all instances are blank, but they populate as various processes
|
5
|
+
# fill it up.
|
6
|
+
|
7
|
+
require 'weakref'
|
8
|
+
require 'base64'
|
9
|
+
require 'securerandom'
|
10
|
+
|
11
|
+
module MessageBus
|
12
|
+
class DistributedCache
|
13
|
+
|
14
|
+
DEFAULT_SITE_ID = 'default'
|
15
|
+
|
16
|
+
class Manager
|
17
|
+
CHANNEL_NAME ||= '/distributed_hash'.freeze
|
18
|
+
|
19
|
+
attr_accessor :app_version
|
20
|
+
|
21
|
+
def initialize(message_bus = nil)
|
22
|
+
@subscribers = []
|
23
|
+
@subscribed = false
|
24
|
+
@lock = Mutex.new
|
25
|
+
@message_bus = message_bus || MessageBus
|
26
|
+
end
|
27
|
+
|
28
|
+
def subscribers
|
29
|
+
@subscribers
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_message(message)
|
33
|
+
i = @subscribers.length - 1
|
34
|
+
|
35
|
+
payload = message.data
|
36
|
+
|
37
|
+
while i >= 0
|
38
|
+
begin
|
39
|
+
current = @subscribers[i]
|
40
|
+
|
41
|
+
next if payload["origin"] == current.identity
|
42
|
+
next if current.key != payload["hash_key"]
|
43
|
+
|
44
|
+
next if @app_version && payload["app_version"] != @app_version
|
45
|
+
|
46
|
+
hash = current.hash(message.site_id || DEFAULT_SITE_ID)
|
47
|
+
|
48
|
+
case payload["op"]
|
49
|
+
when "set" then hash[payload["key"]] = payload["marshalled"] ? Marshal.load(Base64.decode64(payload["value"])) : payload["value"]
|
50
|
+
when "delete" then hash.delete(payload["key"])
|
51
|
+
when "clear" then hash.clear
|
52
|
+
end
|
53
|
+
|
54
|
+
rescue WeakRef::RefError
|
55
|
+
@subscribers.delete_at(i)
|
56
|
+
ensure
|
57
|
+
i -= 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def ensure_subscribe!
|
63
|
+
return if @subscribed
|
64
|
+
@lock.synchronize do
|
65
|
+
return if @subscribed
|
66
|
+
@message_bus.subscribe(CHANNEL_NAME) do |message|
|
67
|
+
@lock.synchronize do
|
68
|
+
process_message(message)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@subscribed = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def publish(hash, message)
|
76
|
+
message[:origin] = hash.identity
|
77
|
+
message[:hash_key] = hash.key
|
78
|
+
message[:app_version] = @app_version if @app_version
|
79
|
+
@message_bus.publish(CHANNEL_NAME, message, user_ids: [-1])
|
80
|
+
end
|
81
|
+
|
82
|
+
def set(hash, key, value)
|
83
|
+
# special support for set
|
84
|
+
marshal = (Set === value || Hash === value || Array === value)
|
85
|
+
value = Base64.encode64(Marshal.dump(value)) if marshal
|
86
|
+
publish(hash, op: :set, key: key, value: value, marshalled: marshal)
|
87
|
+
end
|
88
|
+
|
89
|
+
def delete(hash, key)
|
90
|
+
publish(hash, op: :delete, key: key)
|
91
|
+
end
|
92
|
+
|
93
|
+
def clear(hash)
|
94
|
+
publish(hash, op: :clear)
|
95
|
+
end
|
96
|
+
|
97
|
+
def register(hash)
|
98
|
+
@lock.synchronize do
|
99
|
+
@subscribers << WeakRef.new(hash)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
@default_manager = Manager.new
|
105
|
+
|
106
|
+
def self.default_manager
|
107
|
+
@default_manager
|
108
|
+
end
|
109
|
+
|
110
|
+
attr_reader :key
|
111
|
+
|
112
|
+
def initialize(key, manager: nil, namespace: true, app_version: nil)
|
113
|
+
@key = key
|
114
|
+
@data = {}
|
115
|
+
@manager = manager || DistributedCache.default_manager
|
116
|
+
@manager.app_version = app_version if app_version
|
117
|
+
@namespace = namespace
|
118
|
+
@app_version = app_version
|
119
|
+
|
120
|
+
@manager.ensure_subscribe!
|
121
|
+
@manager.register(self)
|
122
|
+
end
|
123
|
+
|
124
|
+
def identity
|
125
|
+
# fork resilient / multi machine identity
|
126
|
+
(@seed_id ||= SecureRandom.hex) + "#{Process.pid}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def []=(k, v)
|
130
|
+
k = k.to_s if Symbol === k
|
131
|
+
@manager.set(self, k, v)
|
132
|
+
hash[k] = v
|
133
|
+
end
|
134
|
+
|
135
|
+
def [](k)
|
136
|
+
k = k.to_s if Symbol === k
|
137
|
+
hash[k]
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete(k, publish: true)
|
141
|
+
k = k.to_s if Symbol === k
|
142
|
+
@manager.delete(self, k) if publish
|
143
|
+
hash.delete(k)
|
144
|
+
end
|
145
|
+
|
146
|
+
def clear
|
147
|
+
@manager.clear(self)
|
148
|
+
hash.clear
|
149
|
+
end
|
150
|
+
|
151
|
+
def hash(site_id_arg = nil)
|
152
|
+
site_id =
|
153
|
+
if @namespace
|
154
|
+
site_id_arg ||
|
155
|
+
(MessageBus.site_id_lookup && MessageBus.site_id_lookup.call) ||
|
156
|
+
DEFAULT_SITE_ID
|
157
|
+
else
|
158
|
+
DEFAULT_SITE_ID
|
159
|
+
end
|
160
|
+
|
161
|
+
@data[site_id] ||= {}
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
@@ -4,7 +4,14 @@ class MessageBus::TimerThread
|
|
4
4
|
attr_reader :jobs
|
5
5
|
|
6
6
|
class Cancelable
|
7
|
-
|
7
|
+
class NoOp
|
8
|
+
def call
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# usually you could just use a blank lambda
|
13
|
+
# but an object is ever so slightly faster
|
14
|
+
NOOP = NoOp.new
|
8
15
|
|
9
16
|
def initialize(job)
|
10
17
|
@job = job
|
@@ -58,7 +65,7 @@ class MessageBus::TimerThread
|
|
58
65
|
|
59
66
|
# queue a block to run after a certain delay (in seconds)
|
60
67
|
def queue(delay = 0, &block)
|
61
|
-
queue_time =
|
68
|
+
queue_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + delay
|
62
69
|
job = [queue_time, block]
|
63
70
|
|
64
71
|
@mutex.synchronize do
|
@@ -96,7 +103,7 @@ class MessageBus::TimerThread
|
|
96
103
|
|
97
104
|
def do_work
|
98
105
|
while !@stopped
|
99
|
-
if @next && @next <=
|
106
|
+
if @next && @next <= Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
100
107
|
_, blk = @mutex.synchronize { @jobs.shift }
|
101
108
|
begin
|
102
109
|
blk.call
|
@@ -107,10 +114,10 @@ class MessageBus::TimerThread
|
|
107
114
|
@next, _ = @jobs[0]
|
108
115
|
end
|
109
116
|
end
|
110
|
-
unless @next && @next <=
|
117
|
+
unless @next && @next <= Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
111
118
|
sleep_time = 1000
|
112
119
|
@mutex.synchronize do
|
113
|
-
sleep_time = @next -
|
120
|
+
sleep_time = @next - Process.clock_gettime(Process::CLOCK_MONOTONIC) if @next
|
114
121
|
end
|
115
122
|
sleep [0, sleep_time].max
|
116
123
|
end
|
data/lib/message_bus/version.rb
CHANGED
data/spec/assets/SpecHelper.js
CHANGED
@@ -27,6 +27,7 @@ beforeEach(function () {
|
|
27
27
|
function MockedXMLHttpRequest(){
|
28
28
|
this.headers = {};
|
29
29
|
};
|
30
|
+
|
30
31
|
MockedXMLHttpRequest.prototype.send = function(){
|
31
32
|
this.readyState = 4
|
32
33
|
this.responseText = encodeChunks(this, spec.responseChunks);
|
@@ -35,21 +36,38 @@ beforeEach(function () {
|
|
35
36
|
if (this.onprogress){ this.onprogress(); }
|
36
37
|
this.onreadystatechange()
|
37
38
|
}
|
39
|
+
|
38
40
|
MockedXMLHttpRequest.prototype.open = function(){ }
|
39
|
-
|
41
|
+
|
42
|
+
MockedXMLHttpRequest.prototype.abort = function(){
|
43
|
+
this.readyState = 4
|
44
|
+
this.responseText = '';
|
45
|
+
this.statusText = '';
|
46
|
+
this.status = 400;
|
47
|
+
this.onreadystatechange()
|
48
|
+
}
|
49
|
+
|
40
50
|
MockedXMLHttpRequest.prototype.setRequestHeader = function(k,v){
|
41
51
|
this.headers[k] = v;
|
42
52
|
}
|
53
|
+
|
43
54
|
MockedXMLHttpRequest.prototype.getResponseHeader = function(){
|
44
55
|
return 'text/plain; charset=utf-8';
|
45
56
|
}
|
57
|
+
|
46
58
|
MessageBus.xhrImplementation = MockedXMLHttpRequest
|
47
59
|
this.MockedXMLHttpRequest = MockedXMLHttpRequest
|
48
|
-
|
60
|
+
|
49
61
|
this.responseChunks = [
|
50
62
|
{channel: '/test', data: {password: 'MessageBusRocks!'}}
|
51
63
|
];
|
52
64
|
|
65
|
+
MessageBus.start();
|
66
|
+
});
|
67
|
+
|
68
|
+
afterEach(function(){
|
69
|
+
MessageBus.stop()
|
70
|
+
MessageBus.callbacks.splice(0, MessageBus.callbacks.length)
|
53
71
|
});
|
54
72
|
|
55
73
|
window.testMB = function(description, testFn, path, data){
|
@@ -88,10 +106,3 @@ window.testMB = function(description, testFn, path, data){
|
|
88
106
|
|
89
107
|
}
|
90
108
|
|
91
|
-
afterEach(function(){
|
92
|
-
MessageBus.stop()
|
93
|
-
if (MessageBus.longPoll){
|
94
|
-
MessageBus.longPoll.abort();
|
95
|
-
}
|
96
|
-
MessageBus.callbacks.splice(0, MessageBus.callbacks.length)
|
97
|
-
});
|
@@ -84,6 +84,12 @@ describe("Messagebus", function() {
|
|
84
84
|
window.MessageBus = mb;
|
85
85
|
});
|
86
86
|
|
87
|
+
it('respects minPollInterval setting with defaults', function(){
|
88
|
+
expect(MessageBus.minPollInterval).toEqual(100);
|
89
|
+
MessageBus.minPollInterval = 1000;
|
90
|
+
expect(MessageBus.minPollInterval).toEqual(1000);
|
91
|
+
});
|
92
|
+
|
87
93
|
testMB('sends using custom header', function(){
|
88
94
|
MessageBus.headers['X-MB-TEST-VALUE'] = '42';
|
89
95
|
this.perform(function(message, xhr){
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require 'minitest/hooks/default'
|
3
|
+
require 'message_bus'
|
4
|
+
require 'message_bus/distributed_cache'
|
5
|
+
|
6
|
+
describe MessageBus::DistributedCache do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
@bus = MessageBus::Instance.new
|
10
|
+
@bus.configure(backend: :memory)
|
11
|
+
@manager = MessageBus::DistributedCache::Manager.new(@bus)
|
12
|
+
end
|
13
|
+
|
14
|
+
after :all do
|
15
|
+
@bus.destroy
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache(name)
|
19
|
+
MessageBus::DistributedCache.new(name, manager: @manager)
|
20
|
+
end
|
21
|
+
|
22
|
+
let :cache_name do
|
23
|
+
SecureRandom.hex
|
24
|
+
end
|
25
|
+
|
26
|
+
before do
|
27
|
+
@cache1 = cache(cache_name)
|
28
|
+
@cache2 = cache(cache_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'supports arrays with hashes' do
|
32
|
+
|
33
|
+
c1 = cache("test1")
|
34
|
+
c2 = cache("test1")
|
35
|
+
|
36
|
+
c1["test"] = [{ test: :test }]
|
37
|
+
|
38
|
+
wait_for do
|
39
|
+
c2["test"] == [{ test: :test }]
|
40
|
+
end
|
41
|
+
|
42
|
+
expect(c2[:test]).must_equal([{ test: :test }])
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'allows us to store Set' do
|
46
|
+
c1 = cache("test1")
|
47
|
+
c2 = cache("test1")
|
48
|
+
|
49
|
+
set = Set.new
|
50
|
+
set << 1
|
51
|
+
set << "b"
|
52
|
+
set << 92803984
|
53
|
+
set << 93739739873973
|
54
|
+
|
55
|
+
c1["cats"] = set
|
56
|
+
|
57
|
+
wait_for do
|
58
|
+
c2["cats"] == set
|
59
|
+
end
|
60
|
+
|
61
|
+
expect(c2["cats"]).must_equal(set)
|
62
|
+
|
63
|
+
set << 5
|
64
|
+
|
65
|
+
c2["cats"] = set
|
66
|
+
|
67
|
+
wait_for do
|
68
|
+
c1["cats"] == set
|
69
|
+
end
|
70
|
+
|
71
|
+
expect(c1["cats"]).must_equal(set)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'does not leak state across caches' do
|
75
|
+
c2 = cache("test1")
|
76
|
+
c3 = cache("test1")
|
77
|
+
c2["hi"] = "hi"
|
78
|
+
wait_for do
|
79
|
+
c3["hi"] == "hi"
|
80
|
+
end
|
81
|
+
|
82
|
+
Thread.pass
|
83
|
+
assert_nil(@cache1["hi"])
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'allows coerces symbol keys to strings' do
|
88
|
+
@cache1[:key] = "test"
|
89
|
+
expect(@cache1["key"]).must_equal("test")
|
90
|
+
|
91
|
+
wait_for do
|
92
|
+
@cache2[:key] == "test"
|
93
|
+
end
|
94
|
+
expect(@cache2["key"]).must_equal("test")
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'sets other caches' do
|
98
|
+
@cache1["test"] = "world"
|
99
|
+
wait_for do
|
100
|
+
@cache2["test"] == "world"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'deletes from other caches' do
|
105
|
+
@cache1["foo"] = "bar"
|
106
|
+
|
107
|
+
wait_for do
|
108
|
+
@cache2["foo"] == "bar"
|
109
|
+
end
|
110
|
+
|
111
|
+
@cache1.delete("foo")
|
112
|
+
assert_nil(@cache1["foo"])
|
113
|
+
|
114
|
+
wait_for do
|
115
|
+
@cache2["foo"] == nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'clears cache on request' do
|
120
|
+
@cache1["foo"] = "bar"
|
121
|
+
|
122
|
+
wait_for do
|
123
|
+
@cache2["foo"] == "bar"
|
124
|
+
end
|
125
|
+
|
126
|
+
@cache1.clear
|
127
|
+
assert_nil(@cache1["foo"])
|
128
|
+
wait_for do
|
129
|
+
@cache2["boom"] == nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -13,7 +13,7 @@ describe MessageBus::TimerThread do
|
|
13
13
|
it "allows you to queue every jobs" do
|
14
14
|
i = 0
|
15
15
|
m = Mutex.new
|
16
|
-
every = @timer.every(0.001){m.synchronize{i += 1 if i < 3}}
|
16
|
+
every = @timer.every(0.001) { m.synchronize { i += 1 if i < 3 } }
|
17
17
|
# allow lots of time, cause in test mode stuff can be slow
|
18
18
|
wait_for(1000) do
|
19
19
|
m.synchronize do
|
@@ -27,7 +27,7 @@ describe MessageBus::TimerThread do
|
|
27
27
|
|
28
28
|
it "allows you to cancel timers" do
|
29
29
|
success = true
|
30
|
-
@timer.queue(0.005){success=false}.cancel
|
30
|
+
@timer.queue(0.005) { success = false }.cancel
|
31
31
|
sleep(0.006)
|
32
32
|
success.must_equal true
|
33
33
|
end
|
@@ -45,7 +45,7 @@ describe MessageBus::TimerThread do
|
|
45
45
|
4 == results.length
|
46
46
|
}
|
47
47
|
|
48
|
-
results.must_equal [0,1,2,3]
|
48
|
+
results.must_equal [0, 1, 2, 3]
|
49
49
|
end
|
50
50
|
|
51
51
|
it "should call the error callback if something goes wrong" do
|
@@ -159,6 +159,26 @@ describe MessageBus do
|
|
159
159
|
assert_nil @bus.last_message("/nothing")
|
160
160
|
end
|
161
161
|
|
162
|
+
describe "#publish" do
|
163
|
+
it "allows publishing to a explicit site" do
|
164
|
+
data, site_id, channel = nil
|
165
|
+
|
166
|
+
@bus.subscribe do |msg|
|
167
|
+
data = msg.data
|
168
|
+
site_id = msg.site_id
|
169
|
+
channel = msg.channel
|
170
|
+
end
|
171
|
+
|
172
|
+
@bus.publish("/chuck", "norris", site_id: "law-and-order")
|
173
|
+
|
174
|
+
wait_for(2000) { data }
|
175
|
+
|
176
|
+
data.must_equal 'norris'
|
177
|
+
site_id.must_equal 'law-and-order'
|
178
|
+
channel.must_equal '/chuck'
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
162
182
|
describe "global subscriptions" do
|
163
183
|
before do
|
164
184
|
seq = 0
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: message_bus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- lib/message_bus/client.rb
|
96
96
|
- lib/message_bus/connection_manager.rb
|
97
97
|
- lib/message_bus/diagnostics.rb
|
98
|
+
- lib/message_bus/distributed_cache.rb
|
98
99
|
- lib/message_bus/em_ext.rb
|
99
100
|
- lib/message_bus/message.rb
|
100
101
|
- lib/message_bus/rack/diagnostics.rb
|
@@ -114,6 +115,7 @@ files:
|
|
114
115
|
- spec/lib/message_bus/backends/redis_spec.rb
|
115
116
|
- spec/lib/message_bus/client_spec.rb
|
116
117
|
- spec/lib/message_bus/connection_manager_spec.rb
|
118
|
+
- spec/lib/message_bus/distributed_cache_spec.rb
|
117
119
|
- spec/lib/message_bus/multi_process_spec.rb
|
118
120
|
- spec/lib/message_bus/rack/middleware_spec.rb
|
119
121
|
- spec/lib/message_bus/timer_thread_spec.rb
|
@@ -157,6 +159,7 @@ test_files:
|
|
157
159
|
- spec/lib/message_bus/backends/redis_spec.rb
|
158
160
|
- spec/lib/message_bus/client_spec.rb
|
159
161
|
- spec/lib/message_bus/connection_manager_spec.rb
|
162
|
+
- spec/lib/message_bus/distributed_cache_spec.rb
|
160
163
|
- spec/lib/message_bus/multi_process_spec.rb
|
161
164
|
- spec/lib/message_bus/rack/middleware_spec.rb
|
162
165
|
- spec/lib/message_bus/timer_thread_spec.rb
|