Amazon DynamoDB LocalをOSX(Mountain Lion)で実行する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ども、大瀧です。
先ほど、Amazon DynamoDBをローカルで実行する、DynamoDB LocalがAWS Blogで公開されました。早速、手元のMBAで動かしてみました。その軌跡を記録として残しておきます。

Oracle Java SE 7のインストール

OSXにプリインストールされるApple Java 6で動かそうと思ったのですが、以下の@KenTamagawaさんから指摘の通り、JDK 7でないと動作しません。

<blockquote class="twitter-tweet aligncenter" data-conversation="none" data-cards="hidden"><p>JDK7が必要ですー [現在失敗]Amazon DynamoDB LocalをOSX(Mountain Lion)で実行する|クラスメソッド株式会社 開発ブログ 書いた人 &gt; <a href="https://twitter.com/takipone">@takipone</a> <a href="http://t.co/MyU5VpLiI3">http://t.co/MyU5VpLiI3</a></p>&mdash; 玉川憲 (@KenTamagawa) <a href="https://twitter.com/KenTamagawa/statuses/378310384047767552">September 13, 2013</a></blockquote>

OSXにOracle Java SE 7がインストールされていなければ、Java SE 7をインストールします。Java SE Downloadsにアクセスし、[Accept License Agreement]のラジオボタンをクリックしつつ、最新のMac OS XのJava SEをダウンロードし、インストールします。

dynamodblocal_01

インストールが完了したら、Terminalを開き、java -versionコマンドでJDKのバージョンを確認します。

$ java -version
java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)
$

OKですね。

実行ファイルのダウンロードと展開

DynamoDB Localは、残念ながらソースは公開されていないようで、ビルド済みのファイルが以下のURLで公開されています。

    https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_2013-09-12.tar.gz

ダウンロードしたら、tarコマンドで任意のディレクトリに展開します。

$ tar zxvf dynamodb_local_2013-09-12.tar.gz
x dynamodb_local_2013-09-12/third_party_licenses/httpcore-nio-4.2_NOTICE.txt
x dynamodb_local_2013-09-12/third_party_licenses/commons-logging-adapters-1.1.1_LICENSE.txt
x dynamodb_local_2013-09-12/third_party_licenses/sqlite4java_LICENSE.txt
x dynamodb_local_2013-09-12/third_party_licenses/jackson_LICENSE.txt
x dynamodb_local_2013-09-12/third_party_licenses/servlet-api-3.0_LICENSE.txt
 (略)
$

展開したファイルを見ると、.jarファイルの他、各プラットフォームでの実行向けのライブラリファイルも見えますね。

$ cd dynamodb_local_2013-09-12
$ ls
DynamoDBLocal.jar               libsqlite4java-linux-i386.so    sqlite4java-win32-x64.dll
LICENSE.txt                     libsqlite4java-osx-10.4.jnilib  sqlite4java-win32-x86.dll
README.txt                      libsqlite4java-osx-ppc.jnilib   third_party_licenses/
libsqlite4java-linux-amd64.so   libsqlite4java-osx.jnilib

では、実行してみます。

$ java -jar DynamoDBLocal.jar –Djava.library.path=.
2013-09-13 23:57:54.313:INFO:oejs.Server:jetty-8.y.z-SNAPSHOT
2013-09-13 23:57:54.371:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8000
(プロンプトは戻ってこない。Ctrl + Cで終了)
^C $

デフォルトでは、0.0.0.0/0で8000番ポートをListenします。一応、javaコマンドの実行時に--portオプションを付加することで任意のポート番号に変更できるようですが、「Local」と謳うのであれば、ループバックアドレスの127.0.0.1で動かしたいところですよねー。やり方求ム!!

AWS CLIでテーブル作成

では、実際にアクセスしてみましょう。今回は先日正式リリースされたAWS CLIを使ってみます。以下のコマンドラインでは、サンプルとしてAMIの情報を管理するテーブルを作成しています。--endpoint-urlオプションがポイントです。

APIアクセスキーによる認証は行われないのですが、アクセスキーとシークレットキーは環境変数として任意の値で事前に設定しておく必要があります。

$ aws dynamodb create-table --endpoint-url http://localhost:8000 \
 --table-name inheritor_ami \
 --attribute-definitions AttributeName=ami-id,AttributeType=S \
 --key-schema AttributeName=ami-id,KeyType=HASH \
 --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "ami-id",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 1,
            "ReadCapacityUnits": 1
        },
        "TableSizeBytes": 0,
        "TableName": "inheritor_ami",
        "TableStatus": "ACTIVE",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "ami-id"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1379085241.731
    }
}
$

テーブルが作成できているか、確認します。

$ aws dynamodb list-tables --endpoint-url http://localhost:8000
{
    "TableNames": [
        "inheritor_ami"
    ]
}
$ aws dynamodb describe-table --table-name inheritor_ami  --endpoint-url http://localhost:8000
{
    "Table": {
        "AttributeDefinitions": [
            {
                "AttributeName": "ami-id",
                "AttributeType": "S"
            }
        ],
 (略)
$

OKですね。

AWS SDK for PHP2でItemをput/getしてみる

まずは、AWS SDK for PHP2でDynamoDB Localに接続するための設定から。AWS SDK for PHP2では、AWS APIにアクセスするための情報を、Aws::factoryメソッドの引数に連想配列で渡します。ここに、base_urlキーでDynamoDB Localのインターフェースおよびポート番号を指定します。

<?php

use Aws\Common\Aws;
use Aws\Common\Enum\Region;

$config = array(
  'key'    => getenv('AWS_ACCESS_KEY_ID'),
  'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
  'base_url' => 'http://localhost:8000',
  'region' => Region::TOKYO,
  'table_prefix' => 'inheritor_'
);
$aws = Aws::factory($config);
$client = $aws->get('DynamoDb');

あとは、通常のDynamoDBと特に変わりません。今回のサンプルは、モデルクラス(Ami)とDynamoDBのラッパクラス(DynamoDbWrapper)を使っているので、コードの見た目はアレですが、普通にput-itemしています。ソースを見たい方は、GitHubを参照ください。

<?php

use Aws\Common\Enum\Region;

$ddb = new DynamoDbWrapper($config);

$ami = new Ami();
$ami->setAmiId('ami-12345678');
$ami->setRegion(Region::TOKYO);
$ami->setParents(array('ami-11111111', 'ami-22222222'));
$ami->setChildren(array('ami-33333333', 'ami-44444444'));
$ami->setBrothers(array('ami-55555555', 'ami-66666666'));

$ddb->persist('ami', $ami);
try {
  $ddb->flush();
}
catch (DynamoDbException $e) {
  print $e;
}

あとは、Itemをgetするコードです。こちらもラッパのせいでDynamoDBのAPIを叩いている雰囲気が無くて申し訳ないです...。

try {
  $item = $ddb->find(array(
    'table_name' => 'ami',
    'item_key' => 'ami-id',
    'item_value' => 'ami-12345678'
  ));
  var_dump($item);
}
catch (DynamoDbException $e) {
  print $e;
}

実行結果としては以下の感じです。

上記コードのvar_dump関数の出力結果

array(5) {
  ["region"]=>
  string(14) "ap-northeast-1"
  ["parents"]=>
  array(2) {
    [0]=>
    string(12) "ami-11111111"
    [1]=>
    string(12) "ami-22222222"
  }
  ["children"]=>
  array(2) {
    [0]=>
    string(12) "ami-33333333"
    [1]=>
    string(12) "ami-44444444"
  }
  ["ami-id"]=>
  string(12) "ami-12345678"
  ["brothers"]=>
  array(2) {
    [0]=>
    string(12) "ami-55555555"
    [1]=>
    string(12) "ami-66666666"
  }
}

ちゃんと取り出せていますね。

DynamoDB Localのデータの実体

DynamoDB Localのデータの実体は、展開したファイルと同じディレクトリに<APIキー名>_<リージョン名>.dbファイルとして作成されます。fileコマンドで調べてみると、SQLiteのデータベースファイルでした。これは接続してみるしか!

$ file XXXXXXXXXXXXXXXXXXXX_ap-northeast-1.db
XXXXXXXXXXXXXXXXXXXX_ap-northeast-1.db: SQLite 3.x database
$ sqlite3 XXXXXXXXXXXXXXXXXXXX_ap-northeast-1.db
SQLite version 3.7.12 2012-04-03 19:43:07
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
dm             inheritor_ami
sqlite>

テーブルはdminheritor_amiの2つがあります。後者はaws dynamodbで作成したテーブルと同名なので、実データが入るテーブルのようです。ということは、dmテーブルは管理用テーブルというところでしょうか。調べてみます。

sqlite> .schema dm
CREATE TABLE dm (TableName TEXT, CreationDateTime INTEGER, LastDecreaseDate INTEGER, LastIncreaseDate INTEGER, NumberOfDecreasesToday INTEGER, ReadCapacityUnits INTEGER, WriteCapacityUnits INTEGER, TableInfo BLOB, PRIMARY KEY(TableName));
sqlite> select * from dm;
inheritor_ami|1379085241731|0|0|0|1|1|{"Attributes":[{"AttributeName":"ami-id","AttributeType":"S"}],"SQLiteIndex":{"":[{"DynamoDBAttribute":{"AttributeName":"ami-id","AttributeType":"S"},"KeyType":"HASH","SQLiteColumnName":"hashKey","SQLiteDataType":"TEXT"}]},"UniqueIndexes":[{"DynamoDBAttribute":{"AttributeName":"ami-id","AttributeType":"S"},"KeyType":"HASH","SQLiteColumnName":"hashKey","SQLiteDataType":"TEXT"}]}
sqlite> .schema inheritor_ami
CREATE TABLE "inheritor_ami" (hashKey TEXT DEFAULT NULL, hashValue BLOB NOT NULL, itemSize INTEGER DEFAULT 0, ObjectJSON BLOB NOT NULL, PRIMARY KEY(hashKey));
CREATE INDEX "inheritor_ami*HVI" ON "inheritor_ami" (hashValue);
sqlite> select * from inheritor_ami;
ami-12345678|�����F��{|133|{"region":{"S":"ap-northeast-1"},"parents":{"SS":["ami-11111111","ami-22222222"]},"children":{"SS":["ami-33333333","ami-44444444"]},"ami-id":{"S":"ami-12345678"},"brothers":{"SS":["ami-55555555","ami-66666666"]}}
sqlite>

予想通り、dmテーブルのデータとして、DynamoDBのinheritor_amiテーブルの定義情報が入っていました。inheritor_amiテーブルに、putしたItemが入っていることも確認できました。

まとめ

最後は探究心からちょっと余計なことをしてしまいましたが、触った限りではAmazon DynamoDBと同様に扱えることがわかりました。肝心のThroughputが無制限のためThroughputの制限による例外処理をデバッグできない点は残念ですが、手軽に動作確認できるツールとして非常に有用だと思います。

これまでクラウド破産が怖くてDynamoDBに手が出せなかったデベロッパーのあなた *1、DynamoDB Localを使ってDynamoDBデビューしましょう!

参考URL

脚注

  1. 普通に使う分には、クラウド破産なんてしないですが(汗)