BBC (British Broadcasting Corporation) > Sub-Slice-1.048 > Sub::Slice::Backend

Download:
Sub-Slice-1.048.tar.gz

Annotate this POD

View/Report Bugs
Source  

NAME ^

Sub::Slice::Backend - API for Sub::Slice backends

SYNOPSIS ^

        use Sub::Slice;
        use Sub::Slice::Backend::MyBackend;
        our %Options = ( ...options for your backend... );
        
        sub create_token {
                my $job = new Sub::Slice (
                        backend => 'MyBackend',
                        storage_options => \%Options,
                );
                return $job->token;
        }
        
        sub do_work {
                my $job = new Sub::Slice (
                        backend => 'MyBackend',
                        storage_options => \%Options,
                        token => shift()
                );
                
                ...
        }

DESCRIPTION ^

This is the API which storage backends for Sub::Slice must implement. Sub::Slice comes with a default Filesystem backend using Storable to serialise data. If you'd rather store the Sub::Slice data somewhere else or in another format, you can create an adaptor class satisfying this API to plug Sub::Slice into your storage system.

Module naming conventions

If a string matching \w+ is passed to Sub::Slice for the backend, the module is assumed to be in the Sub::Slice::Backend namespace (such as the example in the SYNOPSIS). If you have a module in a namespace other than the top-level one, e.g. MySystem::StorageModule, that exposes this API, Sub::Slice will happily use this as a backend module without you needing to create a wrapper in the Sub::Slice::Backend namespace.

        my $job = new Sub::Slice (
                backend => 'MySystem::StorageModule',
                storage_options => \%Options,
        );

METHODS ^

$backend = new Sub::Slice::Backend::YOURCLASS(\%options)

Contructor takes a hash of options

$id = $backend->new_id()

Generates a new ID.

$job = $backend->load_job($id)

Loads an existing job from persistent storage

$backend->save_job($job)

Saves a job to persistent storage

$backend->delete_job($id)

Removes a job from persistent storage

$backend->store($job, $key, $data)

Store data for job

$data = $backend->fetch($job, $key)

Fetch data for job

$backend->store_blob($job, $key, $data)

Store BLOB data for job

$data = $backend->fetch_blob($job, $key)

Fetch BLOB data for job

$count = $backend->cleanup($age)

Deletes any leftover data which has been unmodified for at least $age days (default 1 day). $count will equal the number of files or items deleted, or may be undef if cleanup wasn't able to scan for leftover data (eg. because a directory didn't exist). Should die if it doesn't manage to clean up (eg. because of a permissions problem).

Normally, tasks should clean up after themselves, so unless you are experiencing errors, cleanup will have nothing to do.

Strategies for issuing IDs ^

The storage API requires that you can issue an ID for a new record before storing data into it. In an Oracle database you normally use a sequence to generate a unique ID in advance of storing a record so this isn't a problem. In MySQL, if you are planning on using an AUTO_INCREMENT column to generate ids, you're probably best off inserting an empty row. Alternatively in most database engines you can simulate an Oracle sequence using a counter table (see the example in the MySQL manual for a neat example using LAST_INSERT_ID). If your storage engine doesn't have a unique ID mechanism, a couple of strategies are open to you: you can use a perl GUID generator (such as Data::UUID), or you can roll your own sequence generator as in the example below.

Worked Example ^

Here's a sample implementation. It's not a particularly good one in that it doesn't handle concurrent processes writing to the DBM file, but it does illustrate the steps required to write a backend.

        package Sub::Slice::Backend::MLDBM;
        
        use GDBM_File;
        use Storable();
        use MLDBM qw(GDBM_File Storable);

The constructor takes a hashref of storage options, which you're free to define the meaning of. These are what the caller will pass in as the storage_options to Sub::Slice.

        sub new {
                my ($class, $options) = @_;
                my $dbm = $options->{DBM} or die("You must supply a DBM");
                my %db;
                tie(%db, 'MLDBM', $dbm, GDBM_WRCREAT, 0666) or die("unable to tie to $dbm");
                my $self = {$db => \%db};
                return bless($self, $class);
        }

        sub DESTROY {
                my $self = shift;
                untie %{$self->{db}} if tied %{$self->{db}};
        }

The new_id routine should create a unique ID which can be used to store the job against. Here we use a sentinel key to store a sequence counter against:

        sub new_id {
                my ($self) = @_;
                my $id = ++$self->{db}->{__COUNT__};
                return $id;
        }

The delete_job routine takes an ID rather than a job:

        sub delete_job {
                my ($self, $id) = @_;
                delete $self->{db}->{$id};
        }

This allows it to be used in cleaner processes without loading the job first. The load_job routine should return the job given the ID:

        sub load_job {
                my ($self, $id) = @_;
                return $self->{db}->{$id};
        }

The save_job should persist the job (against the ID):

        sub save_job {
                my ($self, $job) = @_;
                $self->{db}->{$job->{id}} = $job;
        }

Store and fetch methods are passed the job object so they can hang data on it, if the backend desires:

        sub store {
                my ($self, $job, $key, $value) = @_;
                $job->{data}{$key} = $value;
        }
        
        sub fetch {
                my ($self, $job, $key) = @_;
                return $job->{data}{$key};
        }

If there is no optimisation you want to perform for BLOB data, the following implementation is legitimate:

        sub store_blob {
                shift()->store(@_);
        }
        
        sub fetch_blob {
                shift()->fetch(@_);
        }

VERSION ^

$Revision: 1.8 $ on $Date: 2004/12/17 14:41:21 $ by $Author: johna $

AUTHOR ^

John Alden <cpan _at_ bbc _dot_ co _dot_ uk>

COPYRIGHT ^

(c) BBC 2004. This program is free software; you can redistribute it and/or modify it under the GNU GPL.

See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt

syntax highlighting: