Amazon-DynamoDB-Simple

 view release on metacpan or  search on metacpan

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

package Amazon::DynamoDB::Simple;
use Amazon::DynamoDB;
use Carp qw/cluck confess carp croak/;
use DDP;
use JSON::XS;
use Moo;
use Try::Tiny;

our $VERSION="0.01";

=head1 NAME

Amazon::DynamoDB::Simple - Simple to use and highly available

=head1 SYNOPSIS

    use Amazon::DynamoDB::Simple;

    my $table = Amazon::DynamoDB::Simple->new(
        table             => $table,       # required
        primary_key       => $primary_key, # required
        access_key_id     => ..., # default: $ENV{AWS_ACCESS_KEY_ID};
        secret_access_key => ..., # default: $ENV{AWS_SECRET_ACCESS_KEY};
    );

    # returns a hash
    my %item = $table->get($key);

    # create or update an item
    $table->put(%item);

    # mark item as deleted
    $table->delete($key);

    # returns a hash representing the whole table as key value pairs
    $table->items();

    # returns all the keys in the table
    $table->keys();

    # delete $old_key, create $new_key
    $table->rename($old_key, $new_key);

    # sync data between AWS regions using the 'last_updated' field to select
    # the newest data.  This method will permanently delete any items marked as
    # 'deleted'.
    $table->sync_regions();

    # This sets the value of the hosts attribute.  The value shown is the
    # default value.  You must use exactly two hosts for stuff to work atm.
    # Sorry.
    $table->hosts([qw/
            dynamodb.us-east-1.amazonaws.com
            dynamodb.us-west-1.amazonaws.com
    /]);

=head1 DESCRIPTION

DynamoDB is a simple key value store.  A Amazon::DynamoDB::Simple object
represents a single table in DynamoDB.

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.

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

        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};
    }

    return %new;
}

sub is_valid_json {
    my ($self, $json) = @_;
    eval { JSON::XS->new->utf8->pretty->decode($json) };
    return 0 if $@;
    return 1;
}

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

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

    for my $dynamodb (@$dynamodbs) {
        try { 
            $dynamodb->delete_item(
                TableName => $self->table,
                Key       => { $self->primary_key => $key },
            )->get;
            $success++;
        }
        catch {
            warn "caught error: " . p $_;
        };
    }

    confess "unable to permanently delete item from any dynamodb" unless $success;
}

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

    my $dynamodbs = $self->dynamodbs();
    my $success   = 0;
    my @items;

    for my $dynamodb (@$dynamodbs) {
        try { 
            push @items, $dynamodb->get_item(
                sub { shift },
                TableName => $self->table,
                Key       => { $self->primary_key => $key },
            )->get();
            $success++;
        }
        catch {
            warn "caught error: " . p $_;
        };
    }

    confess "unable to connect and get item from any dynamodb" unless $success;

    my $most_recent;
    for my $item (@items) {
        next unless $item;

        $most_recent = $item 
            if !$most_recent ||
                $most_recent->{last_updated} le $item->{last_updated};
    }

    return if $most_recent->{deleted};



( run in 3.406 seconds using v1.01-cache-2.11-cpan-524268b4103 )