Amazon-DynamoDB-Simple

 view release on metacpan or  search on metacpan

lib/Amazon/DynamoDB/Simple.pm  view on Meta::CPAN

This module provides a simple UI layer on top of Amazon::DynamoDB.  It also
makes your data highly available across exactly 2 AWS regions.  In other words
it provides redundancy in case one region goes down.  It doesn't do async.  It
doesn't (currently) support secondary keys.

Note Amazon::DynamoDB can't handle complex data structures.  But this module
can because it serializes yer stuff to JSON if needed.

At the moment you cannot use this module against a single dynamodb server.  The
table must exist in 2 regions.  I want to make the high availability part
optional in the future.  It should not be hard.  Patches welcome.

=head1 DATA REDUNDANCY

TODO

=cut

has table             => (is => 'rw', required => 1);
has primary_key       => (is => 'rw', required => 1);
has dynamodbs         => (is => 'lazy');
has hosts             => (is => 'rw', lazy => 1, builder => 1);
has access_key_id     => (is => 'rw', lazy => 1, builder => 1);
has secret_access_key => (is => 'rw', lazy => 1, builder => 1);

sub _build_access_key_id     { $ENV{AWS_ACCESS_KEY_ID}     }
sub _build_secret_access_key { $ENV{AWS_SECRET_ACCESS_KEY} }

sub _build_hosts {
    return [qw/
        dynamodb.us-east-1.amazonaws.com
        dynamodb.us-west-1.amazonaws.com
    /];
}

sub _build_dynamodbs {
    my $self = shift;

    my @dynamodbs;
    my $hosts = $self->hosts();

    for my $host (@$hosts) {
        push @dynamodbs, 
            Amazon::DynamoDB->new(
                access_key     => $self->access_key_id,
                secret_key     => $self->secret_access_key,
                ssl            => 1,
                version        => '20120810',
                implementation => 'Amazon::DynamoDB::MojoUA',
                host           => $host,
            );
    }

    return \@dynamodbs;
}

sub put {
    my ($self, %item) = @_;

    # timestamp this transaction
    $item{last_updated} = DateTime->now . ""; # stringify datetime
    $item{deleted}    ||= 0;

    %item         = $self->deflate(%item);
    my $dynamodbs = $self->dynamodbs();
    my $success   = 0;

    for my $dynamodb (@$dynamodbs) {
        try { 
            $dynamodb->put_item(
                TableName => $self->table,
                Item      => \%item,
            )->get;
            $success++;
        }
        catch {
            warn "caught error: " . p $_;
        };
    }

    # make sure at least one put_item() was successful
    confess "unable to save to any dynamodb" unless $success;
}

sub delete {
    my ($self, $key) = @_;

    my %item = $self->get($key);

    return unless keys %item;

    $self->put(%item, deleted => 1);
}

# Amazon::DynamoDB can't handle anything other than simple scalars
sub inflate {
    my ($self, %item) = @_;
    my %new;

    for my $key (keys %item) {
        my $value   = $item{$key};
        $new{$key} = $self->is_valid_json($value)
            ? JSON::XS->new->utf8->pretty->decode($value)
            : $value;
    }

    return %new;
}

# Amazon::DynamoDB can't handle anything other than simple scalars
sub deflate {
    my ($self, %item) = @_;
    my %new;

    for my $key (keys %item) {
        my $value  = $item{$key};
        $new{$key} = ref $value
            ? JSON::XS->new->utf8->pretty->encode($value)
            : $item{$key};
    }



( run in 1.416 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )