First things first

I approach this topic with a great deal of humility

Seriously

For me, this has worked very well

One of the best things that can happen to me

Being wrong about something

So,

This is a discussion!

Message Based Programming

Dana's Way

There are many like it, but this one is mine

IPC::Transit

Simple?

Simple!!

Version 0.1 has 461 lines of code

A little bit of history

First written some time in the mid 90s

Uploaded to friend's web server

Nobody downloaded it

Probably for the best

Re-implement it every job I've landed at

Only takes a few days each time

Wasn't super clean though

Somebody asked me to give a talk

Damn

Time to write from scratch

Clean and CPAN worthy

(more or less)

ONWARD!

Basic Tenents of Message Based Programming

(Strictly in the context of this talk)

Message Defined:

Arbitrarily complex

'normal'

Starts with hash reference

Normal is defined as scalars, arrays and hashes.

Like this:

#{   this => 'thing',
    stuff => ['x', 10, 'y']
}

Works with all serialization methods.

BUT

#{   this => 'thing',
    stuff => sub {
        return ['x', $main::foo++, qr/^x$/],
    }
}

Is fun too!

Bless you Data::Dumper

Be aware of the limitations.

Keep it this simple if possible, because it maps well to other languages.

YAD

(Yet Another Diversion)

How I think about things

Decouple first, ask questions later

Not always a good move

But it's where I usually start

Performant designs

Perl is a fairly performant language

Design is the key to performance

Reliable systems

Best way to make a flaky system?

Try to make it 100% reliable!

Reliability by embracing non-reliability

We'll get back to that!

OOP rocks

Functional Programming rocks

Be like Perl: multi-paradigm

UNIX Philosophy

Doug McIlroy: the inventor of Unix pipes

One of the founders of the Unix tradition

Make each program do one thing well.

To do a new job, build afresh rather than complicate old programs by adding new features.

Expect the output of every program to become the input to another

How about some code?

IPC::Transit::send(qname => 'prog2', message => { a => 'b' });

my $m = IPC::Transit::receive(qname => 'prog2');

The first line can be any process on any box.

The second line can be any process on any box.

Think of it as a dynamic pipeline.

#producer1.pl:
while(my $work = get_slow_work()) {
    IPC::Transit::send(qname => 'slow_worker1.pl', message => $work);
}

#slow_worker1.pl:
while(my $work = IPC::Transit::receive(qname => 'slow_worker1.pl')) {
    do_slow_work($work);
    IPC::Transit::send(qname => 'reporting.pl', message => $work);
}

To do slow work faster:

Run more copies of slow_worker1.pl

Maybe on different boxes

State coherence?

The message is the state!*

*Yes, overall.

Sometimes, we keep 'side effects', often for caching.

Try to keep that to a minimum

When all state is in the message,

Transparent core scalability

Transparent box scalability

Complex system is VERY easy to reason about

Core Concept:

Return what you get

Just add your own stuff

Or maybe change some stuff

Neat, but:

Every program is doing its own routing

NOT like a UNIX pipeline

$abstraction++;

'Centralize' the routing

#producer1.pl:
use IPC::Transit::Router qw(route_trans);
while(my $work = get_slow_work()) {
    route_trans($work);
}

#slow_worker1.pl:
use IPC::Transit::Router qw(route_trans);
while(my $work = IPC::Transit::receive(qname => 'slow_worker1.pl')) {
    do_slow_work($work);
    route_trans($work);
}

Wait, what?

Secret sauce:

#{   routes => [
        {   match => { source => 'producer1.pl' },
            forwards => [
                { qname => 'slow_worker1.pl' }
            ]
        },{ match => { source => 'slow_worker1.pl' },
            forwards => [
                { qname => 'reporting.pl' }
            ]
        }
    ]
}

I tend to use a single, global routing config

This is *slightly* simplified

Fully functional examples in the demo!

This methodology is weak at first

Its power grows as more things use it

More in the demos

http://www.realms.org/


