+ - 0:00:00
Notes for current slide
Notes for next slide

Message passing for actors and humans

1 / 62

Hi

name - Peter Saxton

@internets - CrowdHailer

@works - paywithcurl.com

2 / 62

3 / 62

lets talk about,

The Actor model

4 / 62

Actors

  • Universal primitive of concurrent computation
  • Communicate via asynchronous message passing
  • React to messages by making local decisions

First proposed by Carl Hewitt in 1973

5 / 62

6 / 62

Universal primitive

  • An actor has three essential elements:
    1. Processing
    2. Storage
    3. Communication
  • Everything is an actor
7 / 62

Asynchronous message passing

  • No delivery guarantees
  • No order guarantees

Similar to original Object Oriented programming (OOP)

8 / 62

Local decisions

  • No shared/global state
  • In response to a message, an actor may:
    1. Create more actors
    2. Send messages to other Actors that it has addresses for
    3. Designate how the Actor is going to handle the next message it receives
9 / 62

The Actor gets to decide what it wants to do with incoming messages. Example: A chicken Actor doesn’t have to agree to cut off its own head. https://gist.github.com/rbishop/9082539

In the wild

  • Elixir
  • erlang
  • Akka (JVM)
10 / 62

Let's build our own

in JavaScript

11 / 62

The simplest actor

var state = 0
while (true) {
const message = await mailbox.receive()
state = state + 1
}
// somewhere else
mailbox.deliver(message)
12 / 62

You might have a messge to stop things but we won't worry about that here block on receive it't the only input for an actor

Blocking mailbox

function Mailbox () {
var messages = [], awaiting = undefined
function receive () {
return new Promise(function (resolve) {
if (next = messages.shift()) {
resolve(next)
} else {
awaiting = resolve
}
})
}
async function deliver (message) {
messages.push(message)
if (awaiting) {
awaiting(messages.shift())
awaiting = undefined
}
}
return {receive: receive, deliver: deliver}
}
13 / 62

General purpose actor

function init () { return 0 }
function handle (message, count) {
return count + 1
}
var state = init()
while (true) {
const message = await mailbox.receive()
state = handle(message, state)
}
14 / 62

Starting actors

function Actor (init, handle) {
const mailbox = Mailbox()
(async function run () {
var state = init()
while (true) {
const message = await mailbox.receive()
state = handle(message, state)
}
})()
return {deliver: mailbox.deliver}
}

Guarantees only this actor is able to receive from the mailbox

15 / 62

third argument could be self

Actor system - requirements

Actors specify a concurrent program. To run the program requires an Actor System that handles.

  • Allocating addresses
  • Delivering messages
  • Scheduling actors
16 / 62

Actor System

const actors = []
function start (init, handle) {
return actors.push(Actor(init, handle)) - 1
}
async function deliver (address, message) {
actors[address].dispatch(message)
}
ActorSystem = {start, deliver}
17 / 62

Actor system - trade offs

  • Concurrent (not parallel)
  • Cooperative (not preemtive)
  • Voluntary (not obligatory)
18 / 62

19 / 62

Cooperative vs Preemtive

  • Cooperative - processes must yield control.
  • Preemtive - a process can be stoped at any time.
const message = mailbox.receive()
// greedy process
while (true) {
// run forever
}
20 / 62

Voluntary vs Mandatory

// send a mutable message
const message = []
ActorSystem.dispatch(actor, message)
// later
message.push('surprise')
const message = mailbox.receive()
// use global state
window.message = message
21 / 62

Example: Ping pong

// actor behaviour
function init () { return null }
function handle ({type, address}, state) {
if (type == 'ping') {
ActorSystem.dispatch(address, {type: 'pong'})
} else {
console.log('Received Pong!')
}
return state
}
// run
const a1 = ActorSystem.start(init, handle)
const a2 = ActorSystem.start(init, handle)
ActorSystem.dispatch(a1, {type: 'ping', address: a2})
22 / 62

Why?

23 / 62

Where is shared memory

24 / 62

Your distributed system

  • High availability
  • client and server
  • backups failover
  • multicore

Sending data ALWAYS has latency, and is unreliable.

25 / 62

Programming is parallel

26 / 62

27 / 62

“My first message is that concurrency is best regarded as a program structuring principle”

Tony Hoare

28 / 62

Packing huge big rocks into containers is very very difficult, but pouring sand into containers is really easy. If you think of processes like little grains of sand and you think of schedulers like big barrels that you have to fill up, filling your barrels up with sand, you can pack them very nicely, you just pour them in and it will work.

Joe Armstrong

29 / 62

ActorSystem2

navigator.hardwareConcurrency
// 4
new Worker('./actor-system.js')

Left as an exercise for the reader.

30 / 62

Abstracted communication

31 / 62

32 / 62

Many to Many relationship among Actors and Addresses.

$ dig +short google.com
216.58.204.14
33 / 62

Descriptive side effects

var state = init()
while (true) {
const message = await mailbox.receive()
{outbound, state} = handle(message, state)
outbound.forEach(doSend)
}

handle can now be a totally pure function

