+ All Categories
Home > Engineering > Casting for not so strange actors

Casting for not so strange actors

Date post: 27-Jul-2015
Category:
Upload: zucaritask
View: 214 times
Download: 3 times
Share this document with a friend
Popular Tags:
83
CASTING FOR NOT • SO • STRANGE ACTORS MARIANO VALLÉS @zucaritask
Transcript

CASTING FOR NOT • SO • STRANGE ACTORS

MARIANO VALLÉS@zucaritask

At Wooga (mobile games):

Long running “player” sessions

Stateless backends are not enough

State is user centered: Good fit for JSON

At Wooga (mobile games):

Long running “player” sessions

Stateless backends are not enough

State is user centered: Good fit for JSON

Building Games

At Wooga (mobile games):

Long running “player” sessions

Stateless backends are not enough

State is user centered: Good fit for JSON

Building Games / Backends

At Wooga (mobile games):

Long running “player” sessions

Stateless backends are not enough

State is user centered: Good fit for JSON

Building Games / Backends

player sessions as events

At Wooga (mobile games):

Long running “player” sessions

Stateless backends are not enough

State is user centered: Good fit for JSON

Building Games / Backends

player sessions as events

stateless is not enough

THE MILLION DOLLAR IDEA

THE MILLION DOLLAR IDEA

New Backend project

THE MILLION DOLLAR IDEA

New Backend project

USE ACTORS

WHAT ACTORS REALLY ARE

WHAT ACTORS REALLY ARE

LIGHTWEIGHT CONCURRENT PROCESSES

WHAT ACTORS REALLY ARE

LIGHTWEIGHT CONCURRENT PROCESSES COMMUNICATING THROUGH MESSAGE PASSING

WHAT ACTORS REALLY ARE

LIGHTWEIGHT CONCURRENT PROCESSES COMMUNICATING THROUGH MESSAGE PASSING THAT ENCAPSULATE STATE

WHAT ACTORS REALLY ARE

LIGHTWEIGHT CONCURRENT PROCESSES COMMUNICATING THROUGH MESSAGE PASSING THAT ENCAPSULATE STATE *bye BYE Threads, LockS, LATCHES..

WHAT ACTORS REALLY ARE

LIGHTWEIGHT CONCURRENT PROCESSES COMMUNICATING THROUGH MESSAGE PASSING THAT ENCAPSULATE STATE *bye BYE Threads, LockS, LATCHES..

PLEASE WELCOME:

Supervisors

Let it crash

Async calls

At most once messages...etc

A WAY TO GET THE FEELING OF:

THE PROTOTYPE

THE PROTOTYPE

HTTP KEY-VALUE STORE

(USER w/STATE)

HTTP

A WAY TO GET THE FEELING OF:

HTTP KEY-VALUE STORE

(USER w/STATE)

HTTP

AND GET THE FEELING OF:

THE PROTOTYPE

HTTP KEY-VALUE STORE

(USER w/STATE)

HTTP

AND GET THE FEELING OF:

JSON PARAMS/RESPONSES

THE PROTOTYPE

HTTP KEY-VALUE STORE

(USER w/STATE)

HTTP

AND GET THE FEELING OF:

JSON PARAMS/RESPONSES PERSISTING STATE (AWS S3)

THE PROTOTYPE

HTTP KEY-VALUE STORE

(USER w/STATE)

HTTP

AND GET THE FEELING OF:

JSON PARAMS/RESPONSES PERSISTING STATE (AWS S3)

WRITING TESTS

THE PROTOTYPE

THE TALENTED CANDIDATES

THE AUDITION

TSUNG SIMULATION

THE AUDITION

TSUNG SIMULATION

5 MINUTES LONG

THE AUDITION

TSUNG SIMULATION

1 NEW SESSION

EVERY 0.05s

5 MINUTES LONG

THE AUDITION

TSUNG SIMULATION

1 NEW SESSION

EVERY 0.05s

5 MINUTES LONG

THE AUDITION

~47K calls

HTTP

alice

create: alice

HTTP

alice

put(alice, {“level”: 1})

alice ! {update, {“level”: 1}}

HTTP

alice

get(alice)

alice ? state

HTTP

alice

bob

carl

dan

get(alice)

alice

Sinatra/Puma User (Actors)

bob

RegistryPOST or GET

class User include Celluloid

TIMEOUT = 15

def initialize(id) @id = id after(TIMEOUT) { terminate } end

