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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c286bf55aac4e1e85558bb56a3b49b2e84dcabc3
4
- data.tar.gz: 1b0743b0e63db87ae6d0b0f81bacdd12c520ba46
3
+ metadata.gz: 50de6f25aa1a85a300729b25e432f661619f2468
4
+ data.tar.gz: d8f1c9329ceefad25909e6db346288a6aea39ab6
5
5
  SHA512:
6
- metadata.gz: a87e8def58e14e31d464783f10c30c421ef0c8343b1a0abb8bc9075c73208ba32e28cc3d195dda7ffee0daa80072e16d2397392e5997810a51f57b35058a2673
7
- data.tar.gz: bb336feb72e68638ad1d92b63826724ea07eb768f377eaa53a01639ba801a876adf5f80588067246922490482572913f5fd0e8478fe5ecca4406e476db00b364
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 used for exception, shutdown, and cleanup handling.
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 # The name of the actor (must be unique in the registry).
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 10
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
- ## Benchmarks
355
+ ## Linking
311
356
 
312
- Please see the [performance] (https://github.com/chadrem/tribe/wiki/Performance "performance") wiki page for more information.
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
- ## TODO - missing features
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
- - Supervisors.
317
- - Linking.
318
- - Future timeouts.
319
- - Remote actors (Tribe clustering).
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!(:shutdown)
64
+ return message!(:_shutdown)
64
65
  end
65
66
 
66
67
  def perform!(&block)
67
- return message!(:perform, block)
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 :shutdown
236
+ when :_shutdown
201
237
  cleanup_handler
202
238
  shutdown_handler(event)
203
- when :perform
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
@@ -8,5 +8,8 @@ module Tribe
8
8
  attr_accessor :name
9
9
  attr_accessor :pool
10
10
  attr_accessor :active_event
11
+ attr_accessor :parent
12
+ attr_accessor :children
13
+ attr_accessor :exception
11
14
  end
12
15
  end
@@ -1,8 +1,14 @@
1
1
  module Tribe
2
- class ActorShutdownError < RuntimeError; end
3
- class ActorNameError < RuntimeError; end
2
+ class TribeError < RuntimeError; end
4
3
 
5
- class FutureError < RuntimeError; end
4
+ class ActorShutdownError < TribeError; end
5
+ class ActorNameError < TribeError; end
6
+ class ActorChildDied < TribeError; end
7
+ class ActorParentDied < TribeError; end
6
8
 
7
- class RegistryError < RuntimeError; end
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
- raise Tribe::FutureError.new('Result must only be set once.') unless @state == :initialized
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::FutureError.new('Result must be set first.') unless @state == :finished
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::FutureError.new('Result must be set first.') unless @state == :finished
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
@@ -1,3 +1,3 @@
1
1
  module Tribe
2
- VERSION = '0.3.1'
2
+ VERSION = '0.3.2'
3
3
  end
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.1
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-09 00:00:00.000000000 Z
11
+ date: 2013-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: workers