Session types

http://www.di.unito.it/~dezani/papers/sto.pdf

34 / 62

35 / 62

HTTP is message passing

The Hypertext Transfer Protocol (HTTP) is a stateless application- level request/response protocol that uses extensible semantics and self-descriptive message payloads for flexible interaction with network-based hypertext information systems.

Let's build a server

36 / 62

Mandatory actor system

The Erlang view of the world is that everything is a process and that processes can interact only by exchanging messages.

Joe Armstrong

37 / 62

What is Raxx?

  1. Elixir interface for HTTP servers, frameworks (and clients)
  2. Toolkit for web development
  3. Simple streaming support

and Ace?

A server to run Raxx applications

  1. HTTP/2 + HTTPS, by default
  2. Isolated message exchanges
38 / 62

Walking tour of Ace

We do not have ONE web-server handling 2 millions sessions. We have 2 million webservers handling one session each.

Managing Two Million Webservers

Joe Armstrong

39 / 62

40 / 62

41 / 62

42 / 62

GenServer

defmodule MyServer do
use GenServer
def handle_call(:request, _from, state) do
{:reply, :response, state}
end
end
43 / 62

Raxx.SimpleServer

defmodule Greetings do
use Raxx.Server
def handle_request(
_request,
_state)
do
%Raxx.Response{status: 200,
headers: ["content-type", "text/plain"]
body: "Hello, World!"}
end
end
44 / 62

Raxx.SimpleServer

defmodule Greetings do
use Raxx.Server
def handle_request(
_request,
_state)
do
response(:ok)
|> set_header("content-type", "text/plain")
|> set_body("Hello, World!")
end
end
45 / 62

Raxx.SimpleServer

defmodule Greetings do
use Raxx.Server
def handle_request(
%{path: ["name", name]},
_state)
do
response(:ok)
|> set_header("content-type", "text/plain")
|> set_body("Hello, #{name}!")
end
end
46 / 62

Raxx.SimpleServer

defmodule Greetings do
use Raxx.Server
def handle_request(
%{path: ["name", name]},
%{greeting: greeting})
do
response(:ok)
|> set_header("content-type", "text/plain")
|> set_body("#{greeting}, #{name}!")
end
end
47 / 62

What about streaming?

48 / 62
tail | data(1+) | head(request) -->
Client ============================================ Server
<-- head(response) | data(1+) | tail
49 / 62
defmodule Upload do
use Raxx.Server
def handle_head(%{path: ["upload"] body: true}, _) do
{:ok, io_device} = File.open("my/path")
{[], {:file, device}}
end
def handle_data(data, state = {:file, device}) do
IO.write(device, data)
{[], state}
end
def handle_tail(_trailers, state) do
response(:see_other)
|> set_header("location", "/")
end
end
50 / 62

The Raxx toolkit

  • Routing ✔️
  • Middleware ✔️
  • Templates ✔️ (EExHTML)
  • Code reloading ✔️ (Raxx.Kit)
  • Project generators ✔️ (Raxx.Kit)
51 / 62

Next?

52 / 62

GenBrowser

GenBrowser treats clients as just another process in one continuous, if widely distributed, system. Every client gets an address to which messages can be dispatched; and a mailbox where messages are delivered.

53 / 62

Actor lifecycle

  1. Client joins, it is not started.

    const client = await GenBrowser.start('http://localhost:8080')
    const {address, mailbox, send, communal} = client
    console.log(address)
    // "g2gCZA ..."
  2. Disconnected clients are not dead.

54 / 62

Messages from the server

message = %{
"type" => "ping",
"from" => GenBrowser.Address.new(self())
}
GenBrowser.send("g2gCZA ...", message)
receive do
message ->
IO.inspect(message)
end
# => %{"type" => "pong"}
55 / 62

Messages from a client

client.send("g2gCZA ...", {type: 'ping', from: client.address})
const reply = await client.mailbox.receive({timeout: 5000})
console.log("Pong received")
56 / 62

Guarantees

  • There should be only one actor per mailbox
    • Reconnection requires a cursor signed by the server
  • Addresses are unforgable
    • Addresses are all signed by the server
    • Object capability model
57 / 62

Try it out

# Plug/Phoenix integration
communal = %{myProcess: GenBrowser.Address.new(MyNamedProcess)}
plug GenBrowser.Plug, communal: communal
# Docker playground
docker run -it -e SECRET=s3cr3t -p 8080:8080 gen-browser
58 / 62

What's behind an address?

{:via, GenBrowser, "abc123"}

{:via, IOTSensor, "lightbulb"}
{:via, PersistantActor, "4ever"}
{:via, EmailService, "bob@example.com"}
59 / 62

Humans

60 / 62

Humans

  • Universal primitive of concurrent computation
  • Communicate via asynchronous message passing
  • React to messages by making local decisions
61 / 62

Only difference is they are non deterministic I'm looking for communication protocol from early navys

Thank you

See the code

Comments and questions

62 / 62

Hi

name - Peter Saxton

@internets - CrowdHailer

@works - paywithcurl.com

2 / 62
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow