CloudWatch Logsのエクスポートタスクの制限(公式ドキュメントと違う？)

日本語ドキュメントの更新漏れかと思ったら英語ドキュメントも同じだったのでメモ。

## 忙しい人に2行で説明(3行いらなかった)

- ログのエクスポートタスクは複数並列実行できるよ
- 同じロググループに対しては並列実行できないよ

## 何が起きたか

CloudWatch Logsのログをデイリーで『とりあえず保存したい』という要望があったので、[ピンポイントな記事][cwl-to-s3] を
参考にしてLambda関数を実装してみた。
※参照先の人すみません、諸々都合により社内での実装は記事の内容からいろいろいじっています。

で、テストを兼ねて直近過去分をテストイベントに過去日付入力して順にポチポチ実行しようとしたところ、
初回実行は正常に終了したが2回目の実行で、

```:実行結果
{
  "errorMessage": "RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Process exited before completing request"
}
```

```:ログ
2018-12-10T09:53:27.821Z	xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx	wait for next request: name = ********, count = 1
2018-12-10T09:53:27.861Z	xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx	Resource limit exceeded. LimitExceededException: Resource limit exceeded.
    at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:48:27)
    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:685:12)
    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
```

`Resource limit exceeded` と出ていたので、[ドキュメント][cwl-limit-ja] を確認したところ、

> エクスポートタスク
> アカウントごとに、一度に 1 つのアクティブ (実行中または保留中) のエクスポートタスクがあります。この制限は変更できません。

どうも複数のタスクはまとめて実行できない仕様らしいので、エクスポートタスクを一つずつ処理する必要があるらしい。
一つ処理したら `COMPLETED` が付くまで待てということだろうか。 ~~待機とかめんどい~~

## 改めて振り返って

ここまで来て改めて実装を確認したところ、こんな感じになっていた。
(node.js 8.10 環境、ソースは社内ツールで公開できないのでイメージで作り直しています)

```javascript
// エクスポート定義単位の処理
const exportTask = async exportDef => {
  const aws = require('aws-sdk')
  const logs = new aws.CloudWatchLogs()
  return await logs.createExportTask({ /* options... */ }).promise()
}

// Lambdaのハンドラ関数
const handler = async (event, context) => {
  return await Promise.all([
    { group: "group-1", s3Pfx: "prefix1" },
    { group: "group-2", s3Pfx: "prefix2" }, // 他に5個ぐらい定義が続くイメージ
  ].map(exportTask))
}
```

要は複数の `ロググループ => S3の特定プレフィックス へのエクスポート` を非同期で処理する実装になっていた。
この実装で `COMPLETED` の待機などしなくても **初回実行は正常に終了** していたのである。

もしかしてと思って上記ハンドラを実行して数秒後にdescribeしたところ、、、

```bash
# aws-cliのバージョンが古いのでちょっと出力形式違います
$ aws logs describe-export-tasks | jq '.exportTasks[] | select (.status.code != "COMPLETED")'
{
  "status": {
    "message": "Started successfully",
    "code": "RUNNING"
  },
  "from": 1543536000000,
  "destinationPrefix": "***** s3 key prefix 1 *****",
  "destination": "***** s3 bucket name *****",
  "logGroupName": "***** export target log group 1 *****",
  "to": 1543622400000,
  "taskId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "taskName": "export_task_1",
  "executionInfo": {
    "creationTime": 1544433759054
  }
}
{
  "status": {
    "message": "Started successfully",
    "code": "RUNNING"
  },
  "from": 1543536000000,
  "destinationPrefix": "***** s3 key prefix 2 *****",
  "destination": "***** s3 bucket name *****",
  "logGroupName": "***** export target log group 2 *****",
  "to": 1543622400000,
  "taskId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
  "taskName": "export_task_2",
  "executionInfo": {
    "creationTime": 1544433759037
  }
}
```

∠( ﾟдﾟ)／　＜　`RUNNING` が2行見つかりましたっ。

ということで、複数のタスクが並列で実行できたとさ( ´ ▽ ` )ﾉ

### 改めて、ドキュメント(2018-12-10時点)

どちらも **アカウント単位** で制限があることを明記している。
このアカウントって **AWSアカウント** のことですよね？
エラーにある `Resource limit exceeded.` がここの制限であれば書いてある内容と違うような。。。

#### [日本語版][cwl-limit-ja]

再掲になるが一応

> エクスポートタスク
> アカウントごとに、一度に 1 つのアクティブ (実行中または保留中) のエクスポートタスクがあります。この制限は変更できません。


#### [英語版][cwl-limit-en]

> Export task
> One active (running or pending) export task at a time, **per account**. This limit cannot be changed.


## あとがき

試行した内容としては以下のとおりです。

- **1関数内** で複数のログエクスポートタスクを並列実行
    - 全て正常に処理された
    - 一時的に複数タスクが `RUNNING` とか `PENDING` で確認された
- 関数終了直後(まだ `RUNNING` のタスクがある時点で再度関数を実行
    - `Resource limit exceeded.` が発生した。

ことから、**同一ロググループに対するエクスポートタスク** は1アクティブに縛らられるのではないかと予想。

実は他の定義に抵触していたのでは？とか検証足らない部分もあるかもしれないので、ツッコミ等は適宜お出しいただければと思います(._.)


---- 

[cwl-to-s3]: CloudWatchLogsからS3へのエクスポートを自動化
[cwl-limit-ja]: https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html
[cwl-limit-en]: https://docs.aws.amazon.com/en_us/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html