libcppa Now: High-Level Distributed Programming Without Sacrificing - - PowerPoint PPT Presentation

libcppa now high level distributed programming without
SMART_READER_LITE
LIVE PREVIEW

libcppa Now: High-Level Distributed Programming Without Sacrificing - - PowerPoint PPT Presentation

libcppa Now: High-Level Distributed Programming Without Sacrificing Performance Matthias Vallentin matthias@bro.org University of California, Berkeley C ++ Now May 14, 2013 Outline 1. Example Application: VAST 2. Designing Distributed


slide-1
SLIDE 1

libcppa Now: High-Level Distributed Programming Without Sacrificing Performance

Matthias Vallentin

matthias@bro.org

University of California, Berkeley

C++Now May 14, 2013

slide-2
SLIDE 2

Outline

  • 1. Example Application: VAST
  • 2. Designing Distributed Applications
  • 3. Thinking libcppa

Interfacing with 3rd-party APIs Agility Through Network Transparency Behavior Composition Monadic Composition

1 / 17

slide-3
SLIDE 3

The Network Security Domain

Network Forensics & Incident Response

◮ Scenario: security breach discovered ◮ Analysts have to determine scope and impact

Analyst questions

◮ How did the attacker(s) get in? ◮ How long did the they stay under the radar? ◮ What is the damage ($$$, reputation, data loss, etc.)? ◮ How to detect similar attacks in the future?

Challenges

◮ Volume: machine-generated data exceeds analysis capacities ◮ Typing: difficult to contextualize minimally typed data ◮ Heterogeneity: numerous log formats and processing styles

2 / 17

slide-4
SLIDE 4

VAST: Visibility Across Space and Time Architecture Overview

VAST

A scalable, interactive system to facilitate

◮ forensic analyses ◮ incident response

Components

◮ Archive: Compressed, serialized events ◮ Index: Encoded, compressed bitmaps ◮ Segment/Partition: Data scaling unit ◮ Ingestion: Sources: IDS, syslog, etc. ◮ Query: Sinks: Analyst, IDS, feed, etc.

Memory Storage Query Ingestor Archive Index Segments Partitions SSHD IDS Analyst 3 / 17

slide-5
SLIDE 5

VAST: Distributed Deployment

Analyst VAST core

4 / 17

slide-6
SLIDE 6

Outline

  • 1. Example Application: VAST
  • 2. Designing Distributed Applications
  • 3. Thinking libcppa

Interfacing with 3rd-party APIs Agility Through Network Transparency Behavior Composition Monadic Composition

4 / 17

slide-7
SLIDE 7

Software Design in Distributed Applications

Component A Component B Component C

5 / 17

slide-8
SLIDE 8

Challenge: Varying Deployment Scenarios

Machine 2 Machine 1 Component A Component B Component C

6 / 17

slide-9
SLIDE 9

Challenge: Varying Deployment Scenarios

Machine 2 Machine 1 Component A Component B Component C Pointer Passing IPC & Serialization

7 / 17

slide-10
SLIDE 10

Primitives for Programming Distributed Systems

Desired Building Blocks

◮ Flexible serialization ◮ Platform independence ◮ Network transparency ◮ Rich-typed messaging ◮ Asynchronous coding style ◮ Powerful concurrency model

Machine 2 Machine 1 Component A Component B Component C

C++ Reality

No existing light-weight middle-layer with high level of abstraction

  • libcppa aims to fill that gap

8 / 17

slide-11
SLIDE 11

Outline

  • 1. Example Application: VAST
  • 2. Designing Distributed Applications
  • 3. Thinking libcppa

Interfacing with 3rd-party APIs Agility Through Network Transparency Behavior Composition Monadic Composition

8 / 17

slide-12
SLIDE 12

Outline

  • 1. Example Application: VAST
  • 2. Designing Distributed Applications
  • 3. Thinking libcppa

Interfacing with 3rd-party APIs Agility Through Network Transparency Behavior Composition Monadic Composition

8 / 17

slide-13
SLIDE 13

Creating a TCP/IP Server

VAST: Data Ingestion

  • 1. Accept new TCP/IP connection
  • 2. Poll POSIX file descriptor of connection
  • 3. Read from socket when data is available
  • 4. Convert data into internal event format

