tribe 0.3.0 → 0.3.1
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.
- checksums.yaml +4 -4
- data/README.md +11 -23
- data/Rakefile +1 -0
- data/lib/tribe.rb +1 -0
- data/lib/tribe/actable.rb +46 -33
- data/lib/tribe/actor_state.rb +1 -2
- data/lib/tribe/benchmark.rb +1 -0
- data/lib/tribe/benchmark/throughput.rb +74 -0
- data/lib/tribe/event.rb +15 -0
- data/lib/tribe/mailbox.rb +31 -11
- data/lib/tribe/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c286bf55aac4e1e85558bb56a3b49b2e84dcabc3
|
4
|
+
data.tar.gz: 1b0743b0e63db87ae6d0b0f81bacdd12c520ba46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a87e8def58e14e31d464783f10c30c421ef0c8343b1a0abb8bc9075c73208ba32e28cc3d195dda7ffee0daa80072e16d2397392e5997810a51f57b35058a2673
|
7
|
+
data.tar.gz: bb336feb72e68638ad1d92b63826724ea07eb768f377eaa53a01639ba801a876adf5f80588067246922490482572913f5fd0e8478fe5ecca4406e476db00b364
|
data/README.md
CHANGED
@@ -29,29 +29,27 @@ Actors are light-weight objects that use asynchronous message passing for commun
|
|
29
29
|
There are two types of methods that you create in your actors:
|
30
30
|
|
31
31
|
1. *Command handlers* are prefixed with "on_" and define the types of commands your actor will process.
|
32
|
-
2. *System handlers* are postfixed with "_handler" and are built into the actor system. These are used for exception, shutdown, and cleanup handling.
|
32
|
+
2. *System handlers* are postfixed with "_handler" and are built into the actor system. These are used for exception, shutdown, and cleanup handling.
|
33
33
|
|
34
34
|
To send a message you use the Actable#message! method and specify a command with an optional data parameter.
|
35
35
|
The return value will always be nil since messaging is asynchronous.
|
36
36
|
|
37
|
+
Note that the actors have a number of methods that end in ! (exclamation point or “bang”).
|
38
|
+
All of these mthods are asynchronous and designed to be thread safe.
|
39
|
+
More information on them will be provided throughout this readme.
|
40
|
+
|
37
41
|
# Create your custom actor class.
|
38
42
|
class MyActor < Tribe::Actor
|
39
43
|
private
|
40
|
-
def initialize(options = {})
|
41
|
-
super
|
42
|
-
end
|
43
|
-
|
44
44
|
def on_my_custom(event)
|
45
45
|
puts "Received a custom event (#{event.inspect})."
|
46
46
|
end
|
47
47
|
|
48
48
|
def exception_handler(e)
|
49
|
-
super
|
50
49
|
puts concat_e("MyActor (#{identifier}) died.", e)
|
51
50
|
end
|
52
51
|
|
53
52
|
def shutdown_handler(event)
|
54
|
-
super
|
55
53
|
puts "MyActor (#{identifier}) is shutting down. Put cleanup code here."
|
56
54
|
end
|
57
55
|
end
|
@@ -83,7 +81,6 @@ Actors that block for long periods of time should use a dedicated thread (:dedic
|
|
83
81
|
:logger => nil, # Ruby logger instance.
|
84
82
|
:dedicated => false, # If true, the actor runs with a worker pool that has one thread.
|
85
83
|
:pool => Workers.pool, # The workers pool used to execute events.
|
86
|
-
:mailbox => Tribe::Mailbox.new, # The mailbox used to receive events.
|
87
84
|
:registry => Tribe.registry, # The registry used to store a reference to the actor if it has a name.
|
88
85
|
:name => nil # The name of the actor (must be unique in the registry).
|
89
86
|
)
|
@@ -108,7 +105,6 @@ Both one-shot and periodic timers are provided.
|
|
108
105
|
private
|
109
106
|
def initialize(options = {})
|
110
107
|
super
|
111
|
-
|
112
108
|
timer(1, :timer, Time.now)
|
113
109
|
periodic_timer(1, :periodic_timer, Time.now)
|
114
110
|
end
|
@@ -151,7 +147,6 @@ No waiting for a result is involved and the actor will continue to process other
|
|
151
147
|
class ActorA < Tribe::Actor
|
152
148
|
private
|
153
149
|
def exception_handler(e)
|
154
|
-
super
|
155
150
|
puts concat_e("ActorA (#{identifier}) died.", e)
|
156
151
|
end
|
157
152
|
|
@@ -176,7 +171,6 @@ No waiting for a result is involved and the actor will continue to process other
|
|
176
171
|
|
177
172
|
class ActorB < Tribe::Actor
|
178
173
|
def exception_handler(e)
|
179
|
-
super
|
180
174
|
puts concat_e("ActorB (#{identifier}) died.", e)
|
181
175
|
end
|
182
176
|
|
@@ -210,7 +204,6 @@ The actor won't process any other events until the future has a result.
|
|
210
204
|
class ActorA < Tribe::Actor
|
211
205
|
private
|
212
206
|
def exception_handler(e)
|
213
|
-
super
|
214
207
|
puts concat_e("ActorA (#{identifier}) died.", e)
|
215
208
|
end
|
216
209
|
|
@@ -231,7 +224,6 @@ The actor won't process any other events until the future has a result.
|
|
231
224
|
|
232
225
|
class ActorB < Tribe::Actor
|
233
226
|
def exception_handler(e)
|
234
|
-
super
|
235
227
|
puts concat_e("ActorB (#{identifier}) died.", e)
|
236
228
|
end
|
237
229
|
|
@@ -264,7 +256,7 @@ Below you will find a summary of performance recommendations regarding the use o
|
|
264
256
|
## Forwarding
|
265
257
|
|
266
258
|
Messages and futures can be forwarded to other actors.
|
267
|
-
This lets you build
|
259
|
+
This lets you build routers that delegate work to other actors.
|
268
260
|
|
269
261
|
# Create your router class.
|
270
262
|
class MyRouter < Tribe::Actor
|
@@ -275,16 +267,14 @@ This lets you build actors that delegate work to other actors.
|
|
275
267
|
end
|
276
268
|
|
277
269
|
def on_process(event)
|
278
|
-
@processors[rand(100)]
|
270
|
+
forward!(@processors[rand(100)])
|
279
271
|
end
|
280
272
|
|
281
273
|
def exception_handler(e)
|
282
|
-
super
|
283
274
|
puts concat_e("MyRouter (#{identifier}) died.", e)
|
284
275
|
end
|
285
276
|
|
286
277
|
def shutdown_handler(event)
|
287
|
-
super
|
288
278
|
puts "MyRouter (#{identifier}) is shutting down. Put cleanup code here."
|
289
279
|
@processors.each { |p| p.shutdown! }
|
290
280
|
end
|
@@ -293,21 +283,15 @@ This lets you build actors that delegate work to other actors.
|
|
293
283
|
# Create your processor class.
|
294
284
|
class MyProcessor < Tribe::Actor
|
295
285
|
private
|
296
|
-
def initialize(options = {})
|
297
|
-
super
|
298
|
-
end
|
299
|
-
|
300
286
|
def on_process(event)
|
301
287
|
puts "MyProcessor (#{identifier}) received a process event (#{event.inspect})."
|
302
288
|
end
|
303
289
|
|
304
290
|
def exception_handler(e)
|
305
|
-
super
|
306
291
|
puts concat_e("MyProcessor (#{identifier}) died.", e)
|
307
292
|
end
|
308
293
|
|
309
294
|
def shutdown_handler(event)
|
310
|
-
super
|
311
295
|
puts "MyProcessor (#{identifier}) is shutting down. Put cleanup code here."
|
312
296
|
end
|
313
297
|
end
|
@@ -323,6 +307,10 @@ This lets you build actors that delegate work to other actors.
|
|
323
307
|
# Shutdown the router.
|
324
308
|
router.shutdown!
|
325
309
|
|
310
|
+
## Benchmarks
|
311
|
+
|
312
|
+
Please see the [performance] (https://github.com/chadrem/tribe/wiki/Performance "performance") wiki page for more information.
|
313
|
+
|
326
314
|
## TODO - missing features
|
327
315
|
|
328
316
|
- Supervisors.
|
data/Rakefile
CHANGED
data/lib/tribe.rb
CHANGED
data/lib/tribe/actable.rb
CHANGED
@@ -18,12 +18,11 @@ module Tribe
|
|
18
18
|
@logger = Workers::LogProxy.new(options[:logger])
|
19
19
|
@_as = Tribe::ActorState.new
|
20
20
|
@_as.dedicated = options[:dedicated] || false
|
21
|
-
@_as.
|
21
|
+
@_as.pool = @_as.dedicated ? Workers::Pool.new(:size => 1) : (options[:pool] || Workers.pool)
|
22
|
+
@_as.mailbox = Tribe::Mailbox.new(@_as.pool)
|
22
23
|
@_as.registry = options[:registry] || Tribe.registry
|
23
24
|
@_as.scheduler = options[:scheduler]
|
24
25
|
@_as.name = options[:name]
|
25
|
-
@_as.pool = @_as.dedicated ? Workers::Pool.new(:size => 1) : (options[:pool] || Workers.pool)
|
26
|
-
@_as.alive = true
|
27
26
|
|
28
27
|
@_as.registry.register(self)
|
29
28
|
end
|
@@ -37,37 +36,25 @@ module Tribe
|
|
37
36
|
|
38
37
|
public
|
39
38
|
|
40
|
-
def
|
41
|
-
|
39
|
+
def event!(event)
|
40
|
+
push_event(event)
|
41
|
+
|
42
|
+
return nil
|
42
43
|
end
|
43
44
|
|
44
|
-
def
|
45
|
-
|
45
|
+
def message!(command, data = nil, source = nil)
|
46
|
+
event = Tribe::Event.new(command, data, source)
|
46
47
|
|
47
|
-
|
48
|
-
@_as.pool.perform { process_events }
|
49
|
-
end
|
48
|
+
push_event(event)
|
50
49
|
|
51
50
|
return nil
|
52
51
|
end
|
53
52
|
|
54
|
-
def future!(command, data = nil)
|
55
|
-
|
53
|
+
def future!(command, data = nil, source = nil)
|
54
|
+
event = Tribe::Event.new(command, data, source)
|
55
|
+
event.future = future = Tribe::Future.new
|
56
56
|
|
57
|
-
|
58
|
-
@_as.futures.add(future)
|
59
|
-
|
60
|
-
perform! do
|
61
|
-
begin
|
62
|
-
result = event_handler(Workers::Event.new(command, data))
|
63
|
-
future.result = result
|
64
|
-
rescue Exception => exception
|
65
|
-
future.result = exception
|
66
|
-
raise
|
67
|
-
ensure
|
68
|
-
@_as.futures.delete(future)
|
69
|
-
end
|
70
|
-
end
|
57
|
+
push_event(event)
|
71
58
|
|
72
59
|
return future
|
73
60
|
end
|
@@ -81,7 +68,7 @@ module Tribe
|
|
81
68
|
end
|
82
69
|
|
83
70
|
def alive?
|
84
|
-
@_as.mailbox.
|
71
|
+
@_as.mailbox.alive?
|
85
72
|
end
|
86
73
|
|
87
74
|
def name
|
@@ -99,9 +86,23 @@ module Tribe
|
|
99
86
|
|
100
87
|
private
|
101
88
|
|
102
|
-
# The return value is used as the result of a future.
|
103
89
|
def event_handler(event)
|
104
|
-
|
90
|
+
result = nil
|
91
|
+
@_as.active_event = event
|
92
|
+
|
93
|
+
begin
|
94
|
+
result = send("on_#{event.command}", event)
|
95
|
+
rescue Exception => e
|
96
|
+
result = e
|
97
|
+
raise
|
98
|
+
ensure
|
99
|
+
if event.future && @_as.active_event
|
100
|
+
event.future.result = result
|
101
|
+
end
|
102
|
+
@_as.active_event = nil
|
103
|
+
end
|
104
|
+
|
105
|
+
return nil
|
105
106
|
end
|
106
107
|
|
107
108
|
def exception_handler(exception)
|
@@ -120,10 +121,9 @@ module Tribe
|
|
120
121
|
|
121
122
|
def cleanup_handler(exception = nil)
|
122
123
|
@_as.pool.shutdown if @_as.dedicated
|
123
|
-
@_as.mailbox.
|
124
|
+
@_as.mailbox.kill
|
124
125
|
@_as.registry.unregister(self)
|
125
126
|
@_as.timers.each { |t| t.cancel } if @_as.timers
|
126
|
-
@_as.futures.each { |f| f.result = exception || Tribe::ActorShutdownError.new } if @_as.futures
|
127
127
|
|
128
128
|
return nil
|
129
129
|
end
|
@@ -181,8 +181,21 @@ module Tribe
|
|
181
181
|
# Notes: These are used by the actor system and you should never call them directly.
|
182
182
|
#
|
183
183
|
|
184
|
+
def forward!(dest)
|
185
|
+
dest.event!(@_as.active_event)
|
186
|
+
@_as.active_event = nil
|
187
|
+
|
188
|
+
return nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def push_event(event)
|
192
|
+
@_as.mailbox.push(event) do
|
193
|
+
process_events
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
184
197
|
def process_events
|
185
|
-
while (event = @_as.mailbox.
|
198
|
+
while (event = @_as.mailbox.obtain_and_shift)
|
186
199
|
case event.command
|
187
200
|
when :shutdown
|
188
201
|
cleanup_handler
|
@@ -199,7 +212,7 @@ module Tribe
|
|
199
212
|
exception_handler(exception)
|
200
213
|
ensure
|
201
214
|
@_as.mailbox.release do
|
202
|
-
|
215
|
+
process_events
|
203
216
|
end
|
204
217
|
|
205
218
|
return nil
|
data/lib/tribe/actor_state.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
require 'tribe/benchmark/throughput'
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Tribe
|
2
|
+
module Benchmark
|
3
|
+
module Throughput
|
4
|
+
MAX_INCR = 100000
|
5
|
+
ACTOR_COUNT = 100000
|
6
|
+
COUNTERS = 20
|
7
|
+
|
8
|
+
def self.run
|
9
|
+
ACTOR_COUNT.times do |i|
|
10
|
+
actor = Tribe.registry["actor_#{i}"]
|
11
|
+
actor.shutdown! if actor
|
12
|
+
end
|
13
|
+
|
14
|
+
$start_time = Time.now.utc
|
15
|
+
$finished = 0
|
16
|
+
$lock = Mutex.new
|
17
|
+
|
18
|
+
ACTOR_COUNT.times do |i|
|
19
|
+
MyActor.new(:name => "actor_#{i}")
|
20
|
+
end
|
21
|
+
|
22
|
+
COUNTERS.times do |i|
|
23
|
+
Tribe.registry["actor_#{rand(ACTOR_COUNT)}"].message!(:do_stuff, MyData.new("data_#{i}"))
|
24
|
+
end
|
25
|
+
|
26
|
+
$lock.synchronize do
|
27
|
+
puts 'Please wait...'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.stop
|
32
|
+
end
|
33
|
+
|
34
|
+
class MyData
|
35
|
+
def initialize(name)
|
36
|
+
@name = name
|
37
|
+
@counter = 0
|
38
|
+
@start_time = Time.now
|
39
|
+
end
|
40
|
+
|
41
|
+
def increment
|
42
|
+
@counter += 1
|
43
|
+
|
44
|
+
if @counter >= MAX_INCR
|
45
|
+
$lock.synchronize do
|
46
|
+
$finished += 1
|
47
|
+
|
48
|
+
if $finished == COUNTERS
|
49
|
+
puts "\nFinished! Rate=#{(COUNTERS * MAX_INCR).to_f / (Time.now.utc - $start_time).to_f } msgs/sec\n"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
|
56
|
+
return true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class MyActor < Tribe::Actor
|
61
|
+
private
|
62
|
+
def on_do_stuff(event)
|
63
|
+
if event.data.increment
|
64
|
+
Tribe.registry["actor_#{rand(ACTOR_COUNT)}"].message!(:do_stuff, event.data)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def exception_handler(e)
|
69
|
+
puts concat_e("MyActor (#{identifier}) died.", e)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/tribe/event.rb
ADDED
data/lib/tribe/mailbox.rb
CHANGED
@@ -1,44 +1,64 @@
|
|
1
1
|
module Tribe
|
2
2
|
class Mailbox
|
3
|
-
def initialize(
|
3
|
+
def initialize(pool)
|
4
|
+
@pool = pool
|
4
5
|
@messages = []
|
6
|
+
@alive = true
|
5
7
|
@mutex = Mutex.new
|
6
8
|
end
|
7
9
|
|
8
10
|
def push(event, &block)
|
9
11
|
@mutex.synchronize do
|
12
|
+
return nil unless @alive
|
13
|
+
|
10
14
|
@messages.push(event)
|
11
|
-
block.call unless @
|
15
|
+
@pool.perform { block.call } unless @owner_thread
|
12
16
|
end
|
13
17
|
|
14
18
|
return nil
|
15
19
|
end
|
16
20
|
|
17
|
-
def
|
21
|
+
def obtain_and_shift
|
18
22
|
@mutex.synchronize do
|
19
|
-
return nil
|
20
|
-
|
21
|
-
@current_thread = Thread.current unless @current_thread
|
23
|
+
return nil unless @alive
|
22
24
|
|
23
|
-
|
25
|
+
if @owner_thread
|
26
|
+
if @owner_thread == Thread.current
|
27
|
+
return @messages.shift
|
28
|
+
else
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
else
|
32
|
+
@owner_thread = Thread.current
|
33
|
+
return @messages.shift
|
34
|
+
end
|
24
35
|
end
|
25
36
|
end
|
26
37
|
|
27
38
|
def release(&block)
|
28
39
|
@mutex.synchronize do
|
29
|
-
@
|
30
|
-
|
40
|
+
return nil unless @owner_thread == Thread.current
|
41
|
+
|
42
|
+
@owner_thread = nil
|
43
|
+
@pool.perform { block.call } if @alive && @messages.length > 0
|
31
44
|
end
|
32
45
|
|
33
46
|
return nil
|
34
47
|
end
|
35
48
|
|
36
|
-
def
|
49
|
+
def kill
|
37
50
|
@mutex.synchronize do
|
38
|
-
|
51
|
+
@alive = false
|
52
|
+
@messages.clear
|
39
53
|
end
|
40
54
|
|
41
55
|
return nil
|
42
56
|
end
|
57
|
+
|
58
|
+
def alive?
|
59
|
+
@mutex.synchronize do
|
60
|
+
return @alive
|
61
|
+
end
|
62
|
+
end
|
43
63
|
end
|
44
64
|
end
|
data/lib/tribe/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tribe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chad Remesch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: workers
|
@@ -41,7 +41,10 @@ files:
|
|
41
41
|
- lib/tribe/actable.rb
|
42
42
|
- lib/tribe/actor.rb
|
43
43
|
- lib/tribe/actor_state.rb
|
44
|
+
- lib/tribe/benchmark.rb
|
45
|
+
- lib/tribe/benchmark/throughput.rb
|
44
46
|
- lib/tribe/dedicated_actor.rb
|
47
|
+
- lib/tribe/event.rb
|
45
48
|
- lib/tribe/exceptions.rb
|
46
49
|
- lib/tribe/future.rb
|
47
50
|
- lib/tribe/mailbox.rb
|