この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
こんにちは、中山です。
少し前の話ですがDynamoDBにTTL機能が追加されました。属性の作成日時をベースに特定期間経過した場合、自動的にアイテムを削除してくれる機能です。DynamoDBを一時的なデータの保管場所として利用しているユースケースでは、嬉しいアップデートだったのではないでしょうか。以前までは、バッチ処理などでアイテムの削除を実施するアプリを自分で作り込む必要がありました。が、この機能を利用することによりAWSにまるっとその部分をおまかせできるという訳です。より詳しい内容は以下のリンクを参照ください。
DynamoDBはサーバレスアーキテクチャのデータストアとしてよく利用されます。私はServerless FrameworkやAWS SAM(AWS Serverless Application Model)をよく利用しているので、それらのツールから使いたかったのですが、執筆時点(2017/04/29)ではCloudFormationのAWS::DynamoDB::TableおよびAWS::Serverless::SimpleTableリソースがTTL用プロパティをサポートしていません。そのため、少し工夫が必要になります。
という訳で、本エントリではこれらのツールからDynamoDBのTTL機能を利用する方法をご紹介します。
Serverless Frameworkの場合
以下のプラグインを利用することで対応可能です。
使い方は簡単です。プラグインのインストール後、 serverlelss.yml
を修正するだけです。
- プラグインのインストール
$ npm install --save serverless-dynamodb-ttl
serverless.yml
の修正
# プラグインの読み込み
plugins:
- serverless-dynamodb-ttl
# TTLの設定
custom:
dynamodb:
ttl:
- table: your-dynamodb-table-name
field: your-ttl-property-name
例えば、以下のように serverless.yml
を定義したとします。
service: dynamodb-ttl
custom:
config: ${file(config.yml)}
dynamodb:
ttl:
- table: ${self:custom.config.tableName}
field: ttl
frameworkVersion: ">=1.12.1 <2.0.0"
provider:
name: aws
runtime: nodejs6.10
stage: ${self:custom.config.stage}
region: ${self:custom.config.region}
cfLogs: true
iamRoleStatements:
- Sid: DynamoDBAccess
Effect: Allow
Resource:
- Fn::Join: [ "", [ "arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":", "table/", Ref: TestTable ] ]
Action:
- dynamodb:*
plugins:
- serverless-dynamodb-ttl
package:
include:
- src/**
exclude:
- .git/**
- config.yml
- package.json
- serverless.yml
- node_modules/**
functions:
scan:
handler: src/handlers/dynamodb/index.scan
environment:
TableName:
Ref: TestTable
put:
handler: src/handlers/dynamodb/index.put
environment:
TableName:
Ref: TestTable
resources:
Resources:
TestTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.config.tableName}
AttributeDefinitions:
- AttributeName: ttl
AttributeType: N
- AttributeName: time
AttributeType: S
KeySchema:
- AttributeName: ttl
KeyType: HASH
- AttributeName: time
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
デプロイ後にHookがバインドされているので sls deploy
の最後に、以下のような出力が表示されTTLが有効化されます。
$ sls deploy -v -r ap-northeast-1
<snip>
Serverless: Enabling TTL setting(s) for DynamoDB
DynamoDBのテーブルを確認すると、意図した動作がされていることを確認できます。
$ aws dynamodb describe-time-to-live \
--table-name testTable
{
"TimeToLiveDescription": {
"TimeToLiveStatus": "ENABLED",
"AttributeName": "ttl"
}
}
注意点があります。このプラグインでは、現状(v0.0.6)DynamoDBオブジェクトを作成する際のリージョン指定がオプションから渡されるため、 -r <region>
でCLIから明示的にリージョンを指定する必要があります。この指定をしないと、以下のようなエラーが表示されます。
Serverless: Enabling TTL setting(s) for DynamoDB
Config Error -------------------------------------------
Missing region in config
AWS SAMの場合
AWS SAMはプラグイン機構がないので、自分で作り込む必要があります。とはいえ、大した話ではないです。実装方法はいくつか考えられますが、一番シンプルな方法はLambda-backed Custom ResourceでUpdateTimeToLive APIを叩く方法だと思います。実装方法はLambdaがまだDead Letter Queue用プロパティをサポートしていない際にご紹介した方法とほぼ同じです。
メインとなるテンプレートを以下のように定義しておきます。
---
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: DynamoDB TTL Main Stack
Resources:
DynamoDB:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: src/templates/dynamodb.yml
Scan:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/handlers/dynamodb
Handler: index.scan
Runtime: nodejs6.10
Policies:
- Version: 2012-10-17
Statement:
- Sid: DynamoDBAccess
Effect: Allow
Action:
- dynamodb:*
Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${DynamoDB.Outputs.TableName}
Environment:
Variables:
TableName: !GetAtt DynamoDB.Outputs.TableName
Put:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/handlers/dynamodb
Handler: index.put
Runtime: nodejs6.10
Environment:
Variables:
TableName: !GetAtt DynamoDB.Outputs.TableName
Policies:
- Version: 2012-10-17
Statement:
- Sid: DynamoDBAccess
Effect: Allow
Action:
- dynamodb:*
Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${DynamoDB.Outputs.TableName}
Custom:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: src/templates/custom.yml
Parameters:
TableName: !GetAtt DynamoDB.Outputs.TableName
AttributeName: ttl
Outputs:
TableName:
Value: !GetAtt DynamoDB.Outputs.TableName
Scan:
Value: !Ref Scan
Put:
Value: !Ref Put
Lambda-backed Custom Resourceを利用するテンプレートを以下のように定義します。ちゃんとエラー処理を実装していない点は生暖かく見守ってください。
---
AWSTemplateFormatVersion: 2010-09-09
Description: DynamoDB TTL Custom Stack
Parameters:
TableName:
Type: String
AttributeName:
Type: String
Resources:
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Sid: AssumeRolePolicy
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: !Sub ${AWS::StackName}-Policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: DynamoDBAccess
Effect: Allow
Action:
- dynamodb:UpdateTimeToLive
Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${TableName}
LambdaUpdateTimeToLive:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt LambdaRole.Arn
Runtime: nodejs6.10
Code:
ZipFile: |
const response = require('cfn-response');
const AWS = require('aws-sdk');
exports.handler = (event, context, callback) => {
let responseData = {};
if (event.RequestType === 'Delete') {
response.send(event, context, response.SUCCESS, responseData);
}
const dynamodb = new AWS.DynamoDB();
let params = {
TableName: event.ResourceProperties.TableName,
TimeToLiveSpecification: {
AttributeName: event.ResourceProperties.AttributeName,
Enabled: true
}
};
dynamodb.updateTimeToLive(params, (err, data) => {
if (err) {
console.log(err, err.stack);
response.send(event, context, response.FAILED, responseData);
} else {
response.send(event, context, response.SUCCESS, responseData);
}
});
};
CustomUpdateTimeToLive:
Type: Custom::UpdateTimeToLive
Version: 1.0
Properties:
ServiceToken: !GetAtt LambdaUpdateTimeToLive.Arn
TableName: !Ref TableName
AttributeName: !Ref AttributeName
いつも通り aws cloudformation package
と aws cloudformation deploy
実施後、DynamoDBのテーブルを確認すると、以下のように意図した結果になっていることが確認できます。
$ aws dynamodb describe-time-to-live \
--table-name dynamodb-ttl-DynamoDB-13DRL8A11KJYA-TestTable-1LP3MV3HIR1U8{
"TimeToLiveDescription": {
"TimeToLiveStatus": "ENABLED",
"AttributeName": "ttl"
}
}
まとめ
いかがだったでしょうか。
Serverless FrameworkとAWS SAMからDynamoDBのTTL機能を利用する方法をご紹介しました。2つのツールはかなり似ていますが、できること/できないことが微妙に異なります。今後も、これらのツールについてご紹介していきたいと思います。
本エントリがみなさんの参考になれば幸いに思います。