message_bus 1.0.16 → 1.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.
Potentially problematic release.
This version of message_bus might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG +7 -0
- data/README.md +34 -0
- data/assets/message-bus.js +36 -18
- data/lib/message_bus/client.rb +11 -1
- data/lib/message_bus/connection_manager.rb +81 -74
- data/lib/message_bus/rack/middleware.rb +5 -1
- data/lib/message_bus/version.rb +1 -1
- data/spec/lib/connection_manager_spec.rb +12 -0
- data/vendor/assets/javascripts/message-bus.js +36 -18
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7add5c68e797a080452032f17f17930bd2d28092
|
4
|
+
data.tar.gz: 15b55cad8b5dc0599d92fb88c74a83271703f46a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3385147eb91410e0606e95d32183f017cf218b86d2c51f2cfed7fd8ec7b15394c64fa6b64bcc9e3862e61781030b7c8f4d8fbd640f4c3e698dc28e2dd784e3ed
|
7
|
+
data.tar.gz: afcd0a5014c1c1f9ae229565d7dc7574f76eb1eecf544066acbed7b47db9d134b656a4bbc772ecf7285928f0e74b16a8c2e0f26cffa31aa252b55a86120066ef
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
07-12-2015
|
2
|
+
|
3
|
+
- Version 1.1.0
|
4
|
+
- Fix: keep track of client sequence on server, abandon old subscribes
|
5
|
+
- Fix: rare concurrency issue when subscribing concurrently
|
6
|
+
- Fature: remove most jQuery dependency from message-bus.js
|
7
|
+
|
1
8
|
09-07-2015
|
2
9
|
- Version 1.0.16
|
3
10
|
- Fix: correct edge cases around keepalive checks on bus
|
data/README.md
CHANGED
@@ -110,6 +110,40 @@ MessageBus.redis_config = { url: "redis://:p4ssw0rd@10.0.1.1:6380/15" }
|
|
110
110
|
```
|
111
111
|
The redis client message_bus uses is [redis-rb](https://github.com/redis/redis-rb), so you can visit it's repo to see what options you can configure.
|
112
112
|
|
113
|
+
### Forking/threading app servers
|
114
|
+
|
115
|
+
If you're using a forking or threading app server and you're not getting immediate updates from published messages, you might need to reconnect Redis in your app server config:
|
116
|
+
|
117
|
+
#### Passenger
|
118
|
+
```ruby
|
119
|
+
# Rails: config/application.rb or config.ru
|
120
|
+
if defined?(PhusionPassenger)
|
121
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
122
|
+
if forked
|
123
|
+
# We're in smart spawning mode.
|
124
|
+
MessageBus.after_fork
|
125
|
+
else
|
126
|
+
# We're in conservative spawning mode. We don't need to do anything.
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
#### Puma
|
133
|
+
```ruby
|
134
|
+
# path/to/your/config/puma.rb
|
135
|
+
on_worker_boot do
|
136
|
+
MessageBus.after_fork
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
#### Unicorn
|
141
|
+
```ruby
|
142
|
+
# path/to/your/config/unicorn.rb
|
143
|
+
after_fork do |server, worker|
|
144
|
+
MessageBus.after_fork
|
145
|
+
end
|
146
|
+
```
|
113
147
|
|
114
148
|
## Similar projects
|
115
149
|
|
data/assets/message-bus.js
CHANGED
@@ -33,12 +33,17 @@ window.MessageBus = (function() {
|
|
33
33
|
|
34
34
|
var hiddenProperty;
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
|
37
|
+
(function(){
|
38
|
+
var prefixes = ["","webkit","ms","moz","ms"];
|
39
|
+
for(var i=0; i<prefixes.length; i++) {
|
40
|
+
var prefix = prefixes[i];
|
41
|
+
var check = prefix + (prefix === "" ? "hidden" : "Hidden");
|
42
|
+
if(document[check] !== undefined ){
|
43
|
+
hiddenProperty = check;
|
44
|
+
}
|
40
45
|
}
|
41
|
-
});
|
46
|
+
})();
|
42
47
|
|
43
48
|
var isHidden = function() {
|
44
49
|
if (hiddenProperty !== undefined){
|
@@ -60,9 +65,12 @@ window.MessageBus = (function() {
|
|
60
65
|
var gotData = false;
|
61
66
|
if (!messages) return false; // server unexpectedly closed connection
|
62
67
|
|
63
|
-
|
68
|
+
|
69
|
+
for (var i=0; i<messages.length; i++) {
|
70
|
+
var message = messages[i];
|
64
71
|
gotData = true;
|
65
|
-
|
72
|
+
for (var j=0; j<callbacks.length; j++) {
|
73
|
+
var callback = callbacks[j];
|
66
74
|
if (callback.channel === message.channel) {
|
67
75
|
callback.last_id = message.message_id;
|
68
76
|
try {
|
@@ -79,8 +87,8 @@ window.MessageBus = (function() {
|
|
79
87
|
callback.last_id = message.data[callback.channel];
|
80
88
|
}
|
81
89
|
}
|
82
|
-
}
|
83
|
-
}
|
90
|
+
}
|
91
|
+
}
|
84
92
|
|
85
93
|
return gotData;
|
86
94
|
};
|
@@ -90,6 +98,7 @@ window.MessageBus = (function() {
|
|
90
98
|
var aborted = false;
|
91
99
|
lastAjax = new Date();
|
92
100
|
totalAjaxCalls += 1;
|
101
|
+
data.__seq = totalAjaxCalls;
|
93
102
|
|
94
103
|
return me.ajax({
|
95
104
|
url: me.baseUrl + "message-bus/" + me.clientId + "/poll?" + (!shouldLongPoll() || !me.enableLongPolling ? "dlp=t" : ""),
|
@@ -104,9 +113,9 @@ window.MessageBus = (function() {
|
|
104
113
|
failCount = 0;
|
105
114
|
if (paused) {
|
106
115
|
if (messages) {
|
107
|
-
|
108
|
-
later.push(messages);
|
109
|
-
}
|
116
|
+
for (var i=0; i<messages.length; i++) {
|
117
|
+
later.push(messages[i]);
|
118
|
+
}
|
110
119
|
}
|
111
120
|
} else {
|
112
121
|
gotData = processMessages(messages);
|
@@ -215,9 +224,9 @@ window.MessageBus = (function() {
|
|
215
224
|
}
|
216
225
|
|
217
226
|
data = {};
|
218
|
-
|
219
|
-
data[
|
220
|
-
}
|
227
|
+
for (var i=0;i<callbacks.length;i++) {
|
228
|
+
data[callbacks[i].channel] = callbacks[i].last_id;
|
229
|
+
}
|
221
230
|
|
222
231
|
me.longPoll = longPoller(poll,data);
|
223
232
|
};
|
@@ -265,7 +274,12 @@ window.MessageBus = (function() {
|
|
265
274
|
channel = channel.substr(0, channel.length - 1);
|
266
275
|
glob = true;
|
267
276
|
}
|
268
|
-
|
277
|
+
|
278
|
+
var filtered = [];
|
279
|
+
|
280
|
+
for (var i=0; i<callbacks.length; i++) {
|
281
|
+
|
282
|
+
callback = callbacks[i];
|
269
283
|
var keep;
|
270
284
|
|
271
285
|
if (glob) {
|
@@ -278,8 +292,12 @@ window.MessageBus = (function() {
|
|
278
292
|
keep = true;
|
279
293
|
}
|
280
294
|
|
281
|
-
|
282
|
-
|
295
|
+
if (keep) {
|
296
|
+
filtered.push(callback);
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
callbacks = filtered;
|
283
301
|
|
284
302
|
if (me.longPoll) {
|
285
303
|
return me.longPoll.abort();
|
data/lib/message_bus/client.rb
CHANGED
@@ -1,18 +1,28 @@
|
|
1
1
|
class MessageBus::Client
|
2
2
|
attr_accessor :client_id, :user_id, :group_ids, :connect_time,
|
3
3
|
:subscribed_sets, :site_id, :cleanup_timer,
|
4
|
-
:async_response, :io, :headers
|
4
|
+
:async_response, :io, :headers, :seq
|
5
5
|
|
6
6
|
def initialize(opts)
|
7
7
|
self.client_id = opts[:client_id]
|
8
8
|
self.user_id = opts[:user_id]
|
9
9
|
self.group_ids = opts[:group_ids] || []
|
10
10
|
self.site_id = opts[:site_id]
|
11
|
+
self.seq = opts[:seq].to_i
|
11
12
|
self.connect_time = Time.now
|
12
13
|
@bus = opts[:message_bus] || MessageBus
|
13
14
|
@subscriptions = {}
|
14
15
|
end
|
15
16
|
|
17
|
+
def cancel
|
18
|
+
if cleanup_timer
|
19
|
+
# concurrency may nil cleanup timer
|
20
|
+
cleanup_timer.cancel rescue nil
|
21
|
+
self.cleanup_timer = nil
|
22
|
+
end
|
23
|
+
ensure_closed!
|
24
|
+
end
|
25
|
+
|
16
26
|
def in_async?
|
17
27
|
@async_response || @io
|
18
28
|
end
|
@@ -2,117 +2,124 @@ require 'json' unless defined? ::JSON
|
|
2
2
|
|
3
3
|
class MessageBus::ConnectionManager
|
4
4
|
require 'monitor'
|
5
|
+
include MonitorMixin
|
5
6
|
|
6
|
-
|
7
|
-
include MonitorMixin
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
super
|
11
|
-
@set = Set.new
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.synchronize(methods)
|
15
|
-
methods.each do |method|
|
16
|
-
define_method method do |*args, &blk|
|
17
|
-
synchronize do
|
18
|
-
@set.send method,*args,&blk
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
synchronize(Set.new.methods - Object.new.methods)
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
def initialize(bus = nil)
|
7
|
+
def initialize(bus=nil)
|
29
8
|
@clients = {}
|
30
9
|
@subscriptions = {}
|
31
10
|
@bus = bus || MessageBus
|
11
|
+
mon_initialize
|
32
12
|
end
|
33
13
|
|
34
14
|
def notify_clients(msg)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
if
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
15
|
+
synchronize do
|
16
|
+
begin
|
17
|
+
site_subs = @subscriptions[msg.site_id]
|
18
|
+
subscription = site_subs[msg.channel] if site_subs
|
19
|
+
|
20
|
+
return unless subscription
|
21
|
+
|
22
|
+
around_filter = @bus.around_client_batch(msg.channel)
|
23
|
+
|
24
|
+
work = lambda do
|
25
|
+
subscription.each do |client_id|
|
26
|
+
client = @clients[client_id]
|
27
|
+
if client && client.allowed?(msg)
|
28
|
+
if copy = client.filter(msg)
|
29
|
+
begin
|
30
|
+
client << copy
|
31
|
+
rescue
|
32
|
+
# pipe may be broken, move on
|
33
|
+
end
|
34
|
+
# turns out you can delete from a set while itereating
|
35
|
+
remove_client(client)
|
52
36
|
end
|
53
|
-
# turns out you can delete from a set while itereating
|
54
|
-
remove_client(client)
|
55
37
|
end
|
56
38
|
end
|
57
39
|
end
|
58
|
-
end
|
59
40
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
41
|
+
if around_filter
|
42
|
+
user_ids = subscription.map do |s|
|
43
|
+
c = @clients[s]
|
44
|
+
c && c.user_id
|
45
|
+
end.compact
|
65
46
|
|
66
|
-
|
67
|
-
|
47
|
+
if user_ids && user_ids.length > 0
|
48
|
+
around_filter.call(msg, user_ids, work)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
work.call
|
68
52
|
end
|
69
|
-
else
|
70
|
-
work.call
|
71
|
-
end
|
72
53
|
|
73
|
-
|
74
|
-
|
54
|
+
rescue => e
|
55
|
+
MessageBus.logger.error "notify clients crash #{e} : #{e.backtrace}"
|
56
|
+
end
|
75
57
|
end
|
76
58
|
end
|
77
59
|
|
78
60
|
def add_client(client)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
61
|
+
synchronize do
|
62
|
+
existing = @clients[client.client_id]
|
63
|
+
if existing && existing.seq > client.seq
|
64
|
+
client.cancel
|
65
|
+
else
|
66
|
+
if existing
|
67
|
+
remove_client(existing)
|
68
|
+
existing.cancel
|
69
|
+
end
|
70
|
+
|
71
|
+
@clients[client.client_id] = client
|
72
|
+
@subscriptions[client.site_id] ||= {}
|
73
|
+
client.subscriptions.each do |k,v|
|
74
|
+
subscribe_client(client, k)
|
75
|
+
end
|
76
|
+
end
|
83
77
|
end
|
84
78
|
end
|
85
79
|
|
86
80
|
def remove_client(c)
|
87
|
-
|
88
|
-
|
89
|
-
|
81
|
+
synchronize do
|
82
|
+
@clients.delete c.client_id
|
83
|
+
@subscriptions[c.site_id].each do |k, set|
|
84
|
+
set.delete c.client_id
|
85
|
+
end
|
86
|
+
if c.cleanup_timer
|
87
|
+
# concurrency may cause this to fail
|
88
|
+
c.cleanup_timer.cancel rescue nil
|
89
|
+
end
|
90
90
|
end
|
91
|
-
c.cleanup_timer.cancel if c.cleanup_timer
|
92
91
|
end
|
93
92
|
|
94
93
|
def lookup_client(client_id)
|
95
|
-
|
94
|
+
synchronize do
|
95
|
+
@clients[client_id]
|
96
|
+
end
|
96
97
|
end
|
97
98
|
|
98
99
|
def subscribe_client(client,channel)
|
99
|
-
|
100
|
-
|
101
|
-
set
|
102
|
-
|
100
|
+
synchronize do
|
101
|
+
set = @subscriptions[client.site_id][channel]
|
102
|
+
unless set
|
103
|
+
set = Set.new
|
104
|
+
@subscriptions[client.site_id][channel] = set
|
105
|
+
end
|
106
|
+
set << client.client_id
|
103
107
|
end
|
104
|
-
set << client.client_id
|
105
108
|
end
|
106
109
|
|
107
110
|
def client_count
|
108
|
-
|
111
|
+
synchronize do
|
112
|
+
@clients.length
|
113
|
+
end
|
109
114
|
end
|
110
115
|
|
111
116
|
def stats
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
117
|
+
synchronize do
|
118
|
+
{
|
119
|
+
client_count: @clients.length,
|
120
|
+
subscriptions: @subscriptions
|
121
|
+
}
|
122
|
+
end
|
116
123
|
end
|
117
124
|
|
118
125
|
end
|
@@ -86,7 +86,11 @@ class MessageBus::Rack::Middleware
|
|
86
86
|
|
87
87
|
request = Rack::Request.new(env)
|
88
88
|
request.POST.each do |k,v|
|
89
|
-
|
89
|
+
if k == "__seq".freeze
|
90
|
+
client.seq = v.to_i
|
91
|
+
else
|
92
|
+
client.subscribe(k, v)
|
93
|
+
end
|
90
94
|
end
|
91
95
|
|
92
96
|
backlog = client.backlog
|
data/lib/message_bus/version.rb
CHANGED
@@ -84,6 +84,18 @@ end
|
|
84
84
|
|
85
85
|
describe MessageBus::ConnectionManager, "notifying and subscribing concurrently" do
|
86
86
|
|
87
|
+
it "does not subscribe incorrect clients" do
|
88
|
+
manager = MessageBus::ConnectionManager.new
|
89
|
+
|
90
|
+
client1 = MessageBus::Client.new(client_id: "a", seq: 1)
|
91
|
+
client2 = MessageBus::Client.new(client_id: "a", seq: 2)
|
92
|
+
|
93
|
+
manager.add_client(client2)
|
94
|
+
manager.add_client(client1)
|
95
|
+
|
96
|
+
manager.lookup_client("a").should == client2
|
97
|
+
end
|
98
|
+
|
87
99
|
it "is thread-safe" do
|
88
100
|
@bus = MessageBus
|
89
101
|
@manager = MessageBus::ConnectionManager.new(@bus)
|
@@ -33,12 +33,17 @@ window.MessageBus = (function() {
|
|
33
33
|
|
34
34
|
var hiddenProperty;
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
|
37
|
+
(function(){
|
38
|
+
var prefixes = ["","webkit","ms","moz","ms"];
|
39
|
+
for(var i=0; i<prefixes.length; i++) {
|
40
|
+
var prefix = prefixes[i];
|
41
|
+
var check = prefix + (prefix === "" ? "hidden" : "Hidden");
|
42
|
+
if(document[check] !== undefined ){
|
43
|
+
hiddenProperty = check;
|
44
|
+
}
|
40
45
|
}
|
41
|
-
});
|
46
|
+
})();
|
42
47
|
|
43
48
|
var isHidden = function() {
|
44
49
|
if (hiddenProperty !== undefined){
|
@@ -60,9 +65,12 @@ window.MessageBus = (function() {
|
|
60
65
|
var gotData = false;
|
61
66
|
if (!messages) return false; // server unexpectedly closed connection
|
62
67
|
|
63
|
-
|
68
|
+
|
69
|
+
for (var i=0; i<messages.length; i++) {
|
70
|
+
var message = messages[i];
|
64
71
|
gotData = true;
|
65
|
-
|
72
|
+
for (var j=0; j<callbacks.length; j++) {
|
73
|
+
var callback = callbacks[j];
|
66
74
|
if (callback.channel === message.channel) {
|
67
75
|
callback.last_id = message.message_id;
|
68
76
|
try {
|
@@ -79,8 +87,8 @@ window.MessageBus = (function() {
|
|
79
87
|
callback.last_id = message.data[callback.channel];
|
80
88
|
}
|
81
89
|
}
|
82
|
-
}
|
83
|
-
}
|
90
|
+
}
|
91
|
+
}
|
84
92
|
|
85
93
|
return gotData;
|
86
94
|
};
|
@@ -90,6 +98,7 @@ window.MessageBus = (function() {
|
|
90
98
|
var aborted = false;
|
91
99
|
lastAjax = new Date();
|
92
100
|
totalAjaxCalls += 1;
|
101
|
+
data.__seq = totalAjaxCalls;
|
93
102
|
|
94
103
|
return me.ajax({
|
95
104
|
url: me.baseUrl + "message-bus/" + me.clientId + "/poll?" + (!shouldLongPoll() || !me.enableLongPolling ? "dlp=t" : ""),
|
@@ -104,9 +113,9 @@ window.MessageBus = (function() {
|
|
104
113
|
failCount = 0;
|
105
114
|
if (paused) {
|
106
115
|
if (messages) {
|
107
|
-
|
108
|
-
later.push(messages);
|
109
|
-
}
|
116
|
+
for (var i=0; i<messages.length; i++) {
|
117
|
+
later.push(messages[i]);
|
118
|
+
}
|
110
119
|
}
|
111
120
|
} else {
|
112
121
|
gotData = processMessages(messages);
|
@@ -215,9 +224,9 @@ window.MessageBus = (function() {
|
|
215
224
|
}
|
216
225
|
|
217
226
|
data = {};
|
218
|
-
|
219
|
-
data[
|
220
|
-
}
|
227
|
+
for (var i=0;i<callbacks.length;i++) {
|
228
|
+
data[callbacks[i].channel] = callbacks[i].last_id;
|
229
|
+
}
|
221
230
|
|
222
231
|
me.longPoll = longPoller(poll,data);
|
223
232
|
};
|
@@ -265,7 +274,12 @@ window.MessageBus = (function() {
|
|
265
274
|
channel = channel.substr(0, channel.length - 1);
|
266
275
|
glob = true;
|
267
276
|
}
|
268
|
-
|
277
|
+
|
278
|
+
var filtered = [];
|
279
|
+
|
280
|
+
for (var i=0; i<callbacks.length; i++) {
|
281
|
+
|
282
|
+
callback = callbacks[i];
|
269
283
|
var keep;
|
270
284
|
|
271
285
|
if (glob) {
|
@@ -278,8 +292,12 @@ window.MessageBus = (function() {
|
|
278
292
|
keep = true;
|
279
293
|
}
|
280
294
|
|
281
|
-
|
282
|
-
|
295
|
+
if (keep) {
|
296
|
+
filtered.push(callback);
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
callbacks = filtered;
|
283
301
|
|
284
302
|
if (me.longPoll) {
|
285
303
|
return me.longPoll.abort();
|
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: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07
|
11
|
+
date: 2015-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -118,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
118
|
version: '0'
|
119
119
|
requirements: []
|
120
120
|
rubyforge_project:
|
121
|
-
rubygems_version: 2.4.5
|
121
|
+
rubygems_version: 2.4.5.1
|
122
122
|
signing_key:
|
123
123
|
specification_version: 4
|
124
124
|
summary: ''
|