Existing solutions

◮ Basic: accept & fork (or new thread) ◮ Boost Asio: non-blocking, but IoC

Memory Storage Ingestor Archive Index Segments Partitions 9 / 17

slide-14
SLIDE 14

A Generic Asynchronous IPv4 Server

template <typename Connection> void server(uint16_t port, char const* addr, actor_ptr handler) { auto acceptor = network::ipv4_acceptor::create(port, addr); receive_loop( // libcppa's blocking API

  • n(atom("accept")) >> [&] {

if (util::poll(acceptor->file_handle()) && (auto io = acceptor->try_accept_connection())) { auto conn = spawn<Connection>((*io).first, (*io).second); send(handler, atom("connection"), conn); } self << last_dequeued(); },

  • n(atom("kill")) >> [] { self->quit(); }

); }

10 / 17

slide-15
SLIDE 15

A Generic Asynchronous IPv4 Server

template <typename Connection> void server(uint16_t port, char const* addr, actor_ptr handler) { auto acceptor = network::ipv4_acceptor::create(port, addr); receive_loop( // libcppa's blocking API

  • n(atom("accept")) >> [&] {

if (util::poll(acceptor->file_handle()) && (auto io = acceptor->try_accept_connection())) { auto conn = spawn<Connection>((*io).first, (*io).second); send(handler, atom("connection"), conn); } self << last_dequeued(); },

  • n(atom("kill")) >> [] { self->quit(); }

); }

10 / 17

slide-16
SLIDE 16

A Generic Asynchronous IPv4 Server

template <typename Connection> void server(uint16_t port, char const* addr, actor_ptr handler) { auto acceptor = network::ipv4_acceptor::create(port, addr); receive_loop( // libcppa's blocking API

  • n(atom("accept")) >> [&] {

if (util::poll(acceptor->file_handle()) && (auto io = acceptor->try_accept_connection())) { auto conn = spawn<Connection>((*io).first, (*io).second); send(handler, atom("connection"), conn); } self << last_dequeued(); },

  • n(atom("kill")) >> [] { self->quit(); }

); }

10 / 17

slide-17
SLIDE 17

A Generic Asynchronous IPv4 Server

template <typename Connection> void server(uint16_t port, char const* addr, actor_ptr handler) { auto acceptor = network::ipv4_acceptor::create(port, addr); receive_loop( // libcppa's blocking API

  • n(atom("accept")) >> [&] {

if (util::poll(acceptor->file_handle()) && (auto io = acceptor->try_accept_connection())) { auto conn = spawn<Connection>((*io).first, (*io).second); send(handler, atom("connection"), conn); } self << last_dequeued(); },

  • n(atom("kill")) >> [] { self->quit(); }

); }

Infinite loop that dequeues messages from the actor's inbox

10 / 17

slide-18
SLIDE 18

A Generic Asynchronous IPv4 Server

template <typename Connection> void server(uint16_t port, char const* addr, actor_ptr handler) { auto acceptor = network::ipv4_acceptor::create(port, addr); receive_loop( // libcppa's blocking API

  • n(atom("accept")) >> [&] {

if (util::poll(acceptor->file_handle()) && (auto io = acceptor->try_accept_connection())) { auto conn = spawn<Connection>((*io).first, (*io).second); send(handler, atom("connection"), conn); } self << last_dequeued(); },

  • n(atom("kill")) >> [] { self->quit(); }

); }

Behavior definition: start the accept loop or shut down the actor

10 / 17

slide-19
SLIDE 19

A Generic Asynchronous IPv4 Server

template <typename Connection> void server(uint16_t port, char const* addr, actor_ptr handler) { auto acceptor = network::ipv4_acceptor::create(port, addr); receive_loop( // libcppa's blocking API

  • n(atom("accept")) >> [&] {

if (util::poll(acceptor->file_handle()) && (auto io = acceptor->try_accept_connection())) { auto conn = spawn<Connection>((*io).first, (*io).second); send(handler, atom("connection"), conn); } self << last_dequeued(); },

  • n(atom("kill")) >> [] { self->quit(); }

); }

