message_bus 2.0.0.beta.2 → 2.0.0.beta.3
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/.travis.yml +10 -2
- data/CHANGELOG +10 -0
- data/Gemfile +1 -6
- data/README.md +43 -16
- data/Rakefile +18 -4
- data/lib/message_bus.rb +64 -47
- data/lib/message_bus/backends/postgres.rb +396 -0
- data/lib/message_bus/{redis/reliable_pub_sub.rb → backends/redis.rb} +1 -0
- data/lib/message_bus/rack/middleware.rb +14 -5
- data/lib/message_bus/version.rb +1 -1
- data/message_bus.gemspec +3 -1
- data/spec/lib/message_bus/assets/asset_encoding_spec.rb +4 -4
- data/spec/lib/message_bus/backends/postgres_spec.rb +208 -0
- data/spec/lib/message_bus/{redis/reliable_pub_sub_spec.rb → backends/redis_spec.rb} +25 -23
- data/spec/lib/message_bus/client_spec.rb +28 -27
- data/spec/lib/message_bus/connection_manager_spec.rb +22 -24
- data/spec/lib/message_bus/multi_process_spec.rb +54 -27
- data/spec/lib/message_bus/rack/middleware_spec.rb +81 -38
- data/spec/lib/message_bus/timer_thread_spec.rb +6 -6
- data/spec/lib/message_bus_spec.rb +36 -35
- data/spec/spec_helper.rb +16 -21
- metadata +24 -7
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative '../../spec_helper'
|
2
2
|
require 'message_bus'
|
3
3
|
|
4
4
|
class FakeAsync
|
@@ -38,11 +38,11 @@ describe MessageBus::ConnectionManager do
|
|
38
38
|
m = MessageBus::Message.new(1,1,"test","data")
|
39
39
|
m.site_id = 10
|
40
40
|
@manager.notify_clients(m)
|
41
|
-
@client.cleanup_timer.cancelled.
|
41
|
+
@client.cleanup_timer.cancelled.must_equal true
|
42
42
|
end
|
43
43
|
|
44
44
|
it "should be able to lookup an identical client" do
|
45
|
-
@manager.lookup_client(@client.client_id).
|
45
|
+
@manager.lookup_client(@client.client_id).must_equal @client
|
46
46
|
end
|
47
47
|
|
48
48
|
it "should be subscribed to a channel" do
|
@@ -53,14 +53,14 @@ describe MessageBus::ConnectionManager do
|
|
53
53
|
m = MessageBus::Message.new(1,1,"test","data")
|
54
54
|
m.site_id = 9
|
55
55
|
@manager.notify_clients(m)
|
56
|
-
@resp.sent.
|
56
|
+
@resp.sent.must_equal nil
|
57
57
|
end
|
58
58
|
|
59
59
|
it "should notify clients on the correct site" do
|
60
60
|
m = MessageBus::Message.new(1,1,"test","data")
|
61
61
|
m.site_id = 10
|
62
62
|
@manager.notify_clients(m)
|
63
|
-
@resp.sent.
|
63
|
+
@resp.sent.wont_equal nil
|
64
64
|
end
|
65
65
|
|
66
66
|
it "should strip site id and user id from the payload delivered" do
|
@@ -69,8 +69,8 @@ describe MessageBus::ConnectionManager do
|
|
69
69
|
m.site_id = 10
|
70
70
|
@manager.notify_clients(m)
|
71
71
|
parsed = JSON.parse(@resp.sent)
|
72
|
-
parsed[0]["site_id"].
|
73
|
-
parsed[0]["user_id"].
|
72
|
+
parsed[0]["site_id"].must_equal nil
|
73
|
+
parsed[0]["user_id"].must_equal nil
|
74
74
|
end
|
75
75
|
|
76
76
|
it "should not deliver unselected" do
|
@@ -78,7 +78,7 @@ describe MessageBus::ConnectionManager do
|
|
78
78
|
m.user_ids = [5]
|
79
79
|
m.site_id = 10
|
80
80
|
@manager.notify_clients(m)
|
81
|
-
@resp.sent.
|
81
|
+
@resp.sent.must_equal nil
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -93,7 +93,7 @@ describe MessageBus::ConnectionManager, "notifying and subscribing concurrently"
|
|
93
93
|
manager.add_client(client2)
|
94
94
|
manager.add_client(client1)
|
95
95
|
|
96
|
-
manager.lookup_client("a").
|
96
|
+
manager.lookup_client("a").must_equal client2
|
97
97
|
end
|
98
98
|
|
99
99
|
it "is thread-safe" do
|
@@ -102,29 +102,27 @@ describe MessageBus::ConnectionManager, "notifying and subscribing concurrently"
|
|
102
102
|
|
103
103
|
client_threads = 10.times.map do |id|
|
104
104
|
Thread.new do
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
}.not_to raise_error
|
105
|
+
@client = MessageBus::Client.new(client_id: "xyz_#{id}", site_id: 10)
|
106
|
+
@resp = FakeAsync.new
|
107
|
+
@client.async_response = @resp
|
108
|
+
@client.subscribe("test", -1)
|
109
|
+
@manager.add_client(@client)
|
110
|
+
@client.cleanup_timer = FakeTimer.new
|
111
|
+
1
|
113
112
|
end
|
114
113
|
end
|
115
114
|
|
116
115
|
subscriber_threads = 10.times.map do |id|
|
117
116
|
Thread.new do
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
}.not_to raise_error
|
117
|
+
m = MessageBus::Message.new(1,id,"test","data_#{id}")
|
118
|
+
m.site_id = 10
|
119
|
+
@manager.notify_clients(m)
|
120
|
+
1
|
123
121
|
end
|
124
122
|
end
|
125
123
|
|
126
|
-
client_threads.each(&:join)
|
127
|
-
subscriber_threads.each(&:join)
|
124
|
+
client_threads.each(&:join).map(&:value).must_equal([1] * 10)
|
125
|
+
subscriber_threads.each(&:join).map(&:value).must_equal([1] * 10)
|
128
126
|
end
|
129
127
|
|
130
128
|
end
|
@@ -1,10 +1,17 @@
|
|
1
|
-
|
1
|
+
require_relative '../../spec_helper'
|
2
2
|
require 'message_bus'
|
3
3
|
|
4
|
-
describe
|
4
|
+
describe PUB_SUB_CLASS do
|
5
|
+
def self.error!
|
6
|
+
@error = true
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.error?
|
10
|
+
defined?(@error)
|
11
|
+
end
|
5
12
|
|
6
13
|
def new_bus
|
7
|
-
|
14
|
+
PUB_SUB_CLASS.new(MESSAGE_BUS_CONFIG.merge(:db => 10))
|
8
15
|
end
|
9
16
|
|
10
17
|
def work_it
|
@@ -13,8 +20,10 @@ describe MessageBus::Redis::ReliablePubSub do
|
|
13
20
|
$stderr.reopen("/dev/null", "w")
|
14
21
|
# subscribe blocks, so we need a new bus to transmit
|
15
22
|
new_bus.subscribe("/echo", 0) do |msg|
|
16
|
-
bus.publish("/response", Process.pid.to_s)
|
23
|
+
bus.publish("/response", "#{msg.data}-#{Process.pid.to_s}")
|
17
24
|
end
|
25
|
+
ensure
|
26
|
+
exit!(0)
|
18
27
|
end
|
19
28
|
|
20
29
|
def spawn_child
|
@@ -26,33 +35,51 @@ describe MessageBus::Redis::ReliablePubSub do
|
|
26
35
|
end
|
27
36
|
end
|
28
37
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
+
n = ENV['MULTI_PROCESS_TIMES'].to_i
|
39
|
+
n = 1 if n < 1
|
40
|
+
n.times do
|
41
|
+
it 'gets every response from child processes' do
|
42
|
+
skip("previous error") if self.class.error?
|
43
|
+
GC.start
|
44
|
+
new_bus.reset!
|
45
|
+
begin
|
46
|
+
pids = (1..10).map{spawn_child}
|
47
|
+
expected_responses = pids.map{|x| (0...10).map{|i| "#{i}-#{x}"}}.flatten
|
48
|
+
unexpected_responses = []
|
49
|
+
bus = new_bus
|
50
|
+
t = Thread.new do
|
51
|
+
bus.subscribe("/response", 0) do |msg|
|
52
|
+
if expected_responses.include?(msg.data)
|
53
|
+
expected_responses.delete(msg.data)
|
54
|
+
else
|
55
|
+
unexpected_responses << msg.data
|
56
|
+
end
|
57
|
+
end
|
38
58
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
59
|
+
10.times{|i| bus.publish("/echo", i.to_s)}
|
60
|
+
wait_for 4000 do
|
61
|
+
expected_responses.empty?
|
62
|
+
end
|
63
|
+
bus.global_unsubscribe
|
64
|
+
t.join
|
44
65
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
66
|
+
expected_responses.must_be :empty?
|
67
|
+
unexpected_responses.must_be :empty?
|
68
|
+
rescue Exception
|
69
|
+
self.class.error!
|
70
|
+
raise
|
71
|
+
ensure
|
72
|
+
if pids
|
73
|
+
pids.each do |pid|
|
74
|
+
begin
|
75
|
+
Process.kill("KILL", pid)
|
76
|
+
rescue SystemCallError
|
77
|
+
end
|
78
|
+
Process.wait(pid)
|
79
|
+
end
|
53
80
|
end
|
81
|
+
bus.global_unsubscribe
|
54
82
|
end
|
55
|
-
bus.global_unsubscribe
|
56
83
|
end
|
57
84
|
end
|
58
85
|
end
|
@@ -1,18 +1,22 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
require_relative '../../../spec_helper'
|
4
4
|
require 'message_bus'
|
5
5
|
require 'rack/test'
|
6
6
|
|
7
7
|
describe MessageBus::Rack::Middleware do
|
8
8
|
include Rack::Test::Methods
|
9
|
+
let(:extra_middleware){nil}
|
9
10
|
|
10
11
|
before do
|
11
12
|
bus = @bus = MessageBus::Instance.new
|
13
|
+
@bus.redis_config = MESSAGE_BUS_CONFIG
|
12
14
|
@bus.long_polling_enabled = false
|
13
15
|
|
16
|
+
e_m = extra_middleware
|
14
17
|
builder = Rack::Builder.new {
|
15
18
|
use FakeAsyncMiddleware, :message_bus => bus
|
19
|
+
use e_m if e_m
|
16
20
|
use MessageBus::Rack::Middleware, :message_bus => bus
|
17
21
|
run lambda {|env| [500, {'Content-Type' => 'text/html'}, 'should not be called' ]}
|
18
22
|
}
|
@@ -31,24 +35,26 @@ describe MessageBus::Rack::Middleware do
|
|
31
35
|
@async_middleware
|
32
36
|
end
|
33
37
|
|
34
|
-
|
38
|
+
module LongPolling
|
39
|
+
extend Minitest::Spec::DSL
|
40
|
+
|
35
41
|
before do
|
36
42
|
@bus.long_polling_enabled = true
|
37
43
|
end
|
38
44
|
|
39
45
|
it "should respond right away if dlp=t" do
|
40
46
|
post "/message-bus/ABC?dlp=t", '/foo1' => 0
|
41
|
-
@async_middleware.in_async?.
|
42
|
-
last_response.
|
47
|
+
@async_middleware.in_async?.must_equal false
|
48
|
+
last_response.ok?.must_equal true
|
43
49
|
end
|
44
50
|
|
45
51
|
it "should respond right away to long polls that are polling on -1 with the last_id" do
|
46
52
|
post "/message-bus/ABC", '/foo' => -1
|
47
|
-
last_response.
|
53
|
+
last_response.ok?.must_equal true
|
48
54
|
parsed = JSON.parse(last_response.body)
|
49
|
-
parsed.length.
|
50
|
-
parsed[0]["channel"].
|
51
|
-
parsed[0]["data"]["/foo"].
|
55
|
+
parsed.length.must_equal 1
|
56
|
+
parsed[0]["channel"].must_equal "/__status"
|
57
|
+
parsed[0]["data"]["/foo"].must_equal @bus.last_id("/foo")
|
52
58
|
end
|
53
59
|
|
54
60
|
it "should respond to long polls when data is available" do
|
@@ -66,12 +72,12 @@ describe MessageBus::Rack::Middleware do
|
|
66
72
|
|
67
73
|
post "/message-bus/ABC", '/foo' => nil
|
68
74
|
|
69
|
-
last_response.
|
75
|
+
last_response.ok?.must_equal true
|
70
76
|
parsed = JSON.parse(last_response.body)
|
71
|
-
parsed.length.
|
72
|
-
parsed[0]["data"].
|
77
|
+
parsed.length.must_equal 1
|
78
|
+
parsed[0]["data"].must_equal "םוֹלשָׁ"
|
73
79
|
|
74
|
-
last_response.headers["FOO"].
|
80
|
+
last_response.headers["FOO"].must_equal "BAR"
|
75
81
|
end
|
76
82
|
|
77
83
|
it "should timeout within its alloted slot" do
|
@@ -80,7 +86,7 @@ describe MessageBus::Rack::Middleware do
|
|
80
86
|
s = Time.now.to_f * 1000
|
81
87
|
post "/message-bus/ABC", '/foo' => nil
|
82
88
|
# allow for some jitter
|
83
|
-
(Time.now.to_f * 1000 - s).
|
89
|
+
(Time.now.to_f * 1000 - s).must_be :<, 100
|
84
90
|
ensure
|
85
91
|
@bus.long_polling_interval = 5000
|
86
92
|
end
|
@@ -91,7 +97,8 @@ describe MessageBus::Rack::Middleware do
|
|
91
97
|
before do
|
92
98
|
@async_middleware.simulate_thin_async
|
93
99
|
end
|
94
|
-
|
100
|
+
|
101
|
+
include LongPolling
|
95
102
|
end
|
96
103
|
|
97
104
|
describe "hijack" do
|
@@ -99,27 +106,28 @@ describe MessageBus::Rack::Middleware do
|
|
99
106
|
@async_middleware.simulate_hijack
|
100
107
|
@bus.rack_hijack_enabled = true
|
101
108
|
end
|
102
|
-
|
109
|
+
|
110
|
+
include LongPolling
|
103
111
|
end
|
104
112
|
|
105
113
|
describe "diagnostics" do
|
106
114
|
|
107
115
|
it "should return a 403 if a user attempts to get at the _diagnostics path" do
|
108
116
|
get "/message-bus/_diagnostics"
|
109
|
-
last_response.status.
|
117
|
+
last_response.status.must_equal 403
|
110
118
|
end
|
111
119
|
|
112
120
|
it "should get a 200 with html for an authorized user" do
|
113
|
-
@bus.
|
121
|
+
def @bus.is_admin_lookup; proc{|_| true} end
|
114
122
|
get "/message-bus/_diagnostics"
|
115
|
-
last_response.status.
|
123
|
+
last_response.status.must_equal 200
|
116
124
|
end
|
117
125
|
|
118
126
|
it "should get the script it asks for" do
|
119
|
-
@bus.
|
127
|
+
def @bus.is_admin_lookup; proc{|_| true} end
|
120
128
|
get "/message-bus/_diagnostics/assets/message-bus.js"
|
121
|
-
last_response.status.
|
122
|
-
last_response.content_type.
|
129
|
+
last_response.status.must_equal 200
|
130
|
+
last_response.content_type.must_equal "text/javascript;"
|
123
131
|
end
|
124
132
|
|
125
133
|
end
|
@@ -142,7 +150,7 @@ describe MessageBus::Rack::Middleware do
|
|
142
150
|
'/bar' => nil
|
143
151
|
}
|
144
152
|
|
145
|
-
last_response.headers["FOO"].
|
153
|
+
last_response.headers["FOO"].must_equal "BAR"
|
146
154
|
end
|
147
155
|
|
148
156
|
it "should respond with a 200 to a subscribe" do
|
@@ -153,7 +161,7 @@ describe MessageBus::Rack::Middleware do
|
|
153
161
|
'/foo' => nil,
|
154
162
|
'/bar' => nil
|
155
163
|
}
|
156
|
-
last_response.
|
164
|
+
last_response.ok?.must_equal true
|
157
165
|
end
|
158
166
|
|
159
167
|
it "should correctly understand that -1 means stuff from now onwards" do
|
@@ -163,11 +171,11 @@ describe MessageBus::Rack::Middleware do
|
|
163
171
|
post "/message-bus/ABCD", {
|
164
172
|
'/foo' => -1
|
165
173
|
}
|
166
|
-
last_response.
|
174
|
+
last_response.ok?.must_equal true
|
167
175
|
parsed = JSON.parse(last_response.body)
|
168
|
-
parsed.length.
|
169
|
-
parsed[0]["channel"].
|
170
|
-
parsed[0]["data"]["/foo"].
|
176
|
+
parsed.length.must_equal 1
|
177
|
+
parsed[0]["channel"].must_equal "/__status"
|
178
|
+
parsed[0]["data"]["/foo"].must_equal@bus.last_id("/foo")
|
171
179
|
|
172
180
|
end
|
173
181
|
|
@@ -184,9 +192,9 @@ describe MessageBus::Rack::Middleware do
|
|
184
192
|
}
|
185
193
|
|
186
194
|
parsed = JSON.parse(last_response.body)
|
187
|
-
parsed.length.
|
188
|
-
parsed[0]["data"].
|
189
|
-
parsed[1]["data"].
|
195
|
+
parsed.length.must_equal 2
|
196
|
+
parsed[0]["data"].must_equal "barbs"
|
197
|
+
parsed[1]["data"].must_equal "borbs"
|
190
198
|
end
|
191
199
|
|
192
200
|
it "should have no cross talk" do
|
@@ -205,7 +213,7 @@ describe MessageBus::Rack::Middleware do
|
|
205
213
|
}
|
206
214
|
|
207
215
|
parsed = JSON.parse(last_response.body)
|
208
|
-
parsed.length.
|
216
|
+
parsed.length.must_equal 0
|
209
217
|
|
210
218
|
end
|
211
219
|
|
@@ -223,7 +231,7 @@ describe MessageBus::Rack::Middleware do
|
|
223
231
|
}
|
224
232
|
|
225
233
|
parsed = JSON.parse(last_response.body)
|
226
|
-
parsed.length.
|
234
|
+
parsed.length.must_equal 1
|
227
235
|
end
|
228
236
|
|
229
237
|
it "should not get consumed messages" do
|
@@ -236,7 +244,7 @@ describe MessageBus::Rack::Middleware do
|
|
236
244
|
}
|
237
245
|
|
238
246
|
parsed = JSON.parse(last_response.body)
|
239
|
-
parsed.length.
|
247
|
+
parsed.length.must_equal 0
|
240
248
|
end
|
241
249
|
|
242
250
|
it "should filter by user correctly" do
|
@@ -251,7 +259,7 @@ describe MessageBus::Rack::Middleware do
|
|
251
259
|
}
|
252
260
|
|
253
261
|
parsed = JSON.parse(last_response.body)
|
254
|
-
parsed.length.
|
262
|
+
parsed.length.must_equal 0
|
255
263
|
|
256
264
|
@bus.user_id_lookup do |env|
|
257
265
|
1
|
@@ -262,7 +270,7 @@ describe MessageBus::Rack::Middleware do
|
|
262
270
|
}
|
263
271
|
|
264
272
|
parsed = JSON.parse(last_response.body)
|
265
|
-
parsed.length.
|
273
|
+
parsed.length.must_equal 1
|
266
274
|
end
|
267
275
|
|
268
276
|
it "should filter by group correctly" do
|
@@ -277,7 +285,7 @@ describe MessageBus::Rack::Middleware do
|
|
277
285
|
}
|
278
286
|
|
279
287
|
parsed = JSON.parse(last_response.body)
|
280
|
-
parsed.length.
|
288
|
+
parsed.length.must_equal 0
|
281
289
|
|
282
290
|
@bus.group_ids_lookup do |env|
|
283
291
|
[1,7,4,100]
|
@@ -288,8 +296,43 @@ describe MessageBus::Rack::Middleware do
|
|
288
296
|
}
|
289
297
|
|
290
298
|
parsed = JSON.parse(last_response.body)
|
291
|
-
parsed.length.
|
299
|
+
parsed.length.must_equal 1
|
292
300
|
end
|
293
|
-
end
|
294
301
|
|
302
|
+
describe "messagebus.channels env support" do
|
303
|
+
let(:extra_middleware) do
|
304
|
+
Class.new do
|
305
|
+
attr_reader :app
|
306
|
+
|
307
|
+
def initialize(app)
|
308
|
+
@app = app
|
309
|
+
end
|
310
|
+
|
311
|
+
def call(env)
|
312
|
+
@app.call(env.merge('message_bus.channels'=>{'/foo'=>0}))
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
it "should respect messagebus.channels in the environment to force channels" do
|
318
|
+
@message_bus_middleware = @async_middleware.app.app
|
319
|
+
foo_id = @bus.publish("/foo", "testfoo")
|
320
|
+
bar_id = @bus.publish("/bar", "testbar")
|
321
|
+
|
322
|
+
post "/message-bus/ABCD", {
|
323
|
+
'/foo' => foo_id - 1
|
324
|
+
}
|
325
|
+
|
326
|
+
parsed = JSON.parse(last_response.body)
|
327
|
+
parsed.first['data'].must_equal 'testfoo'
|
328
|
+
|
329
|
+
post "/message-bus/ABCD", {
|
330
|
+
'/bar' => bar_id - 1
|
331
|
+
}
|
332
|
+
|
333
|
+
parsed = JSON.parse(last_response.body)
|
334
|
+
parsed.first['data'].must_equal 'testfoo'
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
295
338
|
end
|