tribe 0.3.1 → 0.3.2
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 +149 -10
- data/lib/tribe/actable.rb +44 -4
- data/lib/tribe/actor_state.rb +3 -0
- data/lib/tribe/exceptions.rb +10 -4
- data/lib/tribe/future.rb +30 -3
- data/lib/tribe/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50de6f25aa1a85a300729b25e432f661619f2468
|
4
|
+
data.tar.gz: d8f1c9329ceefad25909e6db346288a6aea39ab6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f58370c1c6b6b9ba42b7858bcac28016f4a5dc170432d7882163573330c12b949ad037daa36424db3839d8dad7f642294302be4d91903ea80f431ee4a9a721a
|
7
|
+
data.tar.gz: d894eb6eca95825d89ab91d4823545fa737a6cc588320ffb80b3f4239e5a3c946e13191f3941210b5f2f0436e2dc22692c2532d3646563452790a4edf6588f9a
|
data/README.md
CHANGED
@@ -9,6 +9,23 @@ It is built on top of the [Workers] (https://github.com/chadrem/workers "Workers
|
|
9
9
|
|
10
10
|
Event-driven servers can be built using [Tribe EM] (https://github.com/chadrem/tribe_em "Tribe EM").
|
11
11
|
|
12
|
+
## Contents
|
13
|
+
|
14
|
+
- [Installation](#installation)
|
15
|
+
- [Actors](#actors)
|
16
|
+
- [Registries](#registries)
|
17
|
+
- [Timers](#timers)
|
18
|
+
- [Futures](#futures)
|
19
|
+
- [Non-blocking](#non-blocking)
|
20
|
+
- [Blocking](#blocking)
|
21
|
+
- [Timeouts](#timeouts)
|
22
|
+
- [Performance](#performance-summary)
|
23
|
+
- [Forwarding](#forwarding)
|
24
|
+
- [Linking](#linking)
|
25
|
+
- [Supervisors](#supervisors)
|
26
|
+
- [Benchmarks](#benchmarks)
|
27
|
+
- [Contributing](#contributing)
|
28
|
+
|
12
29
|
## Installation
|
13
30
|
|
14
31
|
Add this line to your application's Gemfile:
|
@@ -29,7 +46,7 @@ Actors are light-weight objects that use asynchronous message passing for commun
|
|
29
46
|
There are two types of methods that you create in your actors:
|
30
47
|
|
31
48
|
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
|
49
|
+
2. *System handlers* are postfixed with "_handler" and are built into the actor system. These are hooks into the Tribe's actor system.
|
33
50
|
|
34
51
|
To send a message you use the Actable#message! method and specify a command with an optional data parameter.
|
35
52
|
The return value will always be nil since messaging is asynchronous.
|
@@ -46,10 +63,12 @@ More information on them will be provided throughout this readme.
|
|
46
63
|
end
|
47
64
|
|
48
65
|
def exception_handler(e)
|
66
|
+
super
|
49
67
|
puts concat_e("MyActor (#{identifier}) died.", e)
|
50
68
|
end
|
51
69
|
|
52
70
|
def shutdown_handler(event)
|
71
|
+
super
|
53
72
|
puts "MyActor (#{identifier}) is shutting down. Put cleanup code here."
|
54
73
|
end
|
55
74
|
end
|
@@ -82,7 +101,8 @@ Actors that block for long periods of time should use a dedicated thread (:dedic
|
|
82
101
|
:dedicated => false, # If true, the actor runs with a worker pool that has one thread.
|
83
102
|
:pool => Workers.pool, # The workers pool used to execute events.
|
84
103
|
:registry => Tribe.registry, # The registry used to store a reference to the actor if it has a name.
|
85
|
-
:name => nil
|
104
|
+
:name => nil, # The name of the actor (must be unique in the registry).
|
105
|
+
:parent => nil # Set the parent actor (used by the supervision feature).
|
86
106
|
)
|
87
107
|
|
88
108
|
## Registries
|
@@ -124,7 +144,7 @@ Both one-shot and periodic timers are provided.
|
|
124
144
|
end
|
125
145
|
|
126
146
|
# Sleep in order to observe the timers.
|
127
|
-
sleep
|
147
|
+
sleep(10)
|
128
148
|
|
129
149
|
# Shutdown the actors.
|
130
150
|
10.times do |i|
|
@@ -147,6 +167,7 @@ No waiting for a result is involved and the actor will continue to process other
|
|
147
167
|
class ActorA < Tribe::Actor
|
148
168
|
private
|
149
169
|
def exception_handler(e)
|
170
|
+
super
|
150
171
|
puts concat_e("ActorA (#{identifier}) died.", e)
|
151
172
|
end
|
152
173
|
|
@@ -171,6 +192,7 @@ No waiting for a result is involved and the actor will continue to process other
|
|
171
192
|
|
172
193
|
class ActorB < Tribe::Actor
|
173
194
|
def exception_handler(e)
|
195
|
+
super
|
174
196
|
puts concat_e("ActorB (#{identifier}) died.", e)
|
175
197
|
end
|
176
198
|
|
@@ -204,6 +226,7 @@ The actor won't process any other events until the future has a result.
|
|
204
226
|
class ActorA < Tribe::Actor
|
205
227
|
private
|
206
228
|
def exception_handler(e)
|
229
|
+
super
|
207
230
|
puts concat_e("ActorA (#{identifier}) died.", e)
|
208
231
|
end
|
209
232
|
|
@@ -224,6 +247,7 @@ The actor won't process any other events until the future has a result.
|
|
224
247
|
|
225
248
|
class ActorB < Tribe::Actor
|
226
249
|
def exception_handler(e)
|
250
|
+
super
|
227
251
|
puts concat_e("ActorB (#{identifier}) died.", e)
|
228
252
|
end
|
229
253
|
|
@@ -245,6 +269,23 @@ The actor won't process any other events until the future has a result.
|
|
245
269
|
actor_a.shutdown!
|
246
270
|
actor_b.shutdown!
|
247
271
|
|
272
|
+
#### Timeouts
|
273
|
+
|
274
|
+
Futures can be confgured to timeout after a specified number of seconds.
|
275
|
+
When a timeout occurs, the result of the future will be a Tribe::FutureTimeout exception.
|
276
|
+
|
277
|
+
# Manually create a future (Use Actable#future! in your actors).
|
278
|
+
future = Tribe::Future.new
|
279
|
+
|
280
|
+
# Set a timeout (in seconds).
|
281
|
+
future.timeout = 2
|
282
|
+
|
283
|
+
# Wait for the timeout.
|
284
|
+
sleep(3)
|
285
|
+
|
286
|
+
# The result of the future is a timeout exception.
|
287
|
+
puts "Result: #{future.result}"
|
288
|
+
|
248
289
|
#### Performance Summary
|
249
290
|
|
250
291
|
Below you will find a summary of performance recommendations regarding the use of futures:
|
@@ -271,10 +312,12 @@ This lets you build routers that delegate work to other actors.
|
|
271
312
|
end
|
272
313
|
|
273
314
|
def exception_handler(e)
|
315
|
+
super
|
274
316
|
puts concat_e("MyRouter (#{identifier}) died.", e)
|
275
317
|
end
|
276
318
|
|
277
319
|
def shutdown_handler(event)
|
320
|
+
super
|
278
321
|
puts "MyRouter (#{identifier}) is shutting down. Put cleanup code here."
|
279
322
|
@processors.each { |p| p.shutdown! }
|
280
323
|
end
|
@@ -288,10 +331,12 @@ This lets you build routers that delegate work to other actors.
|
|
288
331
|
end
|
289
332
|
|
290
333
|
def exception_handler(e)
|
334
|
+
super
|
291
335
|
puts concat_e("MyProcessor (#{identifier}) died.", e)
|
292
336
|
end
|
293
337
|
|
294
338
|
def shutdown_handler(event)
|
339
|
+
super
|
295
340
|
puts "MyProcessor (#{identifier}) is shutting down. Put cleanup code here."
|
296
341
|
end
|
297
342
|
end
|
@@ -307,16 +352,110 @@ This lets you build routers that delegate work to other actors.
|
|
307
352
|
# Shutdown the router.
|
308
353
|
router.shutdown!
|
309
354
|
|
310
|
-
##
|
355
|
+
## Linking
|
311
356
|
|
312
|
-
|
357
|
+
Linking allows actors to group together into a tree structure such that they all live or die as one group.
|
358
|
+
Such linking is useful for breaking complex problems into smaller (and easier to manage) components.
|
359
|
+
To create a linked actor you use the Actable#spawn method to create it.
|
360
|
+
If any actor in a tree of linked actors dies, it will cause all actors above and below it to die too.
|
361
|
+
|
362
|
+
# Create the root level actor class.
|
363
|
+
class Level1 < Tribe::Actor
|
364
|
+
private
|
365
|
+
def on_spawn(event)
|
366
|
+
5.times do |i|
|
367
|
+
name = "level2_#{i}"
|
368
|
+
puts name
|
369
|
+
actor = spawn(Level2, :name => name)
|
370
|
+
actor.message!(:spawn, i)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# Create the mid-level actor class.
|
376
|
+
class Level2 < Tribe::Actor
|
377
|
+
private
|
378
|
+
def on_spawn(event)
|
379
|
+
5.times do |i|
|
380
|
+
name = "level3_#{event.data}_#{i}"
|
381
|
+
actor = spawn(Level3, :name => name)
|
382
|
+
actor.message!(:spawn)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# Create the bottom level actor class.
|
388
|
+
class Level3 < Tribe::Actor
|
389
|
+
private
|
390
|
+
def on_spawn(event)
|
391
|
+
puts "#{identifier} hello world!"
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
# Create the root actor.
|
396
|
+
root = Level1.new(:name => 'level1')
|
397
|
+
|
398
|
+
# Tell the root actor to create the tree of children.
|
399
|
+
root.message!(:spawn)
|
400
|
+
|
401
|
+
## Supervisors
|
402
|
+
|
403
|
+
As mentioned above, a failure in a linked actor will cause all associated actors (parent and children) to die.
|
404
|
+
Supervisors can be used to block the failure from propogating and allow you to restart the failed section of the tree.
|
405
|
+
|
406
|
+
# Create the root level actor class.
|
407
|
+
class Level1 < Tribe::Actor
|
408
|
+
private
|
409
|
+
def on_spawn(event)
|
410
|
+
5.times do |i|
|
411
|
+
create_subtree
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def create_subtree
|
416
|
+
actor = spawn(Level2)
|
417
|
+
actor.message!(:spawn)
|
418
|
+
end
|
419
|
+
|
420
|
+
def child_died_handler(actor, exception)
|
421
|
+
puts "My child (#{actor.identifier}) died. Restarting it."
|
422
|
+
create_subtree
|
423
|
+
end
|
424
|
+
end
|
313
425
|
|
314
|
-
|
426
|
+
# Create the mid-level actor class.
|
427
|
+
class Level2 < Tribe::Actor
|
428
|
+
private
|
429
|
+
def on_spawn(event)
|
430
|
+
5.times do |i|
|
431
|
+
actor = spawn(Level3)
|
432
|
+
actor.message!(:spawn)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
315
436
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
437
|
+
# Create the bottom level actor class.
|
438
|
+
class Level3 < Tribe::Actor
|
439
|
+
private
|
440
|
+
def on_spawn(event)
|
441
|
+
puts "#{identifier} says hello world!"
|
442
|
+
raise 'Sometimes I like to die.' if rand < 0.5
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# Create the root actor.
|
447
|
+
root = Level1.new(:name => 'root')
|
448
|
+
|
449
|
+
# Tell the root actor to create the tree of children.
|
450
|
+
root.message!(:spawn)
|
451
|
+
|
452
|
+
#### Important!
|
453
|
+
Restarting named actors is NOT currently supported, but will be in a future update.
|
454
|
+
Attempting to do so may result in Tribe::RegistryError exceptions when trying to spawn a replacement child.
|
455
|
+
|
456
|
+
## Benchmarks
|
457
|
+
|
458
|
+
Please see the [performance] (https://github.com/chadrem/tribe/wiki/Performance "performance") wiki page for more information.
|
320
459
|
|
321
460
|
## Contributing
|
322
461
|
|
data/lib/tribe/actable.rb
CHANGED
@@ -23,6 +23,7 @@ module Tribe
|
|
23
23
|
@_as.registry = options[:registry] || Tribe.registry
|
24
24
|
@_as.scheduler = options[:scheduler]
|
25
25
|
@_as.name = options[:name]
|
26
|
+
@_as.parent = options[:parent]
|
26
27
|
|
27
28
|
@_as.registry.register(self)
|
28
29
|
end
|
@@ -60,11 +61,11 @@ module Tribe
|
|
60
61
|
end
|
61
62
|
|
62
63
|
def shutdown!
|
63
|
-
return message!(:
|
64
|
+
return message!(:_shutdown)
|
64
65
|
end
|
65
66
|
|
66
67
|
def perform!(&block)
|
67
|
-
return message!(:
|
68
|
+
return message!(:_perform, block)
|
68
69
|
end
|
69
70
|
|
70
71
|
def alive?
|
@@ -106,6 +107,16 @@ module Tribe
|
|
106
107
|
end
|
107
108
|
|
108
109
|
def exception_handler(exception)
|
110
|
+
if @_as.parent
|
111
|
+
@_as.parent.message!(:_child_died, [self, exception])
|
112
|
+
end
|
113
|
+
|
114
|
+
if @_as.children
|
115
|
+
@_as.children.each { |c| c.message!(:_parent_died, [self, exception]) }
|
116
|
+
@_as.children.clear
|
117
|
+
@_as.children = nil
|
118
|
+
end
|
119
|
+
|
109
120
|
return nil
|
110
121
|
end
|
111
122
|
|
@@ -120,14 +131,29 @@ module Tribe
|
|
120
131
|
end
|
121
132
|
|
122
133
|
def cleanup_handler(exception = nil)
|
134
|
+
@_as.exception = exception
|
123
135
|
@_as.pool.shutdown if @_as.dedicated
|
124
136
|
@_as.mailbox.kill
|
125
137
|
@_as.registry.unregister(self)
|
126
138
|
@_as.timers.each { |t| t.cancel } if @_as.timers
|
127
139
|
|
140
|
+
if @_as.children && exception.nil?
|
141
|
+
@_as.children.each { |c| c.shutdown! }
|
142
|
+
@_as.children.clear
|
143
|
+
@_as.children = nil
|
144
|
+
end
|
145
|
+
|
128
146
|
return nil
|
129
147
|
end
|
130
148
|
|
149
|
+
def child_died_handler(child, exception)
|
150
|
+
raise Tribe::ActorChildDied.new("#{child.identifier} died.")
|
151
|
+
end
|
152
|
+
|
153
|
+
def parent_died_handler(parent, exception)
|
154
|
+
raise Tribe::ActorParentDied.new("#{parent.identifier} died.")
|
155
|
+
end
|
156
|
+
|
131
157
|
#
|
132
158
|
# Private API methods.
|
133
159
|
# Notes: Use these methods internally in your actor.
|
@@ -188,20 +214,34 @@ module Tribe
|
|
188
214
|
return nil
|
189
215
|
end
|
190
216
|
|
217
|
+
def spawn(klass, options = {})
|
218
|
+
options[:parent] = self
|
219
|
+
|
220
|
+
@_as.children ||= []
|
221
|
+
@_as.children << (actor = klass.new(options))
|
222
|
+
|
223
|
+
return actor
|
224
|
+
end
|
225
|
+
|
191
226
|
def push_event(event)
|
192
227
|
@_as.mailbox.push(event) do
|
193
228
|
process_events
|
194
229
|
end
|
195
230
|
end
|
196
231
|
|
232
|
+
# All system commands are prefixed with an underscore.
|
197
233
|
def process_events
|
198
234
|
while (event = @_as.mailbox.obtain_and_shift)
|
199
235
|
case event.command
|
200
|
-
when :
|
236
|
+
when :_shutdown
|
201
237
|
cleanup_handler
|
202
238
|
shutdown_handler(event)
|
203
|
-
when :
|
239
|
+
when :_perform
|
204
240
|
perform_handler(event)
|
241
|
+
when :_child_died
|
242
|
+
child_died_handler(event.data[0], event.data[1])
|
243
|
+
when :_parent_died
|
244
|
+
parent_died_handler(event.data[0], event.data[1])
|
205
245
|
else
|
206
246
|
event_handler(event)
|
207
247
|
end
|
data/lib/tribe/actor_state.rb
CHANGED
data/lib/tribe/exceptions.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
module Tribe
|
2
|
-
class
|
3
|
-
class ActorNameError < RuntimeError; end
|
2
|
+
class TribeError < RuntimeError; end
|
4
3
|
|
5
|
-
class
|
4
|
+
class ActorShutdownError < TribeError; end
|
5
|
+
class ActorNameError < TribeError; end
|
6
|
+
class ActorChildDied < TribeError; end
|
7
|
+
class ActorParentDied < TribeError; end
|
6
8
|
|
7
|
-
class
|
9
|
+
class FutureError < TribeError; end
|
10
|
+
class FutureNoResult < TribeError; end
|
11
|
+
class FutureTimeout < TribeError; end
|
12
|
+
|
13
|
+
class RegistryError < TribeError; end
|
8
14
|
end
|
data/lib/tribe/future.rb
CHANGED
@@ -17,9 +17,17 @@ module Tribe
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
def timeout?
|
21
|
+
@mutex.synchronnize do
|
22
|
+
return @state == :finished && @result.is_a?(Tribe::FutureTimeout)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
20
26
|
def result=(val)
|
21
27
|
@mutex.synchronize do
|
22
|
-
|
28
|
+
return unless @state == :initialized
|
29
|
+
|
30
|
+
@timer.cancel if @timer
|
23
31
|
|
24
32
|
@result = val
|
25
33
|
@state = :finished
|
@@ -37,7 +45,7 @@ module Tribe
|
|
37
45
|
|
38
46
|
def result
|
39
47
|
@mutex.synchronize do
|
40
|
-
raise Tribe::
|
48
|
+
raise Tribe::FutureNoResult.new('Result must be set first.') unless @state == :finished
|
41
49
|
|
42
50
|
return @result
|
43
51
|
end
|
@@ -55,7 +63,7 @@ module Tribe
|
|
55
63
|
|
56
64
|
def success?
|
57
65
|
@mutex.synchronize do
|
58
|
-
raise Tribe::
|
66
|
+
raise Tribe::FutureNoResult.new('Result must be set first.') unless @state == :finished
|
59
67
|
|
60
68
|
return !@result.is_a?(Exception)
|
61
69
|
end
|
@@ -94,5 +102,24 @@ module Tribe
|
|
94
102
|
return nil
|
95
103
|
end
|
96
104
|
end
|
105
|
+
|
106
|
+
def timeout
|
107
|
+
@mutex.synchronize do
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def timeout=(val)
|
112
|
+
raise Tribe::FutureError.new('Timeout may only be set once.') if @timeout
|
113
|
+
|
114
|
+
@timeout = val
|
115
|
+
|
116
|
+
@timer = Workers::Timer.new(val) do
|
117
|
+
begin
|
118
|
+
raise Tribe::FutureTimeout.new("Timeout after #{@timeout} seconds.")
|
119
|
+
rescue Tribe::FutureTimeout => e
|
120
|
+
self.result = e
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
97
124
|
end
|
98
125
|
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.2
|
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-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: workers
|