Spawns an event_based_actor with a pair of streams for reading and writing, then announces the new actor to another actor

10 / 17

slide-20
SLIDE 20

Outline

  • 1. Example Application: VAST
  • 2. Designing Distributed Applications
  • 3. Thinking libcppa

Interfacing with 3rd-party APIs Agility Through Network Transparency Behavior Composition Monadic Composition

10 / 17

slide-21
SLIDE 21

Developing Agile Systems

Provisioning

◮ Reality: skewed workload distributions

→ Requires dynamic provisioning

◮ Under-provisioned: spin up actors ◮ Over-provisioned: bring down actors

Agility

◮ Ability to handle/buffer peak loads ◮ Runtime re-configuration

→ without process restart → without losing in-memory state

100 200 300 400 500 600 5 10 15 20 25 30 35 Time (seconds) Bytes (MB)

11 / 17

slide-22
SLIDE 22

Wiring Components at Runtime

class program { public: void run() { auto host = config_.get("tracker.host"); auto port = config_.as<unsigned>("tracker.port"); if (config_.check("tracker-actor")) { tracker_ = spawn<id_tracker>("/path/to/id_file"); publish(tracker_, port, host.data()); } else { tracker_ = remote_actor(host, port); } ... // Further similar initializations. } private: configuration config_; actor_ptr tracker_; };

12 / 17

slide-23
SLIDE 23

Wiring Components at Runtime

class program { public: void run() { auto host = config_.get("tracker.host"); auto port = config_.as<unsigned>("tracker.port"); if (config_.check("tracker-actor")) { tracker_ = spawn<id_tracker>("/path/to/id_file"); publish(tracker_, port, host.data()); } else { tracker_ = remote_actor(host, port); } ... // Further similar initializations. } private: configuration config_; actor_ptr tracker_; };

If actor should run in this process, spawn and publish it

12 / 17

slide-24
SLIDE 24

Wiring Components at Runtime

class program { public: void run() { auto host = config_.get("tracker.host"); auto port = config_.as<unsigned>("tracker.port"); if (config_.check("tracker-actor")) { tracker_ = spawn<id_tracker>("/path/to/id_file"); publish(tracker_, port, host.data()); } else { tracker_ = remote_actor(host, port); } ... // Further similar initializations. } private: configuration config_; actor_ptr tracker_; };

Otherwise connect to it

12 / 17

slide-25
SLIDE 25

Wiring Components at Runtime

class program { public: void run() { auto host = config_.get("tracker.host"); auto port = config_.as<unsigned>("tracker.port"); if (config_.check("tracker-actor")) { tracker_ = spawn<id_tracker>("/path/to/id_file"); publish(tracker_, port, host.data()); } else { tracker_ = remote_actor(host, port); } ... // Further similar initializations. } private: configuration config_; actor_ptr tracker_; };

12 / 17

slide-26
SLIDE 26

Outline

