message_bus 2.0.0.beta.2 → 2.0.0.beta.3
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/.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
|