def state=(state) @state = state end

def state @state endend

class User include Celluloid

TIMEOUT = 15

def initialize(id) @id = id after(TIMEOUT) { terminate } end

def state=(state) @state = state end

def state @state endend

class User include Celluloid

TIMEOUT = 15

def initialize(id) @id = id after(TIMEOUT) { terminate } end

def state=(state) @state = state end

def state @state endend

require 'celluloid/autostart'

class Registry class << self def create(id) supervisor = User.supervise_as(id.to_sym, id) supervisor.actors.first end

def lookup(id) user = Celluloid::Actor[id.to_sym] user ||= create(id) end

endend

require 'celluloid/autostart'

class Registry class << self def create(id) supervisor = User.supervise_as(id.to_sym, id) supervisor.actors.first end

def lookup(id) user = Celluloid::Actor[id.to_sym] user ||= create(id) end

endend

global actor registry

THE TSUNG SIMULATION RUNS FOR 5 MINUTES

THE TSUNG SIMULATION RUNS FOR 5 MINUTES

YOU WON’T BELIEVE WHAT HAPPENS NEXT

CRASH

E, [2015-03-11T20:53:14.981000 #11076] ERROR -- : Actor crashed!ThreadError: unable to create new native thread

org/jruby/RubyThread.java:440:in `initialize'org/jruby/RubyThread.java:389:in `new'

github.com/wooga/wesWESBUILD ACTOR BASED SERVICES IN ERLANG

github.com/wooga/wesWES

CHANNELSUBSCRIBED ACTORS

github.com/wooga/wesWES

CHANNELSUBSCRIBED ACTORS

github.com/wooga/wesWES

command

CHANNELSUBSCRIBED ACTORS

alice

channel actor

bob

DB (s3, ets,

null)

-module(wes_user).-behaviour(wes_actor).-export([init/1, command/3, command/4, key/1, to_struct/2, from_struct/2]).

-record(user_state, {id, data}).

init([Id]) -> { ok, #user_state{ id = Id, data = [ ] } }.

command(_, get, {Id}, #user_state{id = Id} = State) -> {reply, State#user_state.data, State};

key(Id) -> <<"id", Id/binary>>.

to_struct(_Actorname, #user_state{id = Id, data = Data}) -> EncodedData = jiffy:encode({Data}), jiffy:encode({[{id, Id}, {data, EncodedData}]}).

from_struct(_Key, Value) -> {Props} = jiffy:decode(Value), {_, Id} = lists:keyfind(<<"id">>, 1, Props), {_, EncodedData} = lists:keyfind(<<"data">>, 1, Props),

{Data} = jiffy:decode(EncodedData),

-module(wes_user).-behaviour(wes_actor).-export([init/1, command/3, command/4, key/1, to_struct/2, from_struct/2]).

-record(user_state, {id, data}).

init([Id]) -> { ok, #user_state{ id = Id, data = [ ] } }.

command(_, get, {Id}, #user_state{id = Id} = State) -> {reply, State#user_state.data, State};

key(Id) -> <<"id", Id/binary>>.

to_struct(_Actorname, #user_state{id = Id, data = Data}) -> EncodedData = jiffy:encode({Data}), jiffy:encode({[{id, Id}, {data, EncodedData}]}).

from_struct(_Key, Value) -> {Props} = jiffy:decode(Value), {_, Id} = lists:keyfind(<<"id">>, 1, Props), {_, EncodedData} = lists:keyfind(<<"data">>, 1, Props),

{Data} = jiffy:decode(EncodedData),

-module(wes_user).-behaviour(wes_actor).-export([init/1, command/3, command/4, key/1, to_struct/2, from_struct/2]).

-record(user_state, {id, data}).

init([Id]) -> { ok, #user_state{ id = Id, data = [ ] } }.

command(_, get, {Id}, #user_state{id = Id} = State) -> {reply, State#user_state.data, State};

key(Id) -> <<"id", Id/binary>>.

to_struct(_Actorname, #user_state{id = Id, data = Data}) -> EncodedData = jiffy:encode({Data}), jiffy:encode({[{id, Id}, {data, EncodedData}]}).

from_struct(_Key, Value) -> {Props} = jiffy:decode(Value), {_, Id} = lists:keyfind(<<"id">>, 1, Props), {_, EncodedData} = lists:keyfind(<<"data">>, 1, Props),

{Data} = jiffy:decode(EncodedData),

alice

Plug/Cowboy Agents

bob

Registry

GenServer

defmodule User do

def start_link do Agent.start_link(fn -> HashDict.new end) end

def get(user, key) do Agent.get(user, &HashDict.get(&1, key)) end

def put(user, key, value) do Agent.update(user, &HashDict.put(&1, key, value)) end

def delete(bucket, key) do Agent.get_and_update(bucket, &HashDict.pop(&1, key)) endend

defmodule User do

def start_link do Agent.start_link(fn -> HashDict.new end) end

def get(user, key) do Agent.get(user, &HashDict.get(&1, key)) end

def put(user, key, value) do Agent.update(user, &HashDict.put(&1, key, value)) end

def delete(bucket, key) do Agent.get_and_update(bucket, &HashDict.pop(&1, key)) endend

Agents

Wes with

alice

Plug/Cowboy WesActors

bob

Registry

GenServer

Wes Storage

defmodule User do @behaviour :wes_actor

def init([id]) do {:ok, %{id: id, data: %{} }} end

def command(_session, :update, {user_id, {key, value}}, state) do new_state = %{ state | :data => Dict.put(state.data, key, value)} {:ok, new_state} end

def command(_session, :get, user_id, state) do {:reply, state.data, state} end

def key(id) do "#{id}" end

def to_struct(_actor_name, state) do {:ok, state} = Jazz.encode(state) state end

def from_struct(_key, value) do

Wes with

defmodule User do @behaviour :wes_actor

def init([id]) do {:ok, %{id: id, data: %{} }} end

def command(_session, :update, {user_id, {key, value}}, state) do new_state = %{ state | :data => Dict.put(state.data, key, value)} {:ok, new_state} end

def command(_session, :get, user_id, state) do {:reply, state.data, state} end

def key(id) do "#{id}" end

def to_struct(_actor_name, state) do {:ok, state} = Jazz.encode(state) state end

def from_struct(_key, value) do

Wes with

TOOLING: MIX, HEX, DOCS

TOOLING: MIX, HEX, DOCS ERLANG INTEROPERABILITY

TOOLING: MIX, HEX, DOCS ERLANG INTEROPERABILITY ADDITIONS ON TOP PLUG(Rack), ENUMERABLE, STREAMS…

alice

Spray Akka Actors

bob

S3 Storage

case class StateUpdate(state: Map[String, String])case class StateGet() class User(userId:String) extends Actor { var userState:Map[String, String] = Map() def receive = { case StateUpdate(newState) => { newState.foreach { case (key, value) => userState += (key -> value) } } case StateGet() => { sender ! userState } case ReceiveTimeout => { val storage = context.actorSelection("/user/storage") storage ! SaveUserState(userId, userState) self ! akka.actor.PoisonPill } }}

case class StateUpdate(state: Map[String, String])case class StateGet() class User(userId:String) extends Actor { var userState:Map[String, String] = Map() def receive = { case StateUpdate(newState) => { newState.foreach { case (key, value) => userState += (key -> value) } } case StateGet() => { sender ! userState } case ReceiveTimeout => { val storage = context.actorSelection("/user/storage") storage ! SaveUserState(userId, userState) self ! akka.actor.PoisonPill } }}

"A UserActor" should "be able to set a new value in its state" in { val user = TestActorRef(Props[User]) val update = Map("foo"-> "bar") user ! StateUpdate(update) user.underlyingActor.asInstanceOf[User] userState should be(update) }

A TEST:

"A UserActor" should "be able to set a new value in its state" in { val user = TestActorRef(Props[User]) val update = Map("foo"-> "bar") user ! StateUpdate(update) user.underlyingActor.asInstanceOf[User] userState should be(update) }

A TEST:

AND THE WINNER IS…

AND THE WINNER IS… (for ouR PROJECT)

AKKA/SCALA

AKKA/SCALA

toolset

AKKA/SCALA

toolset Maturity of Akka

AKKA/SCALA

toolset Maturity of Akka

COMMUNITY & DOCS

AKKA/SCALA

toolset Maturity of Akka

COMMUNITY & DOCS JVM

AKKA/SCALA

toolset Maturity of Akka

COMMUNITY & DOCS JVM

AKKA IS DESIGNED FOR ACTORS

AN INFORMED DECISION IS

TAKEAWAY:

BETTER

CASTING FOR NOT • SO • STRANGE ACTORS

MARIANO VALLÉS@zucaritask


Recommended