DynamoDB StreamsのREMOVEイベントとTTL(Time to Live)でハマった話

最近、DynamoDB Streamsを利用して実装していた機能で、ちょっとハマったことがあったので紹介します。 TTL(Time to Live)によるレコードの削除に伴うREMOVEイベントの考慮が漏れてしまっていた、という内容です。
2019.06.11

こんにちは。 OSS大好きなshoitoです。

最近、DynamoDB Streamsを利用して実装していた機能で、ちょっとハマったことがあったので紹介します。 ユーザーの操作によるDynamoDBテーブルのレコードの削除に伴うREMOVEイベントは考慮していたけど、TTL(Time to Live)によるレコードの削除に伴うREMOVEイベントの考慮が漏れてしまっていた、という内容です。

以下の公式ガイドに気付いてれば良かったです...。

DynamoDB ストリーム および Time To Live https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/time-to-live-ttl-streams.html

DynamoDB StreamsとTTL(Time to Live)

DynamoDB Streamsは、DynamoDBテーブルのストリームの管理から有効化しておくと、レコードの追加、変更、削除のイベントをほぼリアルタイムに受けられるようになります。

DynamoDB ストリーム を使用したテーブルアクティビティのキャプチャ https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Streams.html

DynamoDB Streamsのイベントとしては、以下の3つが定義されています。 https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/APIReference/API_streams_Record.html

  • INSERT: レコードが追加された
  • MODIFY: レコードの属性が変更された
  • REMOVE: レコードが削除された

TTLは、以下のブログでも紹介されているように、指定した期間経過後にレコードを自動で削除する機能です。

DynamoDBの各データを自動で削除する機能(TTL:Time to Live)を試してみた

やりたかったこととハマったこと

DynamoDB Streamsを利用して実装していた機能なのですが、ユーザー操作により、DynamoDBテーブルのレコードが削除された際に、それをトリガーにLambdaを実行するものでした。

機能実装を終えた気になっていたんですが、開発環境にデプロイしてから数日経過後に 「TTLで消えたレコードのイベントも処理されてません?」 とチームメンバーに訊かれて、TTL設定されてたのか、おやおやとなりました。

DynamoDBテーブルからレコードが消された場合は REMOVE イベントが流れるので、ユーザーによる操作ではなくTTLでレコードが消された場合も同様です。 今回は ユーザー操作により、DynamoDBテーブルのレコードが削除された際に という条件だったので、TTLのケースは除外する必要がありました。

Lambdaが受け取るイベントのJSONを見ると、TTLでレコードが削除された時とそれ以外で削除された時では、内容が一部違いました。

ユーザー操作によりレコードが削除されたときのLambdaが受け取るJSON

{
'Records':[
{
'eventID':'7e3a89f72e3ea8c077aa5fa4de5661da',
'eventName':'REMOVE',
'eventVersion':'1.1',
'eventSource':'aws:dynamodb',
'awsRegion':'ap-northeast-1',
'dynamodb':{
'ApproximateCreationDateTime':1558672104.0,
'Keys':{
....
},
'OldImage':{
....
},
'SequenceNumber':'69657600000000044131896862',
'SizeBytes':300,
'StreamViewType':'NEW_AND_OLD_IMAGES'
},
'eventSourceARN':'....'
}
]
}

よく見ないと分からないのですが、TTLでレコードが削除された時は以下のように userIdentity が含まれている点が違います。

{
'Records':[
{
'eventID':'8558e9b1387c639f1ac3e73a8ced5a00',
'eventName':'REMOVE',
'eventVersion':'1.1',
'eventSource':'aws:dynamodb',
'awsRegion':'ap-northeast-1',
'dynamodb':{
'ApproximateCreationDateTime':1559538087.0,
'Keys':{
....
},
'OldImage':{
....
},
'SequenceNumber':'116861500000000027471850174',
'SizeBytes':330,
'StreamViewType':'NEW_AND_OLD_IMAGES'
},
'userIdentity':{
'principalId':'dynamodb.amazonaws.com',
'type':'Service'
},
'eventSourceARN':'...'
}
]
}

確かに、公式ガイドに記載されている通りですが、後で気付きました。 https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/time-to-live-ttl-streams.html

{
'Records':[
{
...
'userIdentity':{
'principalId':'dynamodb.amazonaws.com',
'type':'Service'
},
...
}
]
}

今回の解決策

Lambda(Python)では、TTLでレコードが削除されたケースのイベント(上記の userIdentity を含む)は、このようにフィルタリングできました。 ご参考までに。

def lambda_handler(event, context):
records = event['Records']
for record in records:
event_name = record['eventName']
if event_name == 'REMOVE' and record.get('userIdentity'):
if record['userIdentity']['type'] == 'Service' and record['userIdentity']['principalId'] == 'dynamodb.amazonaws.com':
# TTLでレコードが削除されたケースのハンドリング