72
Motivation Our Solution Issues Summary Going Postal Chisel Wright NET-A-PORTER YAPC::EU 2010 Chisel Wright Going Postal

YAPC::EU 2010 - Going Postal

  • View
    1.293

  • Download
    0

Embed Size (px)

DESCRIPTION

Learn more about the evolution of inter-system communication from direct database access to message-queue based solutions. It's not a Holy Grail or Silver Bullet but might prevent people making mistakes already made by others on their behalf. The journey starts with a quick tour of things we used to do and progresses through time ... ending with an overview of an almost-in-production Net::ActiveMQ. No Aardvark were harmed in the making of this presentation.

Citation preview

Page 1: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Going Postal

Chisel Wright

NET-A-PORTER

YAPC::EU 2010

Chisel Wright Going Postal

Page 2: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

Motivation

Why Bother?

Chisel Wright Going Postal

Page 3: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

A Long Time AgoWhen There Weren’t Any Better Solutions

Direct, far away database writesNeed to know their dialectThey can’t easily change their schemaTightly coupled

Chisel Wright Going Postal

Page 4: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

A Long Time AgoWhen There Weren’t Any Better Solutions

Direct, far away database writesNeed to know their dialectThey can’t easily change their schemaTightly coupled

Chisel Wright Going Postal

Page 5: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

A Long Time AgoWhen There Weren’t Any Better Solutions

Direct, far away database writesNeed to know their dialectThey can’t easily change their schemaTightly coupled

Chisel Wright Going Postal

Page 6: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

A Long Time AgoWhen There Weren’t Any Better Solutions

Direct, far away database writesNeed to know their dialectThey can’t easily change their schemaTightly coupled

Chisel Wright Going Postal

Page 7: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

Not quite so long agoWhen There Were Options

TheSchwartzresolved a slightly different issuefirst step in the right directiondidn’t help with far away issueperl only

Chisel Wright Going Postal

Page 8: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

Not quite so long agoWhen There Were Options

TheSchwartzresolved a slightly different issuefirst step in the right directiondidn’t help with far away issueperl only

Chisel Wright Going Postal

Page 9: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

Not quite so long agoWhen There Were Options

TheSchwartzresolved a slightly different issuefirst step in the right directiondidn’t help with far away issueperl only

Chisel Wright Going Postal

Page 10: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

Not quite so long agoWhen There Were Options

TheSchwartzresolved a slightly different issuefirst step in the right directiondidn’t help with far away issueperl only

Chisel Wright Going Postal

Page 11: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Talking To Strangers

Not quite so long agoWhen There Were Options

TheSchwartzresolved a slightly different issuefirst step in the right directiondidn’t help with far away issueperl only

Chisel Wright Going Postal

Page 12: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Our Solution

Chisel Wright Going Postal

Page 13: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

ActiveMQ

Chisel Wright Going Postal

Page 14: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

ActiveMQ

Java guys like itPerl guys can interact with itReliable, well-used solution

Chisel Wright Going Postal

Page 15: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

ActiveMQ

Java guys like itPerl guys can interact with itReliable, well-used solution

Chisel Wright Going Postal

Page 16: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

ActiveMQ

Java guys like itPerl guys can interact with itReliable, well-used solution

Chisel Wright Going Postal

Page 17: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::Stomp

STOMP is great for talking to ActiveMQNet::Stomp excellent for quickly interacting with ActiveMQ

Chisel Wright Going Postal

Page 18: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::Stomp

STOMP is great for talking to ActiveMQNet::Stomp excellent for quickly interacting with ActiveMQ

Chisel Wright Going Postal

Page 19: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::StompTalking to a Queue

# send a message to the queue ’foo’use Net::Stomp;my $stomp = Net::Stomp->new({

hostname => ’localhost’,port => ’61613’,

});$stomp->connect({

login => ’hello’,passcode => ’there’,

});$stomp->send({

destination => ’/queue/foo’,body => ’test message’,

});$stomp->disconnect;

Chisel Wright Going Postal

Page 20: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Receiving with Net::StompSubscribing to a Queue

# subscribe to messages from the queue ’foo’use Net::Stomp;my $stomp = Net::Stomp->new({

hostname => ’localhost’,port => ’61613’,

});$stomp->connect({

login => ’hello’,passcode => ’there’,

});$stomp->subscribe({

destination => ’/queue/foo’,’ack’ => ’client’,’activemq.prefetchSize’ => 1,

});

Chisel Wright Going Postal

Page 21: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Receiving with Net::StompPlucking from the Queue

while (1) {my $frame = $stomp->receive_frame;warn $frame->body; # do something here$stomp->ack( { frame => $frame } );

}$stomp->disconnect;

Chisel Wright Going Postal

Page 22: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::StompThoughts

Quickly and easily talk to ActiveMQDoes what it promises

Low-levelCopy-and-paste codingError-handling, . . .

Chisel Wright Going Postal

Page 23: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::StompThoughts

Quickly and easily talk to ActiveMQDoes what it promises

Low-levelCopy-and-paste codingError-handling, . . .

Chisel Wright Going Postal

Page 24: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::StompThoughts

Quickly and easily talk to ActiveMQDoes what it promises

Low-levelCopy-and-paste codingError-handling, . . .

Chisel Wright Going Postal

Page 25: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::StompThoughts

Quickly and easily talk to ActiveMQDoes what it promises

Low-levelCopy-and-paste codingError-handling, . . .

Chisel Wright Going Postal

Page 26: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::StompThoughts

Quickly and easily talk to ActiveMQDoes what it promises

Low-levelCopy-and-paste codingError-handling, . . .

Chisel Wright Going Postal

Page 27: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::ActiveMQ

Chisel Wright Going Postal

Page 28: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::ActiveMQ

Standardise our AMQ solution across appsLower barrier to entry

Chisel Wright Going Postal

Page 29: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Net::ActiveMQ

Standardise our AMQ solution across appsLower barrier to entry

Chisel Wright Going Postal

Page 30: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

What Is It?

Essentially:Net::StompCatalyst::Engine::Stomp

with a lovely ribbon and bow around it.

Message producersMessage consumers

all in one place.

Chisel Wright Going Postal

Page 31: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQTalking to a Queue

use Net::ActiveMQ::Producer;

my $producer = Net::ActiveMQ::Producer->new({hostname => ’localhost’,port => 61613

});

$producer->send(’Some::Message’,{ message => ’data’, goes => ’here’ }

);

Net::ActiveMQ::Producer - message type does not exist -Some::Messageat /path/to/.../Class/MOP/Method/Wrapped.pm line 159

Chisel Wright Going Postal

Page 32: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQA Producer

package Net::ActiveMQ::Producer::Some::Message;use Moose;

with ’Net::ActiveMQ::Role::Producer’;

sub transform {my ($self, $header, $data) = @_;# make sure it goes somewhere$header->{destination} ||= ’/queue/some-message’;

# the desired action from the consumer$data->{’@type’} ||= ’action_method’;

# "transform" the data$data->{process_time} = scalar localtime;

return ($header, $data);}

1;

Chisel Wright Going Postal

Page 33: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQValidate What You Send

# This is completely optional!sub message_spec {

# Data::Rx formatreturn {type => ’//rec’,

required => {message => ’//str’,

},

optional => {’@type’ => ’//str’,goes => ’//str’,process_time => ’//str’,

},};

}

Chisel Wright Going Postal

Page 34: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQCatalyst Model

# create your model class./script/myapp_create.pl model \

MyMQ \Net::ActiveMQ \localhost \61613 \YES

# in your controller$c->model(’MyMQ’)->send(

’Some::Message’,{ message => { a => ’shiny’, hash => ’reference’ } }

);

Chisel Wright Going Postal

Page 35: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQWhy did you do that?

sub message_specUsed by proof-of-conceptCan catch your own mistakesNo compelling reason to remove it

Data::RxNicer error messagesSlightly more agnostic

Chisel Wright Going Postal

Page 36: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQWhy did you do that?

sub message_specUsed by proof-of-conceptCan catch your own mistakesNo compelling reason to remove it

Data::RxNicer error messagesSlightly more agnostic

Chisel Wright Going Postal

Page 37: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQWhy did you do that?

sub message_specUsed by proof-of-conceptCan catch your own mistakesNo compelling reason to remove it

Data::RxNicer error messagesSlightly more agnostic

Chisel Wright Going Postal

Page 38: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQWhy did you do that?

sub message_specUsed by proof-of-conceptCan catch your own mistakesNo compelling reason to remove it

Data::RxNicer error messagesSlightly more agnostic

Chisel Wright Going Postal

Page 39: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQWhy did you do that?

sub message_specUsed by proof-of-conceptCan catch your own mistakesNo compelling reason to remove it

Data::RxNicer error messagesSlightly more agnostic

Chisel Wright Going Postal

Page 40: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQWhy did you do that?

sub message_specUsed by proof-of-conceptCan catch your own mistakesNo compelling reason to remove it

Data::RxNicer error messagesSlightly more agnostic

Chisel Wright Going Postal

Page 41: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Sending with Net::ActiveMQWhy did you do that?

sub message_specUsed by proof-of-conceptCan catch your own mistakesNo compelling reason to remove it

Data::RxNicer error messagesSlightly more agnostic

Chisel Wright Going Postal

Page 42: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Receiving with Net::ActiveMQQuick But Boring

$ CATALYST_DEBUG=1 \> net_activemq_consumer_server.pl

[debug] Loaded engine "Catalyst::Engine::Stomp"...[debug] Loaded components:.--------------------------------+----------.| Class | Type |+--------------------------------+----------+| ...::Controller::Root | instance |’--------------------------------+----------’...[info] Application powered by Catalyst 5.80016

Not very useful in this stateFairly easy to add something useful

Chisel Wright Going Postal

Page 43: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Receiving with Net::ActiveMQQuick But Boring

$ CATALYST_DEBUG=1 \> net_activemq_consumer_server.pl

[debug] Loaded engine "Catalyst::Engine::Stomp"...[debug] Loaded components:.--------------------------------+----------.| Class | Type |+--------------------------------+----------+| ...::Controller::Root | instance |’--------------------------------+----------’...[info] Application powered by Catalyst 5.80016

Not very useful in this stateFairly easy to add something useful

Chisel Wright Going Postal

Page 44: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Receiving with Net::ActiveMQConsuming Some::Message

$ net_activemq_consumer_create.pl controller Some::MessageActiveMQ

created "lib/Net/ActiveMQ/Consumer/Controller/Some"created "t"created "lib/Net/ActiveMQ/Consumer/Controller/Some/Message.pm"created "t/controller_Some-Message.t"

Catalyst::Helper to do the hard work

Chisel Wright Going Postal

Page 45: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Receiving with Net::ActiveMQConsuming Some::Message

$ CATALYST_DEBUG=1 \> PERL5LIB=$PWD/lib \> net_activemq_consumer_server.pl

[debug] Loaded engine "Catalyst::Engine::Stomp"...[debug] Loaded components:.--------------------------------+----------.| Class | Type |+--------------------------------+----------+| ...::Controller::Root | instance || ...::Controller::Some::Message | instance |’--------------------------------+----------’...[info] Application powered by Catalyst 5.80016

Chisel Wright Going Postal

Page 46: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Receiving with Net::ActiveMQController Actions

package Net::ActiveMQ::Consumer::Controller::Some::Message;use Moose;BEGIN {

extends’Net::ActiveMQ::Consumer::ControllerBase::MessageDriven’

}__PACKAGE__->config(

action_namespace => ’some-message’);# this will handle ’@type’ of ’action_method’ in# the ’some-message’ queuesub action_method :Local {

my ($self, $c, $message) = @_;$c->log->warn(’Some::Message / some-message / action_method’);

}__PACKAGE__->meta->make_immutable;1;

Chisel Wright Going Postal

Page 47: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

ActiveMQNet::StompNet::ActiveMQ

Receiving with Net::ActiveMQQueue::Spec

package Net::ActiveMQ::Consumer::Queue::Spec::Some::Message;use Moose;

