この記事は公開されてから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)で実行する|クラスメソッド株式会社 開発ブログ 書いた人 > <a href="https://twitter.com/takipone">@takipone</a> <a href="http://t.co/MyU5VpLiI3">http://t.co/MyU5VpLiI3</a></p>— 玉川憲 (@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をダウンロードし、インストールします。
インストールが完了したら、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>
テーブルはdmとinheritor_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
- Additional Tools and Libraries For Amazon DynamoDB - Amazon DynamoDB
- Configuration — AWS SDK for PHP 2.4.5 documentation
脚注
- 普通に使う分には、クラウド破産なんてしないですが(汗) ↩