  • 1. Example Application: VAST
  • 2. Designing Distributed Applications
  • 3. Thinking libcppa

Interfacing with 3rd-party APIs Agility Through Network Transparency Behavior Composition Monadic Composition

12 / 17

slide-27
SLIDE 27

Modularizing Code

Basic Techniques

◮ Straight-forward to reuse code in OO-style programs ◮ But how to reuse behavior in libcppa?

Object-Oriented Inheritance

struct base {}; struct derived : base {};

Object-Oriented Composition

struct foo {}; struct bar { foo f };

Composing behavior in libcppa

◮ Example:

partial_function f; partial_function g; partial_function h = f.or_else(g); partial_function i; behavior j = h.or_else(i);

13 / 17

slide-28
SLIDE 28

Example: Base Class for Event Sources (simplified)

template <typename Derived> class async_source : public event_based_actor { public: async_source(actor_ptr upstream) { running_ = (

  • n_arg_match >> [=](event const& e) { /* work */ },
  • n_arg_match >> [=](std::vector<event> const& v) { /* work */ }

); } void init() override { become(running_.or_else(static_cast<Derived*>(this)->impl)); } private: partial_function running_; }; struct file_source : async_source<file_source> { behavior impl; };

14 / 17

slide-29
SLIDE 29

Example: Base Class for Event Sources (simplified)

template <typename Derived> class async_source : public event_based_actor { public: async_source(actor_ptr upstream) { running_ = (

  • n_arg_match >> [=](event const& e) { /* work */ },
  • n_arg_match >> [=](std::vector<event> const& v) { /* work */ }

); } void init() override { become(running_.or_else(static_cast<Derived*>(this)->impl)); } private: partial_function running_; }; struct file_source : async_source<file_source> { behavior impl; };

libcppa's event-based actors require implementation of init()

14 / 17

slide-30
SLIDE 30

Example: Base Class for Event Sources (simplified)

template <typename Derived> class async_source : public event_based_actor { public: async_source(actor_ptr upstream) { running_ = (

  • n_arg_match >> [=](event const& e) { /* work */ },
  • n_arg_match >> [=](std::vector<event> const& v) { /* work */ }

); } void init() override { become(running_.or_else(static_cast<Derived*>(this)->impl)); } private: partial_function running_; }; struct file_source : async_source<file_source> { behavior impl; };

(Stateful) base-class with partial behavior

14 / 17

slide-31
SLIDE 31

Example: Base Class for Event Sources (simplified)

template <typename Derived> class async_source : public event_based_actor { public: async_source(actor_ptr upstream) { running_ = (

  • n_arg_match >> [=](event const& e) { /* work */ },
  • n_arg_match >> [=](std::vector<event> const& v) { /* work */ }

); } void init() override { become(running_.or_else(static_cast<Derived*>(this)->impl)); } private: partial_function running_; }; struct file_source : async_source<file_source> { behavior impl; };

Composition of partial functions (or behavior)

14 / 17

slide-32
SLIDE 32

Outline

  • 1. Example Application: VAST
  • 2. Designing Distributed Applications
  • 3. Thinking libcppa

Interfacing with 3rd-party APIs Agility Through Network Transparency Behavior Composition Monadic Composition

14 / 17

slide-33
SLIDE 33

Developing Message Protocols

Asynchronous Messaging

◮ Decoupled send and receive

  • 1. Pattern-match dequeued message
  • 2. Invoke corresponding handler
  • 3. Send message (optional)

◮ libcppa: send(..)

→ Loose coupling

B A w

  • r

k s t a t s g

  • Synchronous Messaging

◮ Lockstep: matching send and receive

  • 1. Send a message
  • 2. Push new behavior to handle reply
  • 3. Pop behavior

◮ libcppa: sync_send(..) ◮ How to compose?

B A p i n g p

  • n

g s t

  • p

< d

  • n

e , i > 15 / 17

slide-34
SLIDE 34

The Lack of Monads in C++

Composition

◮ C++ lacks a powerful language primitive for composition: monads ◮ (>>=) :: (Monad m) => m a -> (a -> m b) -> m b

HasC++all

sync_send(b, atom("ping")) >>= on(atom("pong")) >> [=] { return sync_send(b, atom("stop")); } >>= on(atom("done"), arg_match) >> [=](int i) { f(i); }

libcppa

sync_send(b, atom("ping")).then(

  • n(atom("pong")) >> [=] { sync_send(b, atom("stop")).then(
  • n(atom("done"), arg_match) >> [=](int i) { f(i); });

});

16 / 17

slide-35
SLIDE 35

Summary

Experience using libcppa

◮ Offered abstractions facilitate solving domain challenges ◮ Asynchronous coding style with local reasoning (no IoC) ◮ Easy integration with 3rd-party APIs ◮ Flexible deployment scenarios support highly dynamic systems ◮ Low overhead (1–3% CPU time)

Future Work

◮ Improve runtime type debugging and diagnostics

Parting thoughts

◮ Actor model as natural paradigm to address today’s challenges

◮ libprocess, jss::actor, Casablanca, . . .

◮ C++1? wish list:

◮ Monads → composition of asynchronous operations ◮ Pattern matching → type-safe dispatching 17 / 17

slide-36
SLIDE 36

Thank You. . . Questions?

FIN

https://github.com/Neverlord/libcppa https://github.com/mavam/vast IRC at Freenode: #libcppa, #vast

1 / 1