sub action_method {return { type => ’//any’ };

}

1;

Chisel Wright Going Postal

Page 48: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

Issues

Chisel Wright Going Postal

Page 49: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

Peer Review

Only recently been used by the rest of the team

BugsHandling errors

Lack of clear documentation

PODHow-To / “How do I. . . ?”

Chisel Wright Going Postal

Page 50: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

Peer Review

Only recently been used by the rest of the team

BugsHandling errors

Lack of clear documentation

PODHow-To / “How do I. . . ?”

Chisel Wright Going Postal

Page 51: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

Peer Review

Only recently been used by the rest of the team

BugsHandling errors

Lack of clear documentation

PODHow-To / “How do I. . . ?”

Chisel Wright Going Postal

Page 52: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

Peer Review

Only recently been used by the rest of the team

BugsHandling errors

Lack of clear documentation

PODHow-To / “How do I. . . ?”

Chisel Wright Going Postal

Page 53: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

Peer Review

Only recently been used by the rest of the team

BugsHandling errors

Lack of clear documentation

PODHow-To / “How do I. . . ?”

Chisel Wright Going Postal

Page 54: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

Peer Review

Only recently been used by the rest of the team

BugsHandling errors

Lack of clear documentation

PODHow-To / “How do I. . . ?”

Chisel Wright Going Postal

Page 55: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationGeneral

Some early features not fully phased out@type => ’...’JMSType

Chisel Wright Going Postal

Page 56: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationGeneral

Some early features not fully phased out@type => ’...’JMSType

Chisel Wright Going Postal

Page 57: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationGeneral

Some early features not fully phased out@type => ’...’JMSType

Chisel Wright Going Postal

Page 58: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationProducers

Shared spec files implementation not completeStill living in the ::Consumer:: namespaceNot easily used by ::Producer:: classesmessage_spec vs build_message_spec

Chisel Wright Going Postal

Page 59: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationProducers

Shared spec files implementation not completeStill living in the ::Consumer:: namespaceNot easily used by ::Producer:: classesmessage_spec vs build_message_spec

Chisel Wright Going Postal

Page 60: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationProducers

Shared spec files implementation not completeStill living in the ::Consumer:: namespaceNot easily used by ::Producer:: classesmessage_spec vs build_message_spec

Chisel Wright Going Postal

Page 61: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationProducers

Shared spec files implementation not completeStill living in the ::Consumer:: namespaceNot easily used by ::Producer:: classesmessage_spec vs build_message_spec

Chisel Wright Going Postal

Page 62: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationConsumers

Consumers limited to Net::ActiveMQ namespaceError-Handling still evolving

Chisel Wright Going Postal

Page 63: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Peer ReviewImplementation

ImplementationConsumers

Consumers limited to Net::ActiveMQ namespaceError-Handling still evolving

Chisel Wright Going Postal

Page 64: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Summary

Avoid direct database interactionUse message queuesDRY

The FutureEvolution - final stagesWe’re starting to use it in productionWe hope to release to the CPAN soon

Chisel Wright Going Postal

Page 65: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Summary

Avoid direct database interactionUse message queuesDRY

The FutureEvolution - final stagesWe’re starting to use it in productionWe hope to release to the CPAN soon

Chisel Wright Going Postal

Page 66: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Summary

Avoid direct database interactionUse message queuesDRY

The FutureEvolution - final stagesWe’re starting to use it in productionWe hope to release to the CPAN soon

Chisel Wright Going Postal

Page 67: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Summary

Avoid direct database interactionUse message queuesDRY

The FutureEvolution - final stagesWe’re starting to use it in productionWe hope to release to the CPAN soon

Chisel Wright Going Postal

Page 68: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Summary

Avoid direct database interactionUse message queuesDRY

The FutureEvolution - final stagesWe’re starting to use it in productionWe hope to release to the CPAN soon

Chisel Wright Going Postal

Page 69: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Summary

Avoid direct database interactionUse message queuesDRY

The FutureEvolution - final stagesWe’re starting to use it in productionWe hope to release to the CPAN soon

Chisel Wright Going Postal

Page 70: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Summary

Avoid direct database interactionUse message queuesDRY

The FutureEvolution - final stagesWe’re starting to use it in productionWe hope to release to the CPAN soon

Chisel Wright Going Postal

Page 71: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

QUESTIONS?

Chisel Wright Going Postal

Page 72: YAPC::EU 2010 - Going Postal

MotivationOur Solution

IssuesSummary

Obligatory LOLCATLOL-Rilla?

[email protected]

Chisel Wright Going Postal