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

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

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

# DynamoDB CloudWatchメトリクス（キャパシティユニット）
![スクリーンショット 2018-06-08 8.51.35.png](https://qiita-image-store.s3.amazonaws.com/0/102769/b68577b3-4e32-26e3-21b2-a12d3569db05.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分間の平均キャパシティユニットを取得する

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

```py
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）。この平均の計算に「期間」が利用されない、ということを理解していませんでした。

# 正しい算出方法
よくよく見れば「注記」に書かれていることをやればいいのですが、

（期間分のユニット使用量の合計値）／（期間[秒]）

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

```py
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時間の開きを指定
```