Announcing Osgood: An Event Repository
Yesterday I released Osgood::Client and today, after realizing I had botched the upload, Osgood::Server. Unfortunately the documentation is a little thin, so I’ll take this opportunity to both inform the world and the module of it’s purpose.
Osgood is a passive, persistent, stateless event repository. The current docs say queue rather than repository but we’ve decided it’s a bit of a misnomer. A primer on the aforementioned explanation:
- Passive: Osgood doesn’t seek out your events, it only waits for notification and query
- Stateless: Querying an event does not change it
- Repository: Once you’ve inserted an event it stays there forever
So what’s that mean? Osgood (::Server) is a system wherein you record the fact that something happened. A client library is provided that allows you to do just that (::Client). You can also query the server to ask it what has happened and it will inform you. I believe an example is in order.
Magazines.com’s backend is basically a big order pipeline after the order is taken. We are phasing out an AS/400 which is currently handling fulfillment of orders. If a customer calls us and wants to cancel a magazine, our our order entry system cancels the order and then needs to notify the AS/400. We have an audit table for order line items, so we would query that table for fresh cancels and send them to the 400 to finish the process.
All was well! Then our Marketing team approached us with a good idea: Send customers who’ve canceled an email enticing them back! It was a great idea until we released that we basically had one shot in our aforementioned setup to send a cancel, and we were using it. There was no way to keep up with the status. Had we sent it to the 400? What about to our email service provider? Were there other things we might do in the future? Osgood was born.
Osgood is based around Events. You use ::Client to send an event to the Osgood server, like so:
my $event = new Osgood::Event(
object => 'Moose',
action => 'farted',
date_occurred => DateTime->now(),
params => {
name => 'Hector'
}
);
Events are composed of an object, an action and a time. These fields are all stored as VARCHAR(64)s, so you can put anything you like up to that size.
The optional params accepts a hashref of name value pairs. This allows storage of information that doesn’t fit Osgood’s Object/Event setup, but that you might need when using the event. A good example would be the primary key of the specific Moose that farted. Note that you can also use get_param and set_param on an Event object.
my $list = new Osgood::EventList(events => [ $event ])
my $client = new Osgood::Client(
url => ‘http://localhost’,
list => $list
);
my $retval = $client->send();
if($list->size() == $retval) {
print “Success :)\n”;
} else {
print “Failure :(\n”;
}
You can put as many events into an EventList as you like. Then you create a Client and send it! Above we check that send returned a number that matches the events we sent.
That’s all well and good, but how do you use what you’ve stored? The client also works the other way around:
my $retval = $client->query({
object => 'Moose',
action => 'farted',
});
if($retval) { # Gets an EventList my $list = $client->list(); my $iterator = $list->iterator(); while($iterator->hasnext()) { my $event = $iterator->next(); print $event->getparam(’name’).” farted at “.$event->date_occurred().”\n”; } } else { print “Oh noes!\n”; }
We asked Osgood for all the Moose/farted events and we got them back. One note of interest is that Events are assigned an ID when they are stored, and that ID is present when you get them in a query. This will come in useful later.
The query method supports many other options as of this writing:
- date_after: date after the specified one
- date_before: date before the specified one
- id: all events with an id greater than the one specified
- limit: only return the specified number of events
So back to our original problem. Rather than make our internal cancel process complex, we simply create a row in a queue table which is emptied frequently and sent to Osgood. Our backend processing jobs then query Osgood using the id parameter to query to find every event that was created since the last one it processed. Now we can create an unlimited number of backend jobs that work from the same event. Our email sending and publisher notification of cancels happen independently.
This work was inspired by past work with Publisher/Subscriber systems and message queues like the venerable JMS. It bears very little resemblance to those ideas, but one might be able to squint a bit and see the similarity.
Osgood::Server is a Catalyst application using the XML::REST plugin and ::Client is Moose based with some help from MooseX::Iterator and some XML::XPath and XML::DOM magic for serializing and deserializing the eventlist.
If you have any questions, feel free to drop me an email or hit me up on irc.perl.org as gphat.

Comments (One comment)
Had trouble installing (on 64bit Debian), failing on MooseX::Iterator, apparently due to the same weak-references-not-enabled problem that’s given me troubles with Class DBI.
After some research I found that doing:
perl -MCPAN -e shell force install Scalar::Util
Seems to resolve it, and I was able to install successfully.
(I’m guessing it may have also solved my Class DBI issues though I haven’t tested)
Jon / March 10th, 2008, 1:56 pm / #
Post a comment