Posted at

DynamoDBのCloudWatchメトリクス(キャパシティユニット)を盛大に誤解していた話

More than 1 year has passed since last update.

DynamoDBの監視をしたくて、CloudWatchメトリクスのキャパシティユニットを扱おうとしていたけど、公式ページを読んでもあまり理解できなく。。。

わかった気になって実装をしていたら、後から盛大に勘違いしていたことに気づいた話。


DynamoDB CloudWatchメトリクス(キャパシティユニット)

スクリーンショット 2018-06-08 8.51.35.png

これは読みに関する記述ですが、なんとなーくわかった気になれる書き方。


やりたかったこと


  • DynamoDBのキャパシティユニット使用量を確認し、プロビジョンドキャパシティユニットとして設定した値の閾値(例:80%)を超えたら通知を出す

  • 上述のために、キャパシティユニットの使用量を割合で出せるようにする。


実装のフロー


  1. 対象テーブルの読み/書きにおけるプロビジョンドキャパシティユニットをCloudWatchメトリクスから取得する。(ProvisionedWriteCapacityUnits, ProvisionedReadCapacityUnits)

  2. 実際のキャパシティユニットの使用量をCloudWatchメトリクスから取得する。(ConsumedWriteCapacityUnits, ConsumedReadCapacityUnits)

  3. No.1,2で取得した値を用いて、Metric Mathを利用してキャパシティユニットの使用量を算出する。


boto3を利用した実装方法

こんな感じで実装しようとしました。(仕様を誤解していたため、誤った実装です)

指定していた条件は以下。(書き込みキャパシティユニットの計算を想定)GetMetricData関数を使います。


  1. 1時間分のCloudWatchメトリクスを取得する

  2. 期間を5分で指定する

  3. 5分間の平均キャパシティユニットを取得する

(なんども書きますが、この実装は間違ってます)

response = cloudwatch_client.get_metric_data(

MetricDataQueries=[
{
'Id': 'm1',
'MetricStat': {
'Metric': {
'Namespace': 'AWS/DynamoDB',
'MetricName': 'ProvisionedWriteCapacityUnits',
'Dimensions': [
{
'Name': 'TableName',
'Value': TABLE_NAME
},
]
},
'Period': 300,
'Stat': 'Average',
'Unit': 'Count'
},
'Label': 'ProvisionedWriteCapacityUnits'
},
{
'Id': 'm2',
'MetricStat': {
'Metric': {
'Namespace': 'AWS/DynamoDB',
'MetricName': 'ConsumedWriteCapacityUnits',
'Dimensions': [
{
'Name': 'TableName',
'Value': TABLE_NAME
},
]
},
'Period': 300,
'Stat': 'Average',
'Unit': 'Count'
},
'Label': 'ConsumedWriteCapacityUnits'
},
{
'Id': 'calculationResult',
'Expression': 'm2/m1'
'Label': 'CalculationResult',
'ReturnData': True
}
],
StartTime=start_time,
EndTime=end_time
# StartTimeとEndTimeには1時間の開きを指定


間違いポイント

CloudWatchメトリクスのページに以下の記述があるのですが、この点を誤って理解していました。


  • Average – 消費されたリクエストごとの平均読み込みキャパシティー。

Averageは「消費されたリクエストごとの平均」を出すので、あくまでリクエスト毎の平均値を出すだけでした。

→5分間の間に10回のアクセスがあり、それぞれの使用ユニット量が「1, 2, 2, 3, 2, 2, 1, 1, 4, 3」ならAverageで取得した値は「2.1」になります(21 / 10)。この平均の計算に「期間」が利用されない、ということを理解していませんでした。


正しい算出方法

よくよく見れば「注記」に書かれていることをやればいいのですが、

(期間分のユニット使用量の合計値)/(期間[秒])

という計算をしてあげる必要があります。クエリで表すとこんな感じです。

response = cloudwatch_client.get_metric_data(

MetricDataQueries=[
{
'Id': 'm1_tmp', # 変更点
'MetricStat': {
'Metric': {
'Namespace': 'AWS/DynamoDB',
'MetricName': 'ProvisionedWriteCapacityUnits',
'Dimensions': [
{
'Name': 'TableName',
'Value': TABLE_NAME
},
]
},
'Period': 300,
'Stat': 'Sum', # 変更点
'Unit': 'Count'
},
'Label': 'ProvisionedWriteCapacityUnits'
},
{
'Id': 'm2',
'MetricStat': {
'Metric': {
'Namespace': 'AWS/DynamoDB',
'MetricName': 'ConsumedWriteCapacityUnits',
'Dimensions': [
{
'Name': 'TableName',
'Value': TABLE_NAME
},
]
},
'Period': 300,
'Stat': 'Sum', # 変更点
'Unit': 'Count'
},
'Label': 'ConsumedWriteCapacityUnits'
},
# 下記のブロックを追加。
{
'Id': 'm1',
'Expression': 'm1_tmp/300',
'Label': 'm1_tmp',
},
{
'Id': 'calculationResult',
'Expression': 'm2/m1'
'Label': 'CalculationResult',
'ReturnData': True
}
],
StartTime=start_time,
EndTime=end_time
# StartTimeとEndTimeには1時間の開きを指定