C++でAmazon DynamoDBへアクセスするのにはどうしたら良いのか、いろいろと調べて実行できるところまでできました。
前提条件
Dockerがインストール済み
> docker --version
Docker version 18.09.1, build 4c52b90
AWS-CLIがインストール済み
> aws --version
aws-cli/1.16.27 Python/3.6.6 Darwin/17.7.0 botocore/1.12.17
その他環境
> sw_vers
ProductName: Mac OS X
ProductVersion: 10.13.6
BuildVersion: 17G4015
> g++ -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 10.0.0 (clang-1000.10.44.4)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
> gcc -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 10.0.0 (clang-1000.10.44.4)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
DynamoDBの準備(DynamoDB Localを利用)
下記を参考にさせていただき、DynamoDB Localを利用することにしました。
AWS SDK for C++も利用されていたのですが、SDKのビルドも試してみたかったので、今回はそちらは見送りました。
dockerでDynamoDB LocalとAWS SDK for C++の開発環境を動かす - Qiita
https://qiita.com/d9magai/items/8b5650656783ae8af1d5
DynamoDB Localコンテナを立ち上げる
Dockerイメージからコンテナを立ち上げます。
> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> mkdir dynamodblocal
> docker run -d \
-p 8000:8000 \
-v dynamodblocal:/var/dynamodblocal \
--name localdynamodb \
-t tray/dynamodb-local \
-sharedDb \
-dbPath /var/dynamodblocal
-sharedDb
オプションを指定しないとハマるそうです
気をつけましょう。
丁度よさげなイメージがこちらにありましたので、これを使って動かします。その際に-sharedDbを指定し、リージョン区分のエミュレート機能を無効にします。個人的にはこの機能はハマりポイントだと思うので(というかハマりました)無効にして単一のDBが作られるようにします。
コンテナが動作しているか確認します。
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efa64bb35f6d tray/dynamodb-local "/opt/jdk/bin/java -…" 9 seconds ago Up 7 seconds 0.0.0.0:8000->8000/tcp localdynamodb
DynamoDB Localにテーブルを作成する
コンテナが立ち上がっていることが確認できたので、テーブルを作成します。
テーブル名などは任意でどうぞ。
> 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
{
"TableDescription": {
"AttributeDefinitions": [
{
"AttributeName": "Artist",
"AttributeType": "S"
},
{
"AttributeName": "SongTitle",
"AttributeType": "S"
}
],
"TableName": "Music",
"KeySchema": [
{
"AttributeName": "Artist",
"KeyType": "HASH"
},
{
"AttributeName": "SongTitle",
"KeyType": "RANGE"
}
],
"TableStatus": "ACTIVE",
"CreationDateTime": 1548140414.002,
"ProvisionedThroughput": {
"LastIncreaseDateTime": 0.0,
"LastDecreaseDateTime": 0.0,
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
},
"TableSizeBytes": 32,
"ItemCount": 1,
"TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/Music"
}
}
テーブルが作成できたかを確認します。
> aws dynamodb list-tables \
--endpoint-url http://localhost:8000
{
"TableNames": [
"Music"
]
}
一応テーブルの中身も確認しておきます。
> aws dynamodb scan \
--table-name Music \
--endpoint-url http://localhost:8000
{
"Items": [],
"Count": 0,
"ScannedCount": 0,
"ConsumedCapacity": null
}
はい。まだ空っぽです。
これでDynamoDBの準備ができました。
AWS SDK for C++のインストール
C++でDynamoDBへアクセスするのにはどうしたら良いのか調べたらすぐにSDKが存在していることがわかったのでそれを利用します。
aws/aws-sdk-cpp: AWS SDK for C++
https://github.com/aws/aws-sdk-cpp
ビルド方法は下記ドキュメントの[Building the SDK from Source]を参考にしました。
Setting Up the AWS SDK for C++ - AWS SDK for C++
https://docs.aws.amazon.com/ja_jp/sdk-for-cpp/v1/developer-guide/setup.html
リポジトリを取得します。
> cd 任意のディレクトリ
> git clone https://github.com/aws/aws-sdk-cpp.git
CMakeでビルド準備?(MakeFileを作成)します。
CMakeがインストールされていない場合、下記でインストールできます。
> brew install cmake
-DBUILD_ONLY
を指定しないとSDK全体がビルドされるので、とても時間がかかります(ましたorz)のでご注意ください。
複数サービスを指定する場合は、セミコロン;
区切りとなります。CMakeで指定できるパラメータについては下記に詳しくあります。
CMake Parameters - AWS SDK for C++
https://docs.aws.amazon.com/ja_jp/sdk-for-cpp/v1/developer-guide/cmake-params.html
> cd 任意のディレクトリ
> mkdir aws-sdk-cpp-build
> cd aws-sdk-cpp-build
> cmake -DBUILD_ONLY="dynamodb" ../aws-sdk-cpp
-- Found Git: /usr/local/bin/git (found version "2.19.1")
-- TARGET_ARCH not specified; inferring host OS to be platform compilation target
-- Building AWS libraries as shared objects
-- Building project version: 1.7.39
-- The C compiler identification is AppleClang 10.0.0.10001044
-- The CXX compiler identification is AppleClang 10.0.0.10001044
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
(略)
-- Configuring done
-- Generating done
-- Build files have been written to: 任意のディレクトリ/aws-sdk-cpp-build
ソースファイルと別のディレクトリでビルドすることをout-of-sourceビルドというそうです。へぇ〜
CMake : out-of-sourceビルドで幸せになる - Qiita
https://qiita.com/osamu0329/items/7de2b190df3cfb4ad0ca
Makefileが作成されたらmake && make install
を実行します。
サービスを限定していてもそこそこ時間がかかりますので、休憩前にでもどうぞ。
> ls
AWSSDK aws-cpp-sdk-dynamodb
CMakeCache.txt aws-cpp-sdk-dynamodb-integration-tests
CMakeFiles aws-sdk-cpp-config.cmake
Makefile cmake_install.cmake
aws-cpp-sdk-core testing-resources
aws-cpp-sdk-core-tests
> make
Scanning dependencies of target aws-cpp-sdk-core
[ 0%] Building CXX object aws-cpp-sdk-core/CMakeFiles/aws-cpp-sdk-core.dir/source/AmazonSerializableWebServiceRequest.cpp.o
[ 0%] Building CXX object aws-cpp-sdk-core/CMakeFiles/aws-cpp-sdk-core.dir/source/AmazonStreamingWebServiceRequest.cpp.o
(略)
[----------] Global test environment tear-down
[==========] 305 tests from 49 test cases ran. (2723 ms total)
[ PASSED ] 305 tests.
[100%] Built target aws-cpp-sdk-core-tests
> make install
[ 28%] Built target aws-cpp-sdk-core
[ 84%] Built target aws-cpp-sdk-dynamodb
[ 86%] Built target testing-resources
[ 87%] Built target aws-cpp-sdk-dynamodb-integration-tests
[100%] Built target aws-cpp-sdk-core-tests
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/lib/cmake/AWSSDK/AWSSDKConfigVersion.cmake
-- Installing: /usr/local/lib/cmake/AWSSDK/platformDeps.cmake
-- Up-to-date: /usr/local/lib/cmake/AWSSDK
(略)
-- Installing: /usr/local/lib/cmake/testing-resources/testing-resources-config.cmake
-- Installing: /usr/local/lib/cmake/testing-resources/testing-resources-config-version.cmake
これでAWS SDK for C++が利用できるようになりました(なったはずです)。
C++で実装
C++初心者なので、まだ自前で実装ができません。
ですので、公式にあった下記のサンプルを利用させてもらいます。
C++ Code Samples for Amazon DynamoDB - AWS Code Sample
https://docs.aws.amazon.com/ja_jp/code-samples/latest/catalog/code-catalog-cpp-example_code-dynamodb.html
> cd 任意のディレクトリ
> touch put_item.cpp
> touch CMakeLists.txt
テーブルにデータをPUTする実装
put_item.cpp - AWS Code Sample
https://docs.aws.amazon.com/ja_jp/code-samples/latest/catalog/cpp-dynamodb-put_item.cpp.html
サンプルソースをほぼそのまま利用しますが、DynamoDB localへアクセスするようにclientConfig.endpointOverride = "http://localhost:8000";
を追加しています。
/*
Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
This file is licensed under the Apache License, Version 2.0 (the "License").
You may not use this file except in compliance with the License. A copy of
the License is located at
http://aws.amazon.com/apache2.0/
This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
#include <aws/core/Aws.h>
#include <aws/core/utils/Outcome.h>
#include <aws/dynamodb/DynamoDBClient.h>
#include <aws/dynamodb/model/AttributeDefinition.h>
#include <aws/dynamodb/model/PutItemRequest.h>
#include <aws/dynamodb/model/PutItemResult.h>
#include <iostream>
/**
* Put an item in a DynamoDB table.
*
* Takes the name of the table, a name (primary key value) and a greeting
* (associated with the key value).
*
* This code expects that you have AWS credentials set up per:
* http://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html
*/
int main(int argc, char** argv)
{
const std::string USAGE = \
"Usage:\n"
" put_item <table> <name> [field=value ...]\n\n"
"Where:\n"
" table - the table to put the item in.\n"
" name - a name to add to the table. If the name already\n"
" exists, its entry will be updated.\n\n"
"Additional fields can be added by appending them to the end of the\n"
"input.\n\n"
"Example:\n"
" put_item Cellists Pau Language=ca Born=1876\n";
if (argc < 2)
{
std::cout << USAGE;
return 1;
}
Aws::SDKOptions options;
Aws::InitAPI(options);
{
const Aws::String table(argv[1]);
const Aws::String name(argv[2]);
Aws::Client::ClientConfiguration clientConfig;
clientConfig.endpointOverride = "http://localhost:8000";
Aws::DynamoDB::DynamoDBClient dynamoClient(clientConfig);
Aws::DynamoDB::Model::PutItemRequest pir;
pir.SetTableName(table);
Aws::DynamoDB::Model::AttributeValue av;
av.SetS(name);
pir.AddItem("Name", av);
for (int x = 3; x < argc; x++)
{
const Aws::String arg(argv[x]);
const Aws::Vector<Aws::String>& flds = Aws::Utils::StringUtils::Split(arg, ':');
if (flds.size() == 2)
{
Aws::DynamoDB::Model::AttributeValue val;
val.SetS(flds[1]);
pir.AddItem(flds[0], val);
}
else
{
std::cout << "Invalid argument: " << arg << std::endl << USAGE;
return 1;
}
}
const Aws::DynamoDB::Model::PutItemOutcome result = dynamoClient.PutItem(pir);
if (!result.IsSuccess())
{
std::cout << result.GetError().GetMessage() << std::endl;
return 1;
}
std::cout << "Done!" << std::endl;
}
Aws::ShutdownAPI(options);
return 0;
}
CMakeListsの定義
CMakeLists.txt - AWS Code Sample
https://docs.aws.amazon.com/ja_jp/code-samples/latest/catalog/cpp-dynamodb-CMakeLists.txt.html
とりあえずPUTのサンプル実装のみ含めたいので、余分なコードをコメントアウトしています。
# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# This file is licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License. A copy of
# the License is located at
# http://aws.amazon.com/apache2.0/
# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
cmake_minimum_required(VERSION 3.2)
project(dynamodb-examples)
set (CMAKE_CXX_STANDARD 11)
# Locate the aws sdk for c++ package.
find_package(AWSSDK REQUIRED COMPONENTS dynamodb)
set(EXAMPLES "")
#list(APPEND EXAMPLES "batch_get_item")
#list(APPEND EXAMPLES "create_table")
#list(APPEND EXAMPLES "create_table_composite_key")
#list(APPEND EXAMPLES "delete_item")
#list(APPEND EXAMPLES "delete_table")
#list(APPEND EXAMPLES "describe_table")
#list(APPEND EXAMPLES "get_item")
#list(APPEND EXAMPLES "list_tables")
list(APPEND EXAMPLES "put_item")
#list(APPEND EXAMPLES "update_item")
#list(APPEND EXAMPLES "update_table")
# The executables to build.
foreach(EXAMPLE IN LISTS EXAMPLES)
add_executable(${EXAMPLE} ${EXAMPLE}.cpp)
target_link_libraries(${EXAMPLE} ${AWSSDK_LINK_LIBRARIES})
endforeach()
ビルドして実行する
実装の準備ができたら、ビルドして実行してみます。
> mkdir 任意のディレクトリ/build
> cd 任意のディレクトリ/build
> cmake ../
-- Found AWS SDK for C++, Version: 1.7.39, Install Root:/usr/local, Platform Prefix:, Platform Dependent Libraries: pthread;curl
-- Components specified for AWSSDK: dynamodb
-- Try finding aws-cpp-sdk-core
-- Found aws-cpp-sdk-core
-- Try finding aws-cpp-sdk-dynamodb
-- Found aws-cpp-sdk-dynamodb
-- Configuring done
-- Generating done
-- Build files have been written to: 任意のディレクトリ/build
> make
Scanning dependencies of target put_item
[ 50%] Building CXX object CMakeFiles/put_item.dir/put_item.cpp.o
[100%] Linking CXX executable put_item
ld: warning: text-based stub file /System/Library/Frameworks//CoreFoundation.framework/CoreFoundation.tbd and library file /System/Library/Frameworks//CoreFoundation.framework/CoreFoundation are out of sync. Falling back to library file for linking.
[100%] Built target put_item
ワーニングが出力されましたが、捨て置きます。
下記あたりが参考になるかもです。(未確認)
r - ld: warning: text-based stub file are out of sync. Falling back to library file for linking - Stack Overflow
https://stackoverflow.com/questions/51314888/ld-warning-text-based-stub-file-are-out-of-sync-falling-back-to-library-file
無事にビルドできたら実行してみます。
> ./put_item
Usage:
put_item <table> <name> [field=value ...]
Where:
table - the table to put the item in.
name - a name to add to the table. If the name already
exists, its entry will be updated.
Additional fields can be added by appending them to the end of the
input.
Example:
put_item Cellists Pau Language=ca Born=1876
実行できたので、パラメータを指定してDynamoDB LocalのテーブルにPUTできるか確認します。
> ./put_item Music Hoge Artist=hoge SongTitle="hoge hoge"
Invalid argument: Artist=hoge
Usage:
(略)
Invalid argument
でエラーとなりました。むむ。
実装をよくよく確認するとconst Aws::Vector<Aws::String>& flds = Aws::Utils::StringUtils::Split(arg, ':');
とあり、パラメータの区切り文字がコロン:
という罠でした。うぉぉ〜ぃ
パラメータ指定を見直して実行しなおします。
> ./put_item Music Hoge Artist:hoge SongTitle:"hoge hoge"
Done!
はい。Done!
実際にPUTされたか確認します。
> aws dynamodb scan \
--table-name Music \
--endpoint-url http://localhost:8000
{
"Items": [
{
"Artist": {
"S": "hoge"
},
"Name": {
"S": "Hoge"
},
"SongTitle": {
"S": "hoge hoge"
}
}
],
"Count": 1,
"ScannedCount": 1,
"ConsumedCapacity": null
}
やったぜ。
それにしてもやっぱりDynamoDBから返されるJSONそのままだとツラミ
おまけ(まだ良くわかっていない点)
今回はAWS SDK for C++をmake install
して/usr/local
配下へインストールして利用しました。そうするとAWS SDK for C++がインストールされていない環境では実行できません。
> cd 任意のディレクトリ/aws-sdk-cpp-build
> make uninstall
(略)
> cd ../build
> ./put_item Music Hoge Artist:hoge SongTitle:"hoge hoge"
dyld: Library not loaded: @rpath/libaws-cpp-sdk-dynamodb.dylib
Referenced from: 任意のディレクトリ/build/./put_item
Reason: image not found
fish: './put_item Music Hoge Artist:ho…' terminated by signal SIGABRT (Abort)
まだわかっていませんが、本来であれば、自前の実装にSDKが含まれるようにCMakeLists.txtで定義してやる必要がある?のでしょうか?
まとめ
C++で外部ライブラリをどう利用するのが良いのかわからないままに試してみましたが、とりあえずAWS SDK for C++を利用してAmazon DynamoDB(Local)へアクセスすることができました。
参考
dockerでDynamoDB LocalとAWS SDK for C++の開発環境を動かす - Qiita
https://qiita.com/d9magai/items/8b5650656783ae8af1d5
aws/aws-sdk-cpp: AWS SDK for C++
https://github.com/aws/aws-sdk-cpp
Setting Up the AWS SDK for C++ - AWS SDK for C++
https://docs.aws.amazon.com/ja_jp/sdk-for-cpp/v1/developer-guide/setup.html
C++ Code Samples for Amazon DynamoDB - AWS Code Sample
https://docs.aws.amazon.com/ja_jp/code-samples/latest/catalog/code-catalog-cpp-example_code-dynamodb.html