前々回はAWS SDK for C++を使ってS3をいじってみました。今回はDynamoDBで試してみます。せっかくなのでDynamoDB Localをdockerで動かし、それを操作します。
準備
まずはdockerでDynamoDB Localを動かします。
dockerでDynamoDB Localを動かす
丁度よさげなイメージがこちらにありましたので、これを使って動かします。その際に-sharedDbを指定し、リージョン区分のエミュレート機能を無効にします。個人的にはこの機能はハマりポイントだと思うので(というかハマりました)無効にして単一のDBが作られるようにします。
$ sudo docker run -d \
                  -p 8000:8000 \
                  -v /var/dynamodblocal:/var/dynamodblocal \
                  --name localdynamodb \
                  -t tray/dynamodb-local -sharedDb -dbPath /var/dynamodblocal
DynamoDB Localにテーブルを作成
動かしたら次はaws cliでテーブルを作ります。
開発者ガイドをパクって、Music という名前のテーブルを作成します。パーティションキーは Artist で、ソートキーは SongTitle です。
DynamoDB Localに作るので--endpoint-url http://localhost:8000を指定しています。
$ aws dynamodb create-table --endpoint-url http://localhost:8000 \
                            --table-name Music \
                            --attribute-definitions AttributeName=Artist,AttributeType=S \
                                                    AttributeName=SongTitle,AttributeType=S \
                            --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \
                            --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 
DynamoDB JavaScript Shellを使って見てみる
http://localhost:8000/shell/ を開いてDynamoDB JavaScript ShellでDBにテーブルが作られたことを確認します。
こちらがテーブルの一覧を表示させるAPIです。
dynamodb.listTables(params, function(err, data) {
    if (err) console.log(err, err.stack); // an error occurred
    else     console.log(data);           // successful response
});
実行するとこのように表示されて、Musicテーブルが作られたことが確認できます。
{"TableNames":["Music"]}
また、こちらのAPIを実行するとMusicテーブルの中身を覗けます。
var params = {
    TableName: 'Music',
};
dynamodb.scan(params, function(err, data) {
    if (err) print(err); // an error occurred
    else print(data); // successful response
});
まだ何もありません。
{
  "Items": [],
  "Count": 0,
  "ScannedCount": 0
}
AWS SDK for C++を使ってDynamoDB Localにputする
さて、AWS SDK for C++を使ってputしようと思いますが、そのための実行環境として拙作のこちらのイメージを使おうと思います。
※余談
このイメージをDocker Hubでビルドしますと現状、必ず失敗します。ライブラリをmakeするときにメモリを使い過ぎるのが原因かな、と思ってます。1ヶ月くらい前の時点では失敗したり成功したりでしたが、今は何回ビルドしても成功しません。なんとかして解決したいものです。
コンテナを起動してプログラムを実行
まずはコンテナを起動します。
$ sudo docker run -i --link localdynamodb:localdynamodb -t d9magai/awssdkcpp bash
コンテナを起動したらprog.cppを書きます。Musicテーブルに歌手名と曲名と再生回数をputするプログラムです。
# include <aws/core/Aws.h>
# include <aws/dynamodb/DynamoDBClient.h>
# include <aws/dynamodb/model/PutItemRequest.h>
# include <aws/dynamodb/model/AttributeValueValue.h>
# include <aws/core/auth/AWSCredentialsProvider.h>
# include <aws/core/utils/Outcome.h>
const Aws::String AWS_ACCESS_KEY_ID = "XXXXXXXXXXXXXXXXXXXX";
const Aws::String AWS_SECRET_ACCESS_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
int main(int argc, char** argv) {
    try {
        Aws::SDKOptions  options;
        Aws::InitAPI(options);
        Aws::Client::ClientConfiguration config;
        config.scheme = Aws::Http::Scheme::HTTP;
        config.connectTimeoutMs = 1000;
        config.requestTimeoutMs = 1000;
        config.endpointOverride = "localdynamodb:8000";
        config.region = Aws::Region::AP_NORTHEAST_1;
        auto dynamoDbClient = Aws::DynamoDB::DynamoDBClient(Aws::Auth::AWSCredentials(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), config);
        Aws::DynamoDB::Model::PutItemRequest putItemRequest = Aws::DynamoDB::Model::PutItemRequest()
        .WithTableName("Music")
        .AddItem("Artist", Aws::DynamoDB::Model::AttributeValue().SetS("aiko"))
        .AddItem("SongTitle", Aws::DynamoDB::Model::AttributeValue().SetS("アンドロメダ"))
        .AddItem("Views", Aws::DynamoDB::Model::AttributeValue().SetN("3002"));
        auto putItemOutcome = dynamoDbClient.PutItem(putItemRequest);
        if(!putItemOutcome.IsSuccess()) {
            std::stringstream ss;
            ss << "PutItem failed with error: " << putItemOutcome.GetError().GetMessage() << std::endl;
            throw std::runtime_error(ss.str());
        }
        std::cout << "PutItem Success" << std::endl;
        Aws::ShutdownAPI(options);
    } catch (const std::exception &e) {
        std::cerr << e.what();
        return 1;
    }
    return 0;
}
接続先がDynamoDB Localなので、アクセスキーもシークレットキーもダミーで構いません。
コンパイルして実行してPutItem Successと表示されれば成功です。
$ g++ -DAWS_CUSTOM_MEMORY_MANAGEMENT -std=c++11 prog.cpp -I/opt/aws-sdk-cpp/include -L/opt/aws-sdk-cpp/lib/linux/intel64/ -laws-cpp-sdk-core -laws-cpp-sdk-dynamodb
$ ./a.out
PutItem Success
DynamoDB JavaScript ShellでMusicテーブルの中を見てみる
Musicテーブルの中身を確認し、ちゃんとデータがputされてるか見てみます。
http://localhost:8000/shell/ をもう一度開き、こちらのAPIでMusicテーブルをscanします。
var params = {
    TableName: 'Music',
};
dynamodb.scan(params, function(err, data) {
    if (err) print(err); // an error occurred
    else print(data); // successful response
});
ちゃんと登録されていたら、こんな感じで表示されます。
{
  "Items": [
    {
      "Artist": "aiko",
      "SongTitle": "アンドロメダ",
      "Views": 3002
    }
  ],
  "Count": 1,
  "ScannedCount": 1
}
DynamoDB LocalとAWS SDK for C++をdockerで動かせた
無事に動かせました。
ちなみにアクセスキー、シークレットキーとAws::Client::ClientConfiguration config;の値を正しく設定すればLocalではないほうのDynamoDBにも接続できます。今のところ私がAWS SDK for C++で使ったことがあるのはS3,DynamoDBくらいなので、もっと勉強していろいろ使ってみようと思います。
参考
https://aws.amazon.com/jp/blogs/aws/introducing-the-aws-sdk-for-c/
