2. Agenda
● Einführung
● Cloud Haskell Architektur
● Cloud Haskell API
● Serialisierung
● Beispiel Anwendung
● Zusammenfassung
3. Einführung
Was ist Cloud Haskell?
"Eine DSL für die Entwicklung von
Programmen für verteilte, nebenlä ufige
Systeme in Haskell."
● Implementiert als Haskell Library
○ Nur minimale Spracherweiterung
● Inspieriert durch und sehr nah angelehnt an
Erlang
○ Message-passing Communication Model
○ Ausfall und Wiederherstellung Strategien
○ Slogan: "Erlang for Haskell (as a library)"
4. Einführung
Was ist Cloud Haskell?
"Eine DSL für die Entwicklung von
Programmen für verteilte, nebenlä ufige
Systeme in Haskell."
● Implementiert als Haskell Library
○ Nur minimale Spracherweiterung
● Inspieriert durch und sehr nah angelehnt an
Erlang
○ Message-passing Communication Model
○ Ausfall und Wiederherstellung Strategien
○ Slogan: "If in doubt, do it the way Erlang does it"
5. Einführung
Was ist Cloud Haskell?
"Eine DSL für die Entwicklung von
Programmen für verteilte, nebenlä ufige
Systeme in Haskell."
● Erlang's Modell erweitert durch Haskell
○ Function "purity", Typen und Monaden
○ Verwendung von Concurrent Haskell (forkIO etc.)
○ Shared-memory concurrency
6. ● Programmierung von Clustern als ganzes
○ Amazon EC2, Google AppEngine, MS Azure
○ Eigene Rechner im lokalen Netzwerk
○ Oder etwa ein paar separate Prozesse auf einem
Multicore-Prozessor Maschine
■ Separate Prozesse haben getrennte Heaps /
Garbage Collector
■ Bessere Skalierung möglich
○ Verteilter Speicher, nicht geteilt
■ Indepentend Failure
Einführung
Key Idea & Verwendung
7. Einführung
Was CLOUD Haskell nicht bietet
● Es parallelisiert nicht automatisch Abläufe
○ Es existieren keine Funktionen wie etwa
mapReduce, map oder reduce
● Es wird nicht automatisch Infrastruktur
konfigurieren
● ...
8. ● Explizite Nebenläufigkeit
● Leichtgewichtige Prozesse
○ Aufbauend auf leichtgewichtigen Threads
● Kein gemeinsamer Zustand zwischen
Prozessen
● Asynchrones message passing
Einführung
Programming Model
Prozess A
Node 1
Prozess B
Node 2
Thread
Thread
Thread
Thread
Thread
9. Einführung
Programming Model
● Explizite Nebenläufigkeit
● Leichtgewichtige Prozesse
○ Aufbauend auf leichtgewichtigen Threads
● Kein gemeinsamer Zustand zwischen
Prozessen
● Asynchrones message passing
"Actor Model"
10. Einführung
Verteilte Programmierung vs. nebenläufiger
Kommunikationskosten Ausfall bezogen auf
einzelne Prozesse
Globale Konsistenz
Nebenläufige Gering Nicht vorhanden Erreichbar durch
shared memory
Verteilte Hoch Einzelne Nodes
können Ausfallen
Nicht einfach möglich
11. ● Papers
○ Toward Haskell in the Cloud - Jeff Epstein, Andrew
Black, and Simon Peyton Jones
○ Functional programming for the data centre - Jeff
Epstein. Masters Thesis, University of Cambridge
○ A Unified Semantics for Future Erlang - Hans
Svensson, Lars-Ake Fredlund and Clara Benac
Earle
● Prototyp
○ reomte package - Jeff Epstein
● Implementation
○ distributed-process - Well-Typed
Einführung
Implemantationen
13. Cloud Haskell Architektur
Network transport layer
● Repräsentiert das Interface zwischen der
Netzwerkschicht und dem Prozess Layer
● Ermöglicht einfache Integration
unterschiedlicher Netzwerkarten
○ TCP/IP
○ Unix pipes (In Arbeit)
○ CCI - HPC Networking Lib für Infiband (In Arbeit)
○ Weitere gewünscht
■ Shared memory, SSH, UDP, TCP with SSL/TLS
14. Cloud Haskell Architektur
Prozess Layer
● Eine Node verwaltet eine Menge von
Prozessen
● Jeder Prozess findet in einem Haskell
Thread statt
● Jeder Prozess hat seine eigene Message
Queue
● Ein Thread per Node für Events
● Ein Thread per Node für den Node
Controller
● Weitere Prozesse
○ Logging Porzess per Node
15. Cloud Haskell Architektur
Backend Implementation - SimpleLocalnet
● Einfaches Backend ohne große
Konfiguration
● TCP basiert im lokalen Netz
● Node Discovery per lokalem UDP Multicast
16. Cloud Haskell API
Basis
instance Monad Process
instance MonadIO Process
-- | Process identifier
data ProcessId = ProcessId { ... }
-- | Node identifier
data NodeId = NodeId { ... }
-- | Objects that can be sent across the network
class (Binary a, Typeable a) => Serializable a
● Process Monade
○ Alle verteilten Tätigkeiten finden in Process statt
○ Layer über der MonadIO
○ Ausführung von IO über liftIO
17. Cloud Haskell API
Process Managment
● Erzeugung eines Prozesses
○ spawn => Synchrones starten eines Prozesses
■ spawnAsync
○ Explizite Platzierung spawn auf einer Node
○ Funktionen werden als Closure's über Netzwerk
gesendet
● Transport
○ Der zu verwendende Transport Layer
spawn :: NodeId -> Closure (Process ()) -> Process ProcessId
newLocalNode :: Transport -> RemoteTable -> IO ()
terminate :: Process a
getSelfPid :: Process ProcessId
getSelfNode :: Process NodeId
18. Cloud Haskell API
Process Managment
● Erzeugung eines Prozesses
○ spawn => Synchrones starten eines Prozesses
■ spawnAsync
○ Explizite Platzierung spawn auf einer Node
○ Funktionen werden als Closure's über Netzwerk
gesendet
● RemoteTable
○ Öffentliche Schnittstelle des Nodes
spawn :: NodeId -> Closure (Process ()) -> Process ProcessId
newLocalNode :: Transport -> RemoteTable -> IO ()
terminate :: Process a
getSelfPid :: Process ProcessId
getSelfNode :: Process NodeId
19. Cloud Haskell API
Message Passing
● Nur "Serializable" Daten können gesendet
werden
○ Einige Daten können bewusst nicht serialisiert
werden wie etwa File handle, MVars...
● Senden schlägt niemals fehl
● send kann blockieren, Rückkehr impliziert
jedoch keine Zustellung
○ Abhilfe: durch Bestätigung
-- | Send a message
send :: Serializable a => ProcessId -> a -> Process ()
-- | Wait for a message of a specific type
expect :: Serializable a => Process a
send pid message
ack <- expect
20. Cloud Haskell API
Message Passing
● Versand der Nachricht ist verlässlich und
geordnet - garantiert durch CH
○ Prozess A -> [m1, m2, m3] -> Prozess B
○ Prozess B erhält entweder alle drei Nachrichten oder
ein Prefix davon
○ Nicht: [m1, m3] oder [m1, m3, m2]
-- | Send a message
send :: Serializable a => ProcessId -> a -> Process ()
-- | Wait for a message of a specific type
expect :: Serializable a => Process a
21. Cloud Haskell API
Message Passing
● Message Queue kann jegliche Art von
Nachrichten-Typen beinhalten
● expect liefert dabei nächste Nachricht aus
der Message Queue
○ Erste des gewünschten Typs
○ Entscheidung durch Kontext, etwa (msg :: Int)
○ Rest verbleibt dabei in der Message Queue
-- | Send a message
send :: Serializable a => ProcessId -> a -> Process ()
-- | Wait for a message of a specific type
expect :: Serializable a => Process a
23. Cloud Haskell API
Advanced Messaging
● Zur Erinnerung:
○ => Rest verbleibt in der Message Queue
● Lösung
○ Angabe einer Liste von von Handler Funktionen
○ Jede Funktion für einen Nachrichtentyp zuständig
○ Iteration über die Message Queue und testen ob für
Nachrichtentyp eine Handler Funktionen existiert
24. Cloud Haskell API
Advanced Messaging
● receiveWait
○ Iteration über die verschidene Handler erfolgt
anhand der Reihenfolge der Liste
-- | Test the matches in order against each message in the queue
receiveWait :: [Match b] -> Process b
-- | Like 'receiveWait' but with a timeout.
receiveTimeout :: Int -> [Match b] -> Process (Maybe b)
-- | Match against any message of the right type
match :: forall a b. Serializable a => (a -> Process b) -> Match b
-- | Match against any message of the right type that satisfies a predicate
matchIf :: forall a b. Serializable a => (a -> Bool) -> (a -> Process b) -> Match b
-- | Remove any message from the queue
matchUnkown :: Process b -> Match b
26. Cloud Haskell API
Typed Channels
● Bisher wurden Messages direkt zu
Prozessen geschickt
○ Wie in Erlang
● Dynamische type checking der Messages in
der Message Queue ist etwas Haskell
untypisch
● Alternative bieten Typed Channels
27. Cloud Haskell API
Typed Channels
● Verwendung statischer Typisierung zur
Sicherstellung, dass Prozesse ankommende
Messages unterstützen
● Messages werden über Channels speziell
für einen bestimmten Typ gesendet
● Ein Channel besteht aus zwei Endpunkten
○ SendPort a, ReceivePort a
28. Cloud Haskell API
Typed Channels
● SendPort a ist Serializable
● ReceivePort ist nicht Serializable
○ Der Endpunkt darf sich nicht ändern oder dupliziert
werden
data SendPort a -- instance of Typeable, Binary
data ReceivePort a
-- | Create a new typed channel
newChan :: Serializable a => Process (SendPort a, ReceivePort a)
-- | Send a message on a typed channel
sendChan :: Serializable a => SendPort a -> a -> Process ()
-- | Wait for a message on a typed channel
receiveChan :: Serializable a => ReceivePort a -> Process a
-- | Merge a list of typed channels
mergePortBiased :: Serializable a => [ReceivePort a] -> Process (ReceivePort a)
29. Cloud Haskell API
Typed Channels
● Verleitet zu einem bestimmten
Entwurfsmuster
○ SendPorts werden als Messages versendet
○ Dienen als privater Antwort-Channel
○ Wirkt teils wie RPC
■ Server können jedoch auch SendPort
weiterversenden
● Absender ist stets bekannt
● Unpraktisch falls Prozess gestartet wird und
anschließend diesem eine Message
geschickt werden soll
○ => ReceivePort
31. ● Erinnerung
○ Keine Exception beim Nachrichtenversand
● In verteilten Systemen generell schwierig
○ Fehler existieren an jeder Stelle
● Beim Auftreten einer Exception wird nur der
jeweilige Prozess beendet
● Notwendigkeit Prozesse zu überwachen
○ Informiere interessierte Prozesse
Cloud Haskell API
Process Monitoring & Error Handling
32. Cloud Haskell API
Error Handling in Erlang
● Philosophie
○ Versuche nicht zu feingranulares Error Handling zu
programmieren
○ Bedenke den Fall das eine Node crashed
○ Lasse den Prozess crashen
○ Falls ein Prozess crashed, wird dieser durch ein
Supervisor in einem wohl definierten Zustand erneut
gestartet
○ Wisse wo der Zustand sich befindet und wie damit
ein wohl definierter Zustand erreicht werden kann
33. Cloud Haskell API
Process Monitoring & Error Handling
● link
○ Unidirektionale Verbindung
○ all or nothing
○ Vorsicht Aufrufe sind asynchron
■ in Kombination mit unlink kann zu race
condition führen
■ Lösung => monitor
-- | Link to a remote process (asynchronous)
link :: ProcessId -> Process ()
-- | Unlink to a remote process (asynchronous)
unlink :: ProcessId -> Process ()
link pidB -- Link to process B
msgFromB <- expect -- Wait for a message from process B
unlink pidB -- Unlink again
34. Cloud Haskell API
Process Monitoring & Error Handling
● monitor
○ Neue Referenz bei jedem Aufruf
○ Bei Mehrfachreferenzen entstehen auch mehrere
Benachrichtigungen
○ Reason:
■ DiedNormal, DiedException,
DiedDisconnect
-- | Monitor another process (asynchronous)
monitor :: ProcessId -> Process MonitorRef
-- | Remove a monitor
unmonitor :: MonitorRef -> Process ()
35. Cloud Haskell API
Process Monitoring & Error Handling
Beispiel Ping Pong mit Monitor/Supervisor
Funktion
36. Cloud Haskell API
Logging
● Sendet Nachrichten an registrierten Logger
Prozess
● Standardmäßig nach stderr
● Im Fall von SimpleLocalnet
○ Lokaler Node Logger Process
-- | Log a string
say :: String -> Process ()
37. Cloud Haskell API
Registry
● Registrierung von Prozessen unter einem
Namen
● Lokale Registry des Prozesses
● Zu registrierende Prozess muss sich nicht
auf der selben Node befinden
-- The process to be registered does not have to be local
itself.
register :: String -> ProcessId -> Process ()
-- | Remove a process from the local registry (asynchronous).
unregister :: String -> Process ()
-- | Query the local process registry
whereis :: String -> Process (Maybe ProcessId)
38. Serialisierung
In Cloud Haskell
● In anderen Sprachen
○ Meist durch die VM
■ Einige Typen sollte nicht serialisiert werden
können => Entscheidung durch Entwickler
■ Unsichtbares Kostenmodell
● In Haskell durch Typklassen
○ Entwickler hat volle Kontrolle
● Serialisierung von Funktionen
○ Unterscheidung von geschlossenen Funktionen und
Funktionen mit freien Variablen
39. Serialisierung
In Cloud Haskell
● Pure Functions
○ Durch Serialiserierung der Code Adresse
■ Nodes besitzen gleiche Codebasis
○ Markierung solcher Funktionen als static
■ Neuer Built-in Typ
■ Benötigt GHC Erweiterung
○ Beispiel
■ f x = send c (y -> y + 1)
■ => f x = send c (static (y -> y+1))
40. Serialisierung
In Cloud Haskell
● Funktionen mit freien Variablen
○ Umtransformation zu einer static Funktion nötig
■ Explizites Environment wird nötig
○ Beispiel
■ f x = send c (y -> y + x + 1)
■ => f x = send c (static (x y -> y + x + 1), x)
○ Abbildung durch Closure
data Closure a = Closure (Static (ByteString -> a)) ByteString
41. Serialisierung
In Cloud Haskell
● Closures
○ Funktionen müssen umgeschrieben werden
■ curried to uncurried function
■ workerProc :: String -> Int -> Process ()
■ => workerProc :: (String, Int) -> Process ()
○ Closure selbst ist ebenfalls Serializable
○ Closure Konversion erfolgt durch Template Haskell
○ f : T1 -> T2 => $(mkClosure 'f) :: T1 -> Closure T2
○ spawn :: NodeId -> Closure (Process ()) -> Process ProcessId
42. Serialisierung
In Cloud Haskell
● Closures Conversion Workaround
○ Durch Template Haskell
○ mkClosure arbeitet auf Namen von Funktionen
○ Lediglich Funktionsname und Env. wird übertragen
○ Registrierung der extern aufrufbaren Funktionen
durch remotable
○ Beispiel:
work :: (String, Int) -> Process ()
work = ...
remotable ['work]
spawnRemote :: NodeId -> Process ()
spawnRemote nodeId = do
_ <- spawn nId ($(mkClosure ’work) ("Question", 42))
return ()
...
newLocalNode transport $ __remoteTable initRemoteTable
44. Chat Server Beispiel
● Demonstration der vorgestellten Funktionen
○ Prozess Managment
○ Messaging
○ Monitoring
○ Logging
● Ungetypte Variante
○ siehe Quellcode
45. Zusammenfassung
Aktueller Stand - TODOs
● High-level Framework wie etwa OTP
○ Ansätze vorhanden wie etwa [distributed-process-
platform]
● Supervision
○ Basis durch link/monitor geschaffen
● Code hot swapping
○ In Haskell nicht möglich
46. Zusammenfassung
Aktueller Stand - TODOs
● Alternativen zu SimpleLocalNet / Azure
Backends
○ Etwa Backends für Datacenter
● Handling in Bezug auf Serialisierung von
Funktionen
○ Auf Seiten von GHC, nicht durch TH
○ Implementation der static Spracherweiterung
47. Zusammenfassung
Performance
● Bezogen auf Transport Layer Ebene (TCP
Implementation)
○ Minimaler overhead gegenüber einem normalen
Netzwerkpaket
○ Latenz overhead verglichen zu C
● Prozess Layer verglichen zum Prototypen
○ ca. 4x geringere Latenzzeit
○ ca. 200x höherer Durchsatz
■ Prototyp verwendete synchrones Messaging
=> Performance akzeptabel
48. Zusammenfassung
Fazit
● Implementation dekt die volle API des
Papers
● Performance "akzeptabel", Benchmarks?
○ Vergleich zu Erlang?
● Braucht noch Unterstützung bei der
Realisierung von Highlevel Libraries
● Für experimentelle Zwecke nach jetzigem
Stand
49. ● Toward Haskell in the Cloud - Jeff Epstein,
Andrew Black, and Simon Peyton Jones
● Functional programming for the data centre -
Jeff Epstein. Masters Thesis, University of
Cambridge
● A Unified Semantics for Future Erlang -
Hans Svensson, Lars-Ake Fredlund and
Clara Benac Earle
Quellen & Verweise
Papers