Slide 1

Slide 1 text

“Don’t communicate by sharing state; share state by communicating” Monday, March 18, 13

Slide 2

Slide 2 text

’Tis Himself Sean McKibben VP Engineering, Push IO sometimes still does contract work Graphex Corp. Designer => Engineer C# => Ruby Flex => Ember.js Pepsi => Coke Cat => Dog Beer => Whiskey Steelcase => Herman Miller Volkswagen => Subaru @graphex Monday, March 18, 13

Slide 3

Slide 3 text

Stuff that was probably just said Moore’s law and stuff Thread.new is not much fun Evented callbacks R fun and reactors are cool Rewriting libs for eventmachine is not so fun Synchrony is kind of awkward and magicky Languages are fun, especially Ruby (but this isn’t Boulder Erlang Group) Monday, March 18, 13

Slide 4

Slide 4 text

Didn’t EventMachine Tornado Node.Js Already solve this? (I am not going to answer this) Monday, March 18, 13

Slide 5

Slide 5 text

Why actor model? Flexibility Can make threaded stuff and evented stuff, and eveaded and thrented whatnots You can make a reactor thread, and then block it, just for fun Pretty easy to tell how to arrange things Monday, March 18, 13

Slide 6

Slide 6 text

What’s it good for? Sidekiq uses Celluloid to replace many Resque workers with a multi-threaded worker I/O Robotics Dealing with situations that are failure-prone Fastering stuff wif cores Monday, March 18, 13

Slide 7

Slide 7 text

Celluloid is mostly Actor Model Ruby keeps it a bit more OOP-y Adds more features like pools Not a member Screen Actors Guild Random computer science reasons (see: wikipedia) Monday, March 18, 13

Slide 8

Slide 8 text

“Don’t communicate by sharing state; share state by communicating” An actor is its own entity that can send and receive messages. Each actor is addressable by any other actor that knows its identifier Actors can create new actors Monday, March 18, 13

Slide 9

Slide 9 text

Call ’em cells Just like you don’t want mutant cells in your body, don’t mutate objects that have gone through the cell wall as messages. This is how celluloid guarantees thread safety. Monday, March 18, 13

Slide 10

Slide 10 text

But I like calling methods with arguments Cool Each actor gets its own thread Method calls to an actor are actually proxied and wrapped in a fiber/thread But it isn’t awkward like synchrony Finally, some code: Monday, March 18, 13

Slide 11

Slide 11 text

require 'celluloid' class SayActor include Celluloid VOICES = %w(Daniel Emily Fiona Jill Karen Lee Moira Samantha Sangeeta Serena Tessa Tom) def initialize(voice=nil) @voice = voice.blank? ? VOICES.shuffle[0] : voice end def say(text) Benchmark.realtime do `say --voice #{@voice} #{text}` end end end Monday, March 18, 13

Slide 12

Slide 12 text

require 'celluloid' class SayActor include Celluloid VOICES = %w(Daniel Emily Fiona Jill Karen Lee Moira Samantha Sangeeta Serena Tessa Tom) def initialize(voice=nil) @voice = voice.blank? ? VOICES.shuffle[0] : voice end def say(text) Benchmark.realtime do `say --voice #{@voice} #{text}` end end end > SayActor.new.say("Hello World") => 1.151892 Boring... Monday, March 18, 13

Slide 13

Slide 13 text

require 'celluloid' class SayActor include Celluloid VOICES = %w(Daniel Emily Fiona Jill Karen Lee Moira Samantha Sangeeta Serena Tessa Tom) def initialize(voice=nil) @voice = voice.blank? ? VOICES.shuffle[0] : voice end def say(text) Benchmark.realtime do `say --voice #{@voice} #{text}` end end end > SayActor.new.async.say("Hello World"); SayActor.new.async.say("Howdy y'all") => nil Fire and forget But no return value... Monday, March 18, 13

Slide 14

Slide 14 text

require 'celluloid' class SayActor include Celluloid VOICES = %w(Daniel Emily Fiona Jill Karen Lee Moira Samantha Sangeeta Serena Tessa Tom) def initialize(voice=nil) @voice = voice.blank? ? VOICES.shuffle[0] : voice end def say(text) Benchmark.realtime do `say --voice #{@voice} #{text}` end end end > SayActor.new.future.say("Hello World").value => 1.768051 Futures capture values and block on #value Back to blocking... Monday, March 18, 13

Slide 15

Slide 15 text

require 'celluloid' class SayActor include Celluloid VOICES = %w(Daniel Emily Fiona Jill Karen Lee Moira Samantha Sangeeta Serena Tessa Tom) def initialize(voice=nil) @voice = voice.blank? ? VOICES.shuffle[0] : voice end def say(text) Benchmark.realtime do `say --voice #{@voice} #{text}` end end end > f = [] > f << SayActor.new.future.say("Howdy y'all") > f << SayActor.new.future.say("Hello World") > f.sum(&:value) => 3.27545 Still Boring... Fully asynchronous with return values Monday, March 18, 13

Slide 16

Slide 16 text

> speakers = 100.times.map{SayActor.new} > speakers.each_with_index{|s,i|s.async.say(i)} But I have a lot of work Monday, March 18, 13

Slide 17

Slide 17 text

> speakers = 1000000.times.map{SayActor.new} > speakers.each_with_index{|s,i|s.async.say(i)} no, a lot of work Monday, March 18, 13

Slide 18

Slide 18 text

> speakers = 1000000.times.map{SayActor.new}; speakers.each_with_index{|s,i|s.async.say(i)} ThreadError: can't create Thread (35) Just kidding Monday, March 18, 13

Slide 19

Slide 19 text

> speaker_pool = SayActor.pool(:size => 5) => # > speaker_pool.say("hello") => 1.379268 > speaker_pool = SayActor.pool(:size => 30) > speaker_pool.future.say("hi") => # > future.value => 0.792455 > futures = 100.times.map{|i| speaker_pool.future.say(i)}; futures.sum(&:value) => 183.451935 Pools Monday, March 18, 13

Slide 20

Slide 20 text

Lifecycle Actors aren’t garbage collected > Celluloid::Actor.all.compact.to_set.select(&:alive?).length => 24 Must call #terminate Monday, March 18, 13

Slide 21

Slide 21 text

Registry Global directory of actors, so you don’t have to pass around instances > Celluloid::Actor[:speaker_pool] = SayActor.pool(:size => 30) > Celluloid::Actor[:speaker_pool].say("hello world") => 1.593524 Monday, March 18, 13

Slide 22

Slide 22 text

Self Celluloid wraps your class so never pass ‘self’ to other actors. Use Celluloid::Actor.current (or if you are in your actor, just Actor.current) to pass the celluloid proxy object instead Monday, March 18, 13

Slide 23

Slide 23 text

Supervisors Supervisors resurrect dead actors when they crash by creating a new instance. > sup = SayActor.supervise_as :overactor => # 1.245053 > Celluloid::Actor[:overactor].last_words("ahhhghhh") SayActor crashed! RuntimeError: ahhhghhhh > Celluloid::Actor[:overactor].say("Hello") => 0.777873 > Celluloid::Actor[:overactor].last_words("ahhhghhh") SayActor crashed! RuntimeError: ahhhghhhh > Celluloid::Actor[:overactor].say("Hello") => 0.735309 > Celluloid::Actor[:overactor].last_words("ahhhghhh") SayActor crashed! > Celluloid::Actor[:overactor].say("Hello") => 0.794133 Monday, March 18, 13

Slide 24

Slide 24 text

Gotchas Be on the lookout for thread unsafety Don’t mutate the state of objects you pass to other actors (sometimes it is good to marshall data) You shouldn’t need fibers and threads and mutexes any more... Monday, March 18, 13

Slide 25

Slide 25 text

Lots more Supervision Groups Mailbox Messaging Linking Signaling Exclusive Blocks Monday, March 18, 13

Slide 26

Slide 26 text

Even More Celluloid::IO DCell Reel Lattice Monday, March 18, 13