brb 0.2.2 → 0.3.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.
- data/CHANGELOG.rdoc +6 -0
- data/MIT-LICENSE +0 -0
- data/README.rdoc +11 -11
- data/Rakefile +1 -1
- data/examples/simple_client.rb +8 -0
- data/examples/simple_core.rb +1 -0
- data/lib/brb.rb +0 -0
- data/lib/brb/exception.rb +9 -1
- data/lib/brb/request.rb +23 -8
- data/lib/brb/tunnel.rb +6 -2
- data/lib/brb/tunnel/shared.rb +57 -2
- data/spec/brb/brb_massive_usage_spec.rb +49 -18
- data/spec/brb/brb_service_spec.rb +1 -1
- data/spec/brb/brb_tunnel_spec.rb +55 -3
- data/spec/spec_helper.rb +5 -0
- metadata +4 -4
data/CHANGELOG.rdoc
CHANGED
data/MIT-LICENSE
CHANGED
File without changes
|
data/README.rdoc
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
= BrB - Easy and Fast distributed ruby
|
2
2
|
|
3
|
-
BrB is a simple, fully transparent and extremely fast interface for doing simple distributed
|
4
|
-
The
|
3
|
+
BrB is a simple, fully transparent and extremely fast interface for doing simple distributed Ruby.
|
4
|
+
The core of the architecture is provided by EventMachine (a fast and reliable IO event library).
|
5
5
|
|
6
|
-
BrB was
|
7
|
-
* Simple and fast message passing between distant
|
8
|
-
* Message passing with
|
9
|
-
* Being extremely fast in order to handle more than a few thousand messages per
|
6
|
+
BrB was built in order to achieve these 4 main goals :
|
7
|
+
* Simple and fast message passing between distant Ruby processes.
|
8
|
+
* Message passing with of return values when needed.
|
9
|
+
* Being extremely fast in order to handle more than a few thousand messages per second.
|
10
10
|
* Being completely transparent for developer.
|
11
11
|
|
12
|
-
The principle is simple and inspired from Drb (
|
13
|
-
A process
|
14
|
-
BrB handle all that part, it's fully transparent in the ruby code.
|
12
|
+
The principle is simple and inspired from Drb (standard distributed ruby library) :
|
13
|
+
A process exposes an object over the network and any ruby process (after having established a connection tunnel) can directly call a method on the exposed object. BrB handles that part, so it’s fully transparent in the Ruby code.
|
15
14
|
|
16
15
|
BrB only support message passing with Marshable dumpable object : String, symbol, Array, hash, Number, Object etc...
|
17
16
|
That mean you can not send file descriptor, Thread or another funky things like that :)
|
@@ -21,7 +20,8 @@ That mean you can not send file descriptor, Thread or another funky things like
|
|
21
20
|
* Unlimited objects exposed
|
22
21
|
* Processes can expose object and be client to exposed object too at the same time
|
23
22
|
* Do not wait for return by default : just do simple message passing
|
24
|
-
*
|
23
|
+
* Handle return values without blocking with the usage of a simple block
|
24
|
+
* Blocking wait for return if needed by simply adding <em>_block</em> at the end of the method name
|
25
25
|
* Transmission of Exception when blocking call
|
26
26
|
* Thread safe if used correctly with Event Machine
|
27
27
|
|
@@ -31,7 +31,7 @@ First of all, a process declare himself as a sever and expose any object on a gi
|
|
31
31
|
Then, any number of distant processes can create a <em>Tunnel</em> with that server and can expose an object in exchange too.
|
32
32
|
After connection are ready, just call method on the tunnel. It will just act like normal method calling on the exposed object !
|
33
33
|
|
34
|
-
== What BrB is
|
34
|
+
== What BrB is designed for ?
|
35
35
|
|
36
36
|
* Doing Simple message passing between ruby process.
|
37
37
|
* Connecting hundred of ruby process transparently.
|
data/Rakefile
CHANGED
data/examples/simple_client.rb
CHANGED
@@ -26,6 +26,14 @@ puts " >> Calling long call, and wait for response..."
|
|
26
26
|
r = core.simple_long_api_method_block
|
27
27
|
puts " > Api long response : #{r}"
|
28
28
|
|
29
|
+
## Calling method with a callback block for handling the return value
|
30
|
+
core.simple_api_method do |r|
|
31
|
+
puts " > Get the callback response : #{r}"
|
32
|
+
end
|
33
|
+
|
34
|
+
puts " >> Callback method has been called continue .."
|
35
|
+
sleep 2
|
36
|
+
|
29
37
|
core.stop_service
|
30
38
|
|
31
39
|
# Our job is over, close event machine :
|
data/examples/simple_core.rb
CHANGED
data/lib/brb.rb
CHANGED
File without changes
|
data/lib/brb/exception.rb
CHANGED
@@ -1 +1,9 @@
|
|
1
|
-
# Future BrB custom exceptions will come here
|
1
|
+
# Future BrB custom exceptions will come here
|
2
|
+
class BrBException < Exception
|
3
|
+
end
|
4
|
+
|
5
|
+
class BrBCallbackWithBlockingMethodException < BrBException
|
6
|
+
def initialize
|
7
|
+
super('Out request can not be blocking and have a callback at the same time !')
|
8
|
+
end
|
9
|
+
end
|
data/lib/brb/request.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
module BrB
|
2
2
|
module Request
|
3
|
+
|
4
|
+
MessageRequestCode = :s
|
5
|
+
CallbackRequestCode = :c
|
6
|
+
ReturnCode = :r
|
3
7
|
|
4
8
|
def is_brb_request_blocking?(meth)
|
5
9
|
if m = meth.to_s and m.rindex('_block') == (m.size - 6)
|
@@ -9,23 +13,34 @@ module BrB
|
|
9
13
|
end
|
10
14
|
|
11
15
|
# Execute a request on a distant object
|
12
|
-
def new_brb_out_request(meth, *args)
|
16
|
+
def new_brb_out_request(meth, *args, &blck)
|
13
17
|
Thread.current[:brb_nb_out] ||= 0
|
14
18
|
Thread.current[:brb_nb_out] += 1
|
15
19
|
|
16
|
-
|
20
|
+
raise BrBCallbackWithBlockingMethodException.new if is_brb_request_blocking?(meth) and block_given?
|
21
|
+
|
22
|
+
block = (is_brb_request_blocking?(meth) or block_given?) ? Thread.current.to_s.to_sym : nil
|
17
23
|
if block
|
18
24
|
args << block
|
19
25
|
args << Thread.current[:brb_nb_out]
|
20
26
|
end
|
27
|
+
|
28
|
+
if block_given?
|
29
|
+
# Simulate a method with _block in order to make BrB send the answer
|
30
|
+
meth = "#{meth}_block".to_sym
|
31
|
+
end
|
21
32
|
|
22
|
-
args.size > 0 ? brb_send([
|
33
|
+
args.size > 0 ? brb_send([MessageRequestCode, meth, args]) : brb_send([MessageRequestCode, meth])
|
34
|
+
|
35
|
+
if block_given?
|
36
|
+
# Declare the callback
|
37
|
+
declare_callback(block, Thread.current[:brb_nb_out], &blck)
|
38
|
+
|
39
|
+
elsif block # Block until the request return
|
23
40
|
|
24
|
-
# Block jusqu'au retour de la requete
|
25
|
-
if block
|
26
41
|
#TimeMonitor.instance.watch_thread!(@timeout_rcv_value || 45)
|
27
42
|
begin
|
28
|
-
r = recv(block, Thread.current[:brb_nb_out])
|
43
|
+
r = recv(block, Thread.current[:brb_nb_out], &blck)
|
29
44
|
rescue Exception => e
|
30
45
|
raise e
|
31
46
|
ensure
|
@@ -52,9 +67,9 @@ module BrB
|
|
52
67
|
thread = args.pop
|
53
68
|
begin
|
54
69
|
r = ((args.size > 0) ? @object.send(m, *args) : @object.send(m))
|
55
|
-
brb_send([
|
70
|
+
brb_send([ReturnCode, r, thread, idrequest])
|
56
71
|
rescue Exception => e
|
57
|
-
brb_send([
|
72
|
+
brb_send([ReturnCode, e, thread, idrequest])
|
58
73
|
tputs e.to_s
|
59
74
|
tputs e.backtrace.join("\n")
|
60
75
|
#raise e
|
data/lib/brb/tunnel.rb
CHANGED
@@ -33,6 +33,10 @@ module BrB
|
|
33
33
|
|
34
34
|
@queue = Queue.new
|
35
35
|
@buffer = ''
|
36
|
+
|
37
|
+
# Callbacks handling :
|
38
|
+
@callbacks = {}
|
39
|
+
@callbacks_mutex = Mutex.new
|
36
40
|
end
|
37
41
|
|
38
42
|
# EventMachine Callback, called after connection has been initialized
|
@@ -78,9 +82,9 @@ module BrB
|
|
78
82
|
end
|
79
83
|
|
80
84
|
# When no method is found on tunnel interface, create an brb out request
|
81
|
-
def method_missing(meth, *args)
|
85
|
+
def method_missing(meth, *args, &block)
|
82
86
|
return nil if !@active
|
83
|
-
new_brb_out_request(meth, *args)
|
87
|
+
new_brb_out_request(meth, *args, &block)
|
84
88
|
end
|
85
89
|
end
|
86
90
|
end
|
data/lib/brb/tunnel/shared.rb
CHANGED
@@ -54,13 +54,17 @@ module BrB
|
|
54
54
|
@buffer << data
|
55
55
|
|
56
56
|
while obj = load_request
|
57
|
-
if obj[0] ==
|
57
|
+
if obj[0] == BrB::Request::ReturnCode
|
58
|
+
|
59
|
+
# Return if we have a callback handling the return :
|
60
|
+
next if treat_callback_return(obj[1], obj[2], obj[3])
|
61
|
+
|
62
|
+
# No callback, so blocking thread is waiting :
|
58
63
|
@replock.lock
|
59
64
|
@responses[obj[2]] ||= Queue.new
|
60
65
|
@replock.unlock
|
61
66
|
@responses[obj[2]] << [obj[1], obj[3]]
|
62
67
|
else
|
63
|
-
|
64
68
|
@queue << obj
|
65
69
|
|
66
70
|
EM.defer do
|
@@ -79,6 +83,51 @@ module BrB
|
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
86
|
+
# Declare a new callback to call for a given request
|
87
|
+
# Thread safe code
|
88
|
+
def declare_callback(key, nb_out, &block)
|
89
|
+
@callbacks_mutex.lock
|
90
|
+
|
91
|
+
@callbacks[key] ||= {}
|
92
|
+
@callbacks[key][nb_out] = block
|
93
|
+
|
94
|
+
ensure
|
95
|
+
@callbacks_mutex.unlock
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return associated callback if present
|
99
|
+
# And if present, delete the associate callback from the table
|
100
|
+
# Thread safe code
|
101
|
+
def get_callback(key, nb_out)
|
102
|
+
@callbacks_mutex.lock
|
103
|
+
|
104
|
+
if @callbacks[key] and b = @callbacks[key].delete(nb_out)
|
105
|
+
return b
|
106
|
+
end
|
107
|
+
|
108
|
+
ensure
|
109
|
+
@callbacks_mutex.unlock
|
110
|
+
end
|
111
|
+
|
112
|
+
# Call a callback if present, return true if exists
|
113
|
+
# Non blocking action, use EM.defer
|
114
|
+
def treat_callback_return(ret, key, nb_out)
|
115
|
+
|
116
|
+
if b = get_callback(key, nb_out)
|
117
|
+
EM.defer do
|
118
|
+
# With arity, handle multiple block arguments or no arguments
|
119
|
+
b.arity == 1 ? b.call(ret) : (b.arity == 0 ? b.call : b.call(*ret))
|
120
|
+
end
|
121
|
+
|
122
|
+
# A callback has been found and called, return true
|
123
|
+
return true
|
124
|
+
end
|
125
|
+
|
126
|
+
# No callback, do nothing
|
127
|
+
return nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# Blocking method that wait on the @responses table an answer
|
82
131
|
def recv(key, nb_out)
|
83
132
|
begin
|
84
133
|
@replock.lock
|
@@ -86,6 +135,12 @@ module BrB
|
|
86
135
|
@replock.unlock
|
87
136
|
while rep = r.pop
|
88
137
|
if rep[1] == nb_out # On check ke c'est bien la réponse que l'on attend
|
138
|
+
|
139
|
+
# Call the callback
|
140
|
+
if block_given?
|
141
|
+
yield(rep[0])
|
142
|
+
end
|
143
|
+
|
89
144
|
return rep[0]
|
90
145
|
end
|
91
146
|
if rep[1] > nb_out
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
|
-
describe :
|
3
|
+
describe :brb_massive_usage do
|
4
4
|
before(:all) do
|
5
5
|
@brb = BrB::Service
|
6
6
|
@brb.stop_service
|
@@ -13,6 +13,10 @@ describe :brb_service do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
def random_client
|
17
|
+
@clients[rand(@clients.size)]
|
18
|
+
end
|
19
|
+
|
16
20
|
# Start the service
|
17
21
|
it "should the service be started" do
|
18
22
|
@clients.each do |cl|
|
@@ -23,26 +27,42 @@ describe :brb_service do
|
|
23
27
|
end
|
24
28
|
|
25
29
|
it "should works with massive simple messaging" do
|
26
|
-
|
27
|
-
|
30
|
+
nb_call_before = @brb_test.nb_call || 0
|
31
|
+
nb_call_to_do = 500
|
32
|
+
|
33
|
+
@clients.each do |cl|
|
34
|
+
nb_call_to_do.times do
|
35
|
+
cl.noarg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
sleep 5
|
40
|
+
# Wait a little in order to be sure all the stack is processed
|
41
|
+
@brb_test.last_call.should == :noarg
|
42
|
+
@brb_test.nb_call.should == (nb_call_to_do * @clients.size) + nb_call_before
|
43
|
+
end
|
28
44
|
|
29
|
-
|
30
|
-
|
31
|
-
|
45
|
+
it "should works with massive callbacks" do
|
46
|
+
block_called = 0
|
47
|
+
nb_callbacks = 1000
|
48
|
+
nb_callbacks.times do |i|
|
49
|
+
random_client.return_same_value(i) do |callback_return_value|
|
50
|
+
callback_return_value.should == i
|
51
|
+
block_called += 1
|
32
52
|
end
|
33
53
|
end
|
34
54
|
|
35
|
-
sleep
|
36
|
-
# Wait a little in order to be sure
|
37
|
-
@brb_test.last_call.should == :
|
38
|
-
|
55
|
+
sleep 2
|
56
|
+
# Wait a little in order to be sure the method is called
|
57
|
+
@brb_test.last_call.should == :return_same_value
|
58
|
+
block_called.should == nb_callbacks
|
39
59
|
end
|
40
|
-
|
41
|
-
it "should works with massive simple messaging including blocking messaging" do
|
60
|
+
|
61
|
+
it "should works with massive simple messaging including blocking messaging and callbacks" do
|
42
62
|
nb_call_before = @brb_test.nb_call || 0
|
43
63
|
nb_call_to_do = 500
|
44
64
|
nb_call_blocking_to_do = 50
|
45
|
-
|
65
|
+
|
46
66
|
t = Thread.new do
|
47
67
|
@clients.each do |cl|
|
48
68
|
nb_call_blocking_to_do.times do
|
@@ -51,17 +71,28 @@ describe :brb_service do
|
|
51
71
|
end
|
52
72
|
end
|
53
73
|
end
|
54
|
-
|
74
|
+
|
75
|
+
block_called = 0
|
76
|
+
nb_callbacks = 1000
|
77
|
+
nb_callbacks.times do |i|
|
78
|
+
random_client.return_same_value(i) do |callback_return_value|
|
79
|
+
callback_return_value.should == i
|
80
|
+
block_called += 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
55
85
|
@clients.each do |cl|
|
56
86
|
nb_call_to_do.times do
|
57
87
|
cl.noarg
|
58
88
|
end
|
59
89
|
end
|
60
|
-
|
61
|
-
sleep
|
90
|
+
|
91
|
+
sleep 5
|
92
|
+
block_called.should == nb_callbacks
|
62
93
|
t.join
|
63
94
|
# Wait a little in order to be sure all the stack is processed
|
64
|
-
@brb_test.nb_call.should == (nb_call_to_do * @clients.size + nb_call_blocking_to_do * @clients.size) + nb_call_before
|
95
|
+
@brb_test.nb_call.should == nb_callbacks + (nb_call_to_do * @clients.size + nb_call_blocking_to_do * @clients.size) + nb_call_before
|
65
96
|
end
|
66
97
|
|
67
98
|
# Finally, stop the service
|
data/spec/brb/brb_tunnel_spec.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
$last_unregistered = nil
|
4
4
|
$last_registered = nil
|
5
5
|
|
6
|
-
describe :
|
6
|
+
describe :brb_tunnel do
|
7
7
|
before(:all) do
|
8
8
|
@brb = BrB::Service
|
9
9
|
@brb.stop_service
|
@@ -90,6 +90,12 @@ describe :brb_service do
|
|
90
90
|
@client.one_arg_with_return_block(h).should == h
|
91
91
|
end
|
92
92
|
|
93
|
+
it "should correctly return multiple values" do
|
94
|
+
r1, r2 = @client.return_same_value_twice_block(:ret, :ret2)
|
95
|
+
r1.should == :ret
|
96
|
+
r2.should == :ret2
|
97
|
+
end
|
98
|
+
|
93
99
|
it "should dump to symbol undumpable value by using the proxy" do
|
94
100
|
@client.return_same_value_block(Thread.current).class.should == Symbol
|
95
101
|
@client.return_same_value_block(Thread.current).should == Thread.current.to_s.to_sym
|
@@ -103,6 +109,52 @@ describe :brb_service do
|
|
103
109
|
end
|
104
110
|
e.should be_a NameError
|
105
111
|
end
|
112
|
+
|
113
|
+
it "should use block as non blocking callback with return value" do
|
114
|
+
block_called = nil
|
115
|
+
@client.return_same_value(:arg) do |v|
|
116
|
+
v.should == :arg
|
117
|
+
block_called = true
|
118
|
+
end
|
119
|
+
sleep 0.2
|
120
|
+
# Wait a little in order to be sure the method is called
|
121
|
+
@brb_test.last_call.should == :return_same_value
|
122
|
+
block_called.should == true
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should correctly handle multiple values return with callbacks" do
|
126
|
+
block_called = nil
|
127
|
+
@client.return_same_value_twice(:ret, :ret2) do |r1, r2|
|
128
|
+
r1.should == :ret
|
129
|
+
r2.should == :ret2
|
130
|
+
block_called = true
|
131
|
+
end
|
132
|
+
sleep 0.2
|
133
|
+
# Wait a little in order to be sure the method is called
|
134
|
+
@brb_test.last_call.should == :return_same_value_twice
|
135
|
+
block_called.should == true
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should correctly handle no block args return with callbacks" do
|
139
|
+
block_called = nil
|
140
|
+
@client.return_same_value_twice(:ret, :ret2) do
|
141
|
+
block_called = true
|
142
|
+
end
|
143
|
+
sleep 0.2
|
144
|
+
# Wait a little in order to be sure the method is called
|
145
|
+
@brb_test.last_call.should == :return_same_value_twice
|
146
|
+
block_called.should == true
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should raise an exception when calling a blocking method with a callback" do
|
150
|
+
e = nil
|
151
|
+
begin
|
152
|
+
@client.return_same_value_block(:arg) do |v|
|
153
|
+
end
|
154
|
+
rescue Exception => e
|
155
|
+
end
|
156
|
+
e.should_not be_nil
|
157
|
+
end
|
106
158
|
|
107
159
|
# Finally, stop the service
|
108
160
|
it "should stop the service after usage" do
|
@@ -110,7 +162,7 @@ describe :brb_service do
|
|
110
162
|
@brb.uri.should be_nil
|
111
163
|
end
|
112
164
|
|
113
|
-
# Finally, stop the
|
165
|
+
# Finally, stop the client tunnel
|
114
166
|
it "should stop the tunnel after usage" do
|
115
167
|
@client.stop_service
|
116
168
|
@client.active?.should_not be_true
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Guillaume Luccisano
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-21 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|