tribe 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 865a31569730f5bc15800bc24f0dc791faea2971
4
- data.tar.gz: c7a793ff5df24f85ddf2d0f877fd5a892f946fe6
3
+ metadata.gz: f3bf6bd1aba1fc0bc7778e35002942f86a23160e
4
+ data.tar.gz: c8b4d5c3c3e28f2d3b0598f257a6769ee7e8708b
5
5
  SHA512:
6
- metadata.gz: ec0d7842dd09a91e305da5719b19bea9a5aa584edcf39a522036f5668c05fac9efaafb442f8706c6766080b15b69e3f47d6edf778b56443439b2a0b567703ce8
7
- data.tar.gz: 28bd5c028b349ac7ed29d45593aae3a888e55d280fa7a927f6d3614e4bd4476c382fce95401ede14e04b2ddd0408b777a0b00fd3630703a2d0ed37ee0c960fb4
6
+ metadata.gz: 83ac8698abdc2012668474d87009d14156a0a8ef82c17553475f47284a427390bedba8c32f91260da9a14075e28602dfa2e07fe967edbf3963e85748d6b2e50a
7
+ data.tar.gz: 269e3611a89686367ed6f012af21eb0fd5ba49a0d9e722b2cd01ecd7025f74158d02484a10d951eebde217ab8e36f8b8731979221fcd8fe25382ed0aca65875c
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1 @@
1
+ 2.2.2
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.5
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in tribe.gemspec
3
+ gem 'coveralls', :require => false
4
+
4
5
  gemspec
6
+
7
+ gem 'workers', :git => 'https://github.com/chadrem/workers.git'
data/README.md CHANGED
@@ -1,31 +1,30 @@
1
- # Tribe
1
+ # Tribe [![Build Status](https://travis-ci.org/chadrem/tribe.svg)](https://travis-ci.org/chadrem/tribe) [![Coverage Status](https://coveralls.io/repos/chadrem/tribe/badge.svg?branch=master&service=github)](https://coveralls.io/github/chadrem/tribe?branch=master)
2
2
 
3
- Tribe is a Ruby gem that implements event-driven [actors] (http://en.wikipedia.org/wiki/Actor_model "actors").
4
- Actors are lightweight concurrent objects that use asynchronous message passing for communication.
3
+ Tribe is a Ruby gem that implements the [actor model] (http://en.wikipedia.org/wiki/Actor_model "actors") in an event-driven way.
5
4
 
6
5
  Tribe focuses on high performance, low latency, a simple API, and flexibility.
7
6
  It's goal is to support at least one million actors running on a small group of threads.
8
7
  It is built on top of the [Workers] (https://github.com/chadrem/workers "Workers") gem.
9
8
 
10
- Event-driven servers can be built using [Tribe EM] (https://github.com/chadrem/tribe_em "Tribe EM").
11
-
12
9
  ## Contents
13
10
 
14
11
  - [Installation](#installation)
15
12
  - [Actors](#actors)
16
- - [Root](#root-actor)
13
+ - [Root](#root)
17
14
  - [Handlers](#handlers)
18
- - [Messages](#messages)
15
+ - [Messages](#messages)
19
16
  - [Registries](#registries)
20
- - [Timers](#timers)
21
17
  - [Futures](#futures)
22
18
  - [Non-blocking](#non-blocking)
23
19
  - [Blocking](#blocking)
24
20
  - [Timeouts](#timeouts)
25
21
  - [Performance](#performance-summary)
26
22
  - [Forwarding](#forwarding)
23
+ - [Timers](#timers)
27
24
  - [Linking](#linking)
28
25
  - [Supervisors](#supervisors)
26
+ - [Blocking code](#blocking-code)
27
+ - [Debugging](#debugging)
29
28
  - [Benchmarks](#benchmarks)
30
29
  - [Contributing](#contributing)
31
30
 
@@ -48,31 +47,51 @@ Or install it yourself as:
48
47
  Actors are the building blocks of your application.
49
48
  There are three ways to create an actor class:
50
49
 
51
- - Inherit from Tribe::Actor (uses the shared thread pool).
52
- - Inherit from Tribe::DedicatedActor (uses a dedicated thread).
53
- - Mixin Tribe::Actable and call the Actable#init_actable method in your constructor.
54
-
50
+ - Inherit from ````Tribe::Actor```` (uses the shared thread pool).
51
+ - Inherit from ````Tribe::DedicatedActor```` (uses a dedicated thread).
52
+ - Mixin ````Tribe::Actable```` and call the ````init_actable```` in your constructor.
55
53
 
56
54
  #### Root
57
55
 
58
- Well designed applications built with the actor model tend to organize their actors in a tree like structure.
56
+ A well designed application organizes its actors in a tree like structure.
59
57
  To encourage this, Tribe has a special built-in actor known as the root actor.
60
- You should use this actor to spawn all of your application specific actors.
58
+ You should use the root actor to spawn all of your application specific actors.
59
+
60
+ class MyActor < Tribe::Actor
61
+ private
62
+ # Your code goes here.
63
+ end
64
+
65
+ Tribe.root.spawn!(MyActor)
66
+
67
+ #### Command Handlers
68
+
69
+ Command handlers are how you customize your actors.
70
+ They are private methods that are prefixed with "on_" and they define the commands your actor knows how to handle.
71
+ They accept one argument, an instance of ````Tribe::Event```` that shouuld always be named ````event````.
61
72
 
62
- Tribe.root
73
+ A few command handlers are built into every actor to handle system specific events. They are:
63
74
 
64
- #### Handlers
75
+ - ````on_initialize````: This handler takes the place of Ruby's ````initialize````. It is the first event processsed by all actors.
76
+ - ````on_exception````: This handler will be called whenever an exception occurs. You can access the exception through ````event.data```` in case you want to print it, log it, etc. An exception inside of an actor will result in that actor's death.
77
+ - ````on_shutdown````: This handler will be called whenever an actor is asked to shutdown cleanly.
78
+ - ````on_child_died````: This handler gives an actor a chance to spawn a replacement child. You can access a reference to the child through ````event.data````. If the actor is a supervisor, it will continue to live otherwise it will die too.
79
+ - ````on_child_shutdown````: This handler is similar to ````on_child_died````, but for when a child is shutdown cleanly.
80
+ - ````on_parent_died````: This handler is also similar to ````on_child_died```` except for the parent actor. Child actors die when their parent dies.
65
81
 
66
- There are two types of methods that you create in your actor classes:
82
+ You should never call the built in command handlers yourself.
83
+ They are reserved for the actor system and calling them yourself could result in unexpected behavior.
67
84
 
68
- 1. *Command handlers* are prefixed with "on_" and define the types of commands your actor will process.
69
- 2. *System handlers* are postfixed with "_handler" and are built into the actor system. These are hooks into the Tribe's actor system.
85
+ ## Messages
70
86
 
71
- #### Messages
87
+ Messages are the most basic type of communication. They are sent using using two methods:
72
88
 
73
- Actors communicate asynchronously using a number of methods that end in ! (exclamation point or “bang”).
74
- The most basic type of communication is a message and they are sent using using the Actable#message! and Actable#deliver_message! methods.
75
- These methods always return nil since they are fire-and-forget.
89
+ - ````message!````: This method is used to tell one actor to send another actor a message. A reference to the source actor is included in the message in case the destination actor wants to respond. Usually it is used when your actor code wants to message another actor.
90
+ - ````direct_message!````: This method is used to directly message an actor. Usually it is used when non-actor code wants to message an actor. No source actor is associated with the message.
91
+
92
+ Since messages are fire-and-forget, both of the above methods always return ````nil````.
93
+
94
+ Messages can include data that you want to pass between actors. It is best practice to treat data as owned by only one actor at a time. By doing this, you prevent race conditions and the need to create locks for your data.
76
95
 
77
96
  # Create your custom actor class.
78
97
  class MyActor < Tribe::Actor
@@ -81,26 +100,20 @@ These methods always return nil since they are fire-and-forget.
81
100
  puts "Received a custom event (#{event.inspect})."
82
101
  end
83
102
 
84
- def exception_handler(e)
85
- super
86
- puts concat_e("MyActor (#{identifier}) died.", e)
87
- end
88
-
89
- def shutdown_handler(event)
90
- super
91
- puts "MyActor (#{identifier}) is shutting down. Put cleanup code here."
103
+ def on_shutdown(event)
104
+ puts "MyActor (#{identifier}) is shutting down."
92
105
  end
93
106
  end
94
107
 
95
108
  # Create some named actors that are children of the root actor.
96
109
  100.times do |i|
97
- Tribe.root.spawn(MyActor, :name => "my_actor_#{i}")
110
+ Tribe.root.spawn!(MyActor, :name => "my_actor_#{i}")
98
111
  end
99
112
 
100
113
  # Send an event to each actor.
101
114
  100.times do |i|
102
115
  actor = Tribe.registry["my_actor_#{i}"]
103
- actor.deliver_message!(:my_custom, 'hello world')
116
+ actor.direct_message!(:my_custom, 'hello world')
104
117
  end
105
118
 
106
119
  # Shutdown the actors.
@@ -112,69 +125,29 @@ These methods always return nil since they are fire-and-forget.
112
125
  ## Registries
113
126
 
114
127
  Registries hold references to named actors so that you can easily find them.
115
- In general you shouldn't have to create your own since there is a global one (Tribe.registry).
128
+ You don't have to create your own since there is a global one called ````Tribe.registry````.
129
+ The Root actor is named 'root' and stored in the default registry.
116
130
 
117
- actor = Tribe.root.spawn(Tribe::Actor, :name => 'some_actor')
131
+ actor = Tribe.root.spawn!(Tribe::Actor, :name => 'some_actor')
118
132
 
119
133
  if actor == Tribe.registry['some_actor']
120
134
  puts 'Successfully found some_actor in the registry.'
121
135
  end
122
136
 
123
- ## Timers
124
-
125
- Actors can create timers to perform some work in the future.
126
- Both one-shot and periodic timers are provided.
127
-
128
- class MyActor < Tribe::Actor
129
- private
130
- def initialize(options = {})
131
- super
132
- timer(1, :timer, Time.now)
133
- periodic_timer(1, :periodic_timer, Time.now)
134
- end
135
-
136
- def on_timer(event)
137
- puts "MyActor (#{identifier}) ONE-SHOT: #{event.data}"
138
- end
139
-
140
- def on_periodic_timer(event)
141
- puts "MyActor (#{identifier}) PERIODIC: #{event.data}"
142
- end
143
- end
144
-
145
- # Create some named actors.
146
- 10.times do |i|
147
- Tribe.root.spawn(MyActor, :name => "my_actor_#{i}")
148
- end
149
-
150
- # Sleep in order to observe the timers.
151
- sleep(10)
152
-
153
- # Shutdown the actors.
154
- 10.times do |i|
155
- actor = Tribe.registry["my_actor_#{i}"]
156
- actor.shutdown!
157
- end
158
-
159
137
  ## Futures
160
138
 
161
- Message passing with the Actable#message! and Actable#deliver_message! methods is asynchronous and always returns nil.
162
- This can be a pain since in many cases you will be interested in the result.
163
- The Actable#future! method solves this problem by returning a Tribe::Future object.
164
- You can then use this object to obtain the result when it becomes available.
139
+ Messages are limited in that they are one way (fire-and-forget).
140
+ Many times you'll be interested in receiving a response and this is when futures become useful.
141
+ To send a future you use ````future!```` instead of ````message!````.
142
+ It will return a ````Future```` object (instead of ````nil````) that will give you access to the result when it becomes available.
165
143
 
166
- #### Non-blocking
144
+ #### Non-blocking API
167
145
 
168
146
  Non-blocking futures are asynchronous and use callbacks.
169
147
  No waiting for a result is involved and the actor will continue to process other events.
170
148
 
171
149
  class ActorA < Tribe::Actor
172
150
  private
173
- def exception_handler(e)
174
- super
175
- puts concat_e("ActorA (#{identifier}) died.", e)
176
- end
177
-
178
151
  def on_start(event)
179
152
  friend = registry['actor_b']
180
153
  future = future!(friend, :compute, 10)
@@ -182,19 +155,18 @@ No waiting for a result is involved and the actor will continue to process other
182
155
  future.success do |result|
183
156
  puts "ActorA (#{identifier}) future result: #{result}"
184
157
  end
158
+ end
185
159
 
186
- future.failure do |exception|
187
- puts "ActorA (#{identifier}) future failure: #{exception}"
188
- end
160
+ def on_shutdown(event)
161
+ puts "MyActor (#{identifier}) is shutting down."
189
162
  end
190
163
  end
191
164
 
192
165
  class ActorB < Tribe::Actor
193
- def exception_handler(e)
194
- super
195
- puts concat_e("ActorB (#{identifier}) died.", e)
166
+ private
167
+ def on_shutdown(event)
168
+ puts "MyActor (#{identifier}) is shutting down."
196
169
  end
197
-
198
170
  def on_compute(event)
199
171
  return factorial(event.data)
200
172
  end
@@ -205,31 +177,28 @@ No waiting for a result is involved and the actor will continue to process other
205
177
  end
206
178
  end
207
179
 
208
- actor_a = Tribe.root.spawn(ActorA, :name => 'actor_a')
209
- actor_b = Tribe.root.spawn(ActorB, :name => 'actor_b')
180
+ actor_a = Tribe.root.spawn!(ActorA, :name => 'actor_a')
181
+ actor_b = Tribe.root.spawn!(ActorB, :name => 'actor_b')
210
182
 
211
- actor_a.deliver_message!(:start)
183
+ actor_a.direct_message!(:start)
212
184
 
185
+ # Shutdown the actors.
186
+ sleep(3)
213
187
  actor_a.shutdown!
214
188
  actor_b.shutdown!
215
189
 
216
- #### Blocking
190
+ #### Blocking API
217
191
 
218
192
  Blocking futures are synchronous.
219
193
  The actor won't process any other events until the future has a result.
220
194
 
221
195
  class ActorA < Tribe::Actor
222
196
  private
223
- def exception_handler(e)
224
- super
225
- puts concat_e("ActorA (#{identifier}) died.", e)
226
- end
227
-
228
197
  def on_start(event)
229
198
  friend = registry['actor_b']
230
199
  future = future!(friend, :compute, 10)
231
200
 
232
- future.wait # The current thread will sleep until a result is available.
201
+ wait!(future) # The current thread will sleep until a result is available.
233
202
 
234
203
  if future.success?
235
204
  puts "ActorA (#{identifier}) future result: #{future.result}"
@@ -240,11 +209,7 @@ The actor won't process any other events until the future has a result.
240
209
  end
241
210
 
242
211
  class ActorB < Tribe::Actor
243
- def exception_handler(e)
244
- super
245
- puts concat_e("ActorB (#{identifier}) died.", e)
246
- end
247
-
212
+ private
248
213
  def on_compute(event)
249
214
  return factorial(event.data)
250
215
  end
@@ -255,10 +220,12 @@ The actor won't process any other events until the future has a result.
255
220
  end
256
221
  end
257
222
 
258
- actor_a = Tribe.root.spawn(ActorA, :name => 'actor_a')
259
- actor_b = Tribe.root.spawn(ActorB, :name => 'actor_b')
223
+ actor_a = Tribe.root.spawn!(ActorA, :name => 'actor_a')
224
+ actor_b = Tribe.root.spawn!(ActorB, :name => 'actor_b')
260
225
 
261
- actor_a.deliver_message!(:start)
226
+ actor_a.direct_message!(:start)
227
+
228
+ sleep(3)
262
229
 
263
230
  actor_a.shutdown!
264
231
  actor_b.shutdown!
@@ -266,27 +233,50 @@ The actor won't process any other events until the future has a result.
266
233
  #### Timeouts
267
234
 
268
235
  Futures can be confgured to timeout after a specified number of seconds.
269
- When a timeout occurs, the result of the future will be a Tribe::FutureTimeout exception.
236
+ When a timeout occurs, the result of the future will be a ````Tribe::FutureTimeout```` exception.
270
237
 
271
- # Manually create a future for this example (Use Actable#future! in your actors).
272
- future = Tribe::Future.new
238
+ class ActorA < Tribe::Actor
239
+ private
240
+ def on_start(event)
241
+ friend = registry['actor_b']
242
+ future = future!(friend, :compute, 10)
243
+ future.timeout = 2
273
244
 
274
- # Set a timeout (in seconds).
275
- future.timeout = 2
245
+ wait!(future) # The current thread will sleep until a result is available.
276
246
 
277
- # Wait for the timeout.
278
- sleep(3)
247
+ if future.success?
248
+ puts "ActorA (#{identifier}) future result: #{future.result}"
249
+ else
250
+ puts "ActorA (#{identifier}) future failure: #{future.result}"
251
+ end
252
+ end
253
+ end
254
+
255
+ class ActorB < Tribe::Actor
256
+ private
257
+ def on_compute(event)
258
+ sleep(4) # Force a timeout.
259
+ return event.data * 2
260
+ end
261
+ end
262
+
263
+ actor_a = Tribe.root.spawn!(ActorA, :name => 'actor_a')
264
+ actor_b = Tribe.root.spawn!(ActorB, :name => 'actor_b')
279
265
 
280
- # The result of the future is a timeout exception.
281
- puts "Result: #{future.result}"
266
+ actor_a.direct_message!(:start)
267
+
268
+ sleep(6)
269
+
270
+ actor_a.shutdown!
271
+ actor_b.shutdown!
282
272
 
283
273
  #### Performance Summary
284
274
 
285
- Below you will find a summary of performance recommendations regarding the use of futures:
275
+ Below you will find a summary of performance recommendations for futures:
286
276
 
287
- - Use Actable#message! unless you really need Actable#future! since futures have overhead.
288
- - If you use Actable#future!, prefer the non-blocking API over the blocking one.
289
- - If you use the blocking API, the actor calling Future#wait should use a dedicated worker thread.
277
+ - Use ````message!```` unless you really need ````future!```` since futures have overhead.
278
+ - If you use ````future!````, prefer the non-blocking API over the blocking one.
279
+ - If you use ````future!```` with the blocking API, the actor calling ````wait!```` will create a temporary thread. Since threads are a a finite resource, you should be careful to not create more of them than your operating system can simultaneously support. There is no such concern with the non-blocking API.
290
280
 
291
281
  ## Forwarding
292
282
 
@@ -296,25 +286,13 @@ This lets you build routers that delegate work to other actors.
296
286
  # Create your router class.
297
287
  class MyRouter < Tribe::Actor
298
288
  private
299
- def initialize(options = {})
300
- super
301
- @processors = 100.times.map { MyProcessor.new }
289
+ def on_initialize(event)
290
+ @processors = 100.times.map { spawn!(MyProcessor) }
302
291
  end
303
292
 
304
293
  def on_process(event)
305
294
  forward!(@processors[rand(100)])
306
295
  end
307
-
308
- def exception_handler(e)
309
- super
310
- puts concat_e("MyRouter (#{identifier}) died.", e)
311
- end
312
-
313
- def shutdown_handler(event)
314
- super
315
- puts "MyRouter (#{identifier}) is shutting down. Put cleanup code here."
316
- @processors.each { |p| p.shutdown! }
317
- end
318
296
  end
319
297
 
320
298
  # Create your processor class.
@@ -323,141 +301,194 @@ This lets you build routers that delegate work to other actors.
323
301
  def on_process(event)
324
302
  puts "MyProcessor (#{identifier}) received a process event (#{event.inspect})."
325
303
  end
326
-
327
- def exception_handler(e)
328
- super
329
- puts concat_e("MyProcessor (#{identifier}) died.", e)
330
- end
331
-
332
- def shutdown_handler(event)
333
- super
334
- puts "MyProcessor (#{identifier}) is shutting down. Put cleanup code here."
335
- end
336
304
  end
337
305
 
338
306
  # Create the router.
339
- router = Tribe.root.spawn(MyRouter, :name => 'router')
307
+ router = Tribe.root.spawn!(MyRouter, :name => 'router')
340
308
 
341
309
  # Send an event to the router and it will forward it to a random processor.
342
310
  100.times do |i|
343
- router.deliver_message!(:process, i)
311
+ router.direct_message!(:process, i)
344
312
  end
345
313
 
346
314
  # Shutdown the router.
315
+ sleep(3)
347
316
  router.shutdown!
348
317
 
349
- ## Linking
318
+ ## Timers
350
319
 
351
- Linking allows actors to group together into a tree structure such that they all live or die as one group.
352
- Such linking is useful for breaking up complex problems into multiple smaller units.
353
- To create a linked actor you use the Actable#spawn method.
354
- By default, if a linked actor dies, it will cause its parent and children to die too.
355
- Thus the entire tree lives or dies together.
320
+ Actors can create timers to perform some work in the future.
321
+ Both one-shot and periodic timers are provided.
356
322
 
357
- # Create the top-level actor class.
358
- class Level1 < Tribe::Actor
323
+ class MyActor < Tribe::Actor
359
324
  private
360
- def on_spawn(event)
361
- 5.times do |i|
362
- name = "level2_#{i}"
363
- puts name
364
- actor = spawn(Level2, :name => name)
365
- message!(actor, :spawn, i)
366
- end
325
+ def on_initialize(event)
326
+ timer!(1, :timer, 'hello once')
327
+ periodic_timer!(1, :periodic_timer, 'hello many times')
367
328
  end
368
- end
369
329
 
370
- # Create the mid-level actor class.
371
- class Level2 < Tribe::Actor
372
- private
373
- def on_spawn(event)
374
- 5.times do |i|
375
- name = "level3_#{event.data}_#{i}"
376
- actor = spawn(Level3, :name => name)
377
- message!(actor, :spawn)
378
- end
330
+ def on_timer(event)
331
+ puts "MyActor (#{identifier}) ONE-SHOT: #{event.data}"
379
332
  end
380
- end
381
333
 
382
- # Create the bottom level actor class.
383
- class Level3 < Tribe::Actor
384
- private
385
- def on_spawn(event)
386
- puts "#{identifier} hello world!"
334
+ def on_periodic_timer(event)
335
+ puts "MyActor (#{identifier}) PERIODIC: #{event.data}"
387
336
  end
388
337
  end
389
338
 
390
- # Create the top-level actor.
391
- top = Tribe.root.spawn(Level1, :name => 'level1')
339
+ # Create some named actors.
340
+ 10.times do |i|
341
+ Tribe.root.spawn!(MyActor, :name => "my_actor_#{i}")
342
+ end
343
+
344
+ # Sleep in order to observe the timers.
345
+ sleep(10)
346
+
347
+ # Shutdown the actors.
348
+ 10.times do |i|
349
+ actor = Tribe.registry["my_actor_#{i}"]
350
+ actor.shutdown!
351
+ end
352
+
353
+ ## Linking
354
+
355
+ Linking allows actors to group together so that they all live or die together.
356
+ Such linking is useful for breaking up complex problems into multiple smaller units.
357
+ To create a linked actor you use the ````spawn!```` method.
358
+ By default, if a linked actor dies, it will cause its parent and children to die too.
359
+ You an override this behavior by using supervisors.
392
360
 
393
- # Tell the root actor to create the tree of children.
394
- top.deliver_message!(:spawn)
361
+ # Create some linked actors.
362
+ top = Tribe::Actor.new
363
+ middle = top.spawn!(Tribe::Actor)
364
+ bottom = middle.spawn!(Tribe::Actor)
365
+
366
+ # Force an exception on the middle actor (it has a parent and a child).
367
+ middle.perform! { raise 'uh oh' }
368
+
369
+ # Wait.
370
+ sleep(3)
371
+
372
+ # All actors died together.
373
+ puts "Top: #{top.alive?}: #{top.exception.class}"
374
+ puts "Middle: #{middle.alive?}: #{middle.exception.class}"
375
+ puts "Bottom: #{bottom.alive?}: #{bottom.exception.class}"
395
376
 
396
377
  ## Supervisors
397
378
 
398
379
  A failure in a linked actor will cause all associated actors (parent and children) to die.
399
380
  Supervisors can be used to block the failure from propogating.
400
- You then have the option to re-create the failed actor.
381
+ You then have the option to re-spawn the failed actor.
382
+ They are created by passing ````{:supervise => true}```` as a third argument to ````spawn!````.
383
+ You can then detect dead children by overriding ````on_child_died````.
384
+
385
+ # Create some linked actors.
386
+ top = Tribe::Actor.new
387
+ middle = top.spawn!(Tribe::Actor, {}, {:supervise => true})
388
+ bottom = middle.spawn!(Tribe::Actor)
389
+
390
+ # Force an exception on the middle actor (it has a parent and a child).
391
+ middle.perform! { raise 'uh oh' }
392
+
393
+ # Wait.
394
+ sleep(3)
395
+
396
+ # Top actor lives because it's a supervisor. The other two die.
397
+ puts "Top: #{top.alive?}: #{top.exception.class}"
398
+ puts "Middle: #{middle.alive?}: #{middle.exception.class}"
399
+ puts "Bottom: #{bottom.alive?}: #{bottom.exception.class}"
401
400
 
402
- # Create the top-level actor class.
403
- class Level1 < Tribe::Actor
404
- private
405
- def on_spawn(event)
406
- 5.times do |i|
407
- create_subtree
408
- end
409
- end
401
+ #### Logging exceptions
410
402
 
411
- def create_subtree
412
- actor = spawn(Level2)
413
- message!(actor, :spawn)
414
- end
403
+ It is common practice to log actor exceptions or print them to stdout.
404
+ This is easily accomplished with the ````on_exception```` handler in a base class:
415
405
 
416
- def child_died_handler(actor, exception)
417
- puts "My child (#{actor.identifier}) died. Restarting it."
418
- create_subtree
406
+ class MyBaseActor < Tribe::Actor
407
+ private
408
+ def on_exception(event)
409
+ e = event.data[:exception]
410
+ puts "#{e.class.name}: #{e.message}:\n#{e.backtrace.join("\n")}"
419
411
  end
420
412
  end
421
413
 
422
- # Create the mid-level actor class.
423
- class Level2 < Tribe::Actor
424
- private
425
- def on_spawn(event)
426
- 5.times do |i|
427
- actor = spawn(Level3)
428
- message!(actor, :spawn)
414
+ class CustomActor < MyBaseActor
415
+ end
416
+
417
+ actor = Tribe.root.spawn!(CustomActor)
418
+ actor.perform! { raise 'goodbye' }
419
+
420
+ Note that you should be careful to make sure ````on_exception```` never raises an exception itself.
421
+ If it does, this second exception will be ignored.
422
+ Thus it is best to limit the use of ````on_exception```` to logging exceptions in a common base class.
423
+
424
+ ## Blocking code
425
+
426
+ Occassionally you will have a need to execute blocking code in one of your actors.
427
+ The most common cases of blocking code are network IO, disk IO, database queries, and the ````sleep```` function.
428
+
429
+ Actors have a convenient method named ````blocking!```` that you should use to wrap such code.
430
+ Under the hood this method is expanding and contracting the thread pool to compensate for the blocked thread.
431
+ This will prevent thread pool starvation.
432
+
433
+ The ````blocking!```` method is designed to work with dedicated and non-dedicated actors.
434
+ By using this method in all of your actors, you will make it easy to convert between the two types.
435
+
436
+ An actor's ````wait!```` method (used with futures) already calls ````blocking!```` for you.
437
+
438
+ If for some reason Ruby can't create a new thread, Ruby will raise a ````ThreadError```` and your actor will die.
439
+ Most modern operating systems can support many thousands of simultanous threads so refer to your operating system documentation as you may need to increase the limits.
440
+
441
+ To support in the tens of thousands, hundreds of thousands, or potentially millions of actors, you will need to use non-blocking actors.
442
+
443
+ class MyActor < Tribe::Actor
444
+ private
445
+ def on_start(event)
446
+ blocking! do
447
+ sleep 6
429
448
  end
430
449
  end
450
+
431
451
  end
432
452
 
433
- # Create the bottom level actor class.
434
- class Level3 < Tribe::Actor
435
- private
436
- def on_spawn(event)
437
- puts "#{identifier} says hello world!"
438
- raise 'Sometimes I like to die.' if rand < 0.5
439
- end
453
+ # Print the default pool size.
454
+ puts "Pool size (before): #{Workers.pool.size}"
455
+
456
+ # Spawn some actors that go to sleep for a bit.
457
+ 100.times do
458
+ actor = Tribe.root.spawn!(MyActor)
459
+ actor.direct_message!(:start)
440
460
  end
441
461
 
442
- # Create the top-level actor.
443
- top = Tribe.root.spawn(Level1, :name => 'root')
462
+ # Wait for all of the actors to sleep.
463
+ sleep(2)
464
+
465
+ # The pool size is increased by 100 threads.
466
+ puts "Pool size (during): #{Workers.pool.size}"
444
467
 
445
- # Tell the top-level actor to create the tree of children.
446
- top.deliver_message!(:spawn)
468
+ # Wait for all of the actors to stop sleeping.
469
+ sleep(10)
470
+
471
+ # The pool size is back to the default size.
472
+ puts "Pool size (after): #{Workers.pool.size}"
473
+
474
+ ## Debugging
447
475
 
448
- #### Important!
476
+ Tribe is written in pure Ruby so it will work with all existing debuggers that support Ruby & threads.
477
+ [Byebug] (https://github.com/deivid-rodriguez/byebug) is commonly used with MRI Ruby 2.X and will let you set breakpoints.
449
478
 
450
- Restarting named actors is NOT currently supported, but will be in a future update.
451
- Attempting to do so may result in Tribe::RegistryError exceptions when trying to spawn a replacement child.
479
+ The most common problem you will encounter with actors is that they die due to exceptions.
480
+ You can access the exception that caused an actor to die by calling the ````exception```` method on the actor:
481
+
482
+ actor = Tribe::Actor.new
483
+ actor.perform! { raise 'goodbye' }
484
+ sleep(3)
485
+ e = actor.exception
486
+ puts "#{e.class.name}: #{e.message}:\n#{e.backtrace.join("\n")}"
452
487
 
453
488
  ## Benchmarks
454
489
 
455
- Please see the [performance] (https://github.com/chadrem/tribe/wiki/Performance "performance") wiki page for more information.
490
+ Please see the [performance] (https://github.com/chadrem/tribe/wiki/Performance "performance") wiki page for more information.
456
491
 
457
492
  ## Contributing
458
493
 
459
- 1. Fork it
460
- 2. Create your feature branch (`git checkout -b my-new-feature`)
461
- 3. Commit your changes (`git commit -am 'Added some feature'`)
462
- 4. Push to the branch (`git push origin my-new-feature`)
463
- 5. Create new Pull Request
494
+ Bug reports and pull requests are welcome on GitHub at https://github.com/chadrem/tribe.