古めのAWS CDKv2で作ったAuroraServerlessV2を新しいCDKに対応させたときに悩んだ話

[インフォ・ラウンジ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/info-lounge) の9日目です。

# summary
AWS CDKの2.82.0から、AuroraServerlessV2がL2で作成できるようになっています。ただし、旧バージョンのCDKでAuroraServerlessV2を作成した場合、そのままアップデートして`cdk deploy`してしまうと、インスタンスのリプレイスが発生し、運用に支障が出ることがわかりました。諸々の問題がありましたが、2.89.0で新方式に切り替えることができました。機能が追加された際にはドキュメントよく確認してアップデートを行いましょう


# (DEPRECATED)CDK 2.82.0未満を使用していた頃
先にも書きましたが、Aurora Serverless V2がCDKでL2対応したのは2.82.0からです

https://github.com/aws/aws-cdk/releases/tag/v2.82.0

それ以前のバージョンでは、直接L2 Constructでは記述が不可能であったため普通にデータベースクラスターを作成したあと、別枠で定義されていた`ServerlessV2ScalingConfigurationProperty`を使って以下のような形でDBクラスターにキャパシティを割り当てていました。これで作成は可能で運用はできていたのですが、エスケープハッチと呼ばれる手法を使用しており、ベストな方法なのかはわからず少しモヤモヤしていました。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_rds.CfnDBCluster.ServerlessV2ScalingConfigurationProperty.html

```typescript
const dbCluster = new rds.DatabaseCluster(this, 'DbCluster', {
  engine: rds.DatabaseClusterEngine.auroraMysql({
    // Aurora Serverless v2に対応した3系を指定
    version: rds.AuroraMysqlEngineVersion.VER_3_02_1
  }),
  instances: 2,
  instanceProps: {
    vpc: props.vpc,
    // インスタンスタイプにサーバーレスを指定
    instanceType: new ec2.InstanceType('serverless'),
    vpcSubnets: vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_ISOLATED }),
  }

  // ServerlessV2に対応していないので、Aspectsを使って設定を書き換える
  Aspects.of(dbCluster).add({
    visit(node) {
      if (node instanceof rds.CfnDBCluster) {
        node.serverlessV2ScalingConfiguration = {
          minCapacity: 0.5,
          maxCapacity: 4
        }
      }
    }
  })
}
```
エスケープハッチとは、L2の機能をL1を使って変更するもので、ここではAspectsを使って書き換えています。動くには動いていたのですが、説明すると深いのでこの点は今回は割愛します（参考リンクを置いておきます）。

https://docs.aws.amazon.com/cdk/v2/guide/aspects.html

https://docs.aws.amazon.com/cdk/v2/guide/cfn_layer.html

## CDK2.82.0以上を使用する
満を持してリリースされた2.82.0以降では、以下のようにしてAuroraServerlessV2のクラスタをエスケープハッチ無しで直接立ち上げることができるようになりました。

```typescript
const dbCluster = new rds.DatabaseCluster(this, 'DbCluster', {
  engine: rds.DatabaseClusterEngine.auroraMysql({
    version: rds.AuroraMysqlEngineVersion.VER_3_02_1
  }),
  vpc: props.vpc,
  vpcSubnets: props.vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_ISOLATED }),
  writer: rds.ClusterInstance.serverlessV2('Writer', {
    instanceType: new ec2.InstanceType('serverless')
  }),
  readers: [
   rds.ClusterInstance.serverlessV2('Reader', {
     scaleWithWriter: true
   })
  ],
  serverlessV2MinCapacity: 0.5,
  serverlessV2MaxCapacity: 4
})
```

変更点としては、`instances`および`instanceProps`が非推奨となり、`writer`と`readers`、`serverlessV2MaxCapacity`と`serverlessV2MinCapacity`が追加になりました。
（`writer`は単数形、`readers`は複数形ですね。）

これにより、書き込みインスタンスと読み込みインスタンスそれぞれに個別の設定がすることができるようになり、キャパシティ設定もAspectsを使うことなくプロパティとして割り当てるように記述できるようになりました。わかりやすいですね。


今から新たに作成するときはこちらが良いでしょう。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_rds.DatabaseCluster.html

# （はまった）cdkを2.82.0未満から以上へバージョンアップするとインスタンスの置換が発生してしまう。

問題が発生するのは、このバージョンをまたいでCDKをアップデートした場合です。
バージョンアップをしてから、`cdk diff`を行うと、インスタンスが再作成されるよと警告が出ます。

```bash
$ cdk --version
2.82.0

$ cdk diff DatabaseStack

(略)
Resources
[-] AWS::RDS::DBInstance DbCluster/Instance1 DatabaseInstance1XXXXX destroy
[+] AWS::RDS::DBInstance DbCluster/Writer DatabaseWriterYYYYY
```

それでは困るので調べて見ると、Identifierが変更されてしまうらしいことがわかったので、以下のようにインスタンスの識別名を既存インスタンスに従うようにしました。
（Identifierがすべての環境で一緒とは限らないので、それぞれの環境でよく確認してください）

```typescript
// 'Instance1' と明示的に指定する
writer: rds.ClusterInstance.provisioned('Instance1', {
  instanceType: new ec2.InstanceType('serverless'),
}),
readers: [
  rds.ClusterInstance.serverlessV2('Instance2', {
    scaleWithWriter: true
  })
],
```

しかし、それだけではdestroyの表示が消えません。
これはインスタンスが一度消え、同じ名前で再作成されるように見えます。

```diff
Resources
[-] AWS::RDS::DBInstance DbCluster/Instance1 DatabaseInstance1XXXXX destroy
[+] AWS::RDS::DBInstance DbCluster/Instance1 DatabaseInstance1XXXXX
```

そこで改めてドキュメントを参照してみると、`isFromLegacyInstanceProps` というマイグレーション時に使うものが実装されていることがわかったので利用してみます。

> Only used for migrating existing clusters from using instanceProps to writer and readers.

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_rds.ClusterInstanceProps.html#isfromlegacyinstanceprops

```typescript
// 'Instance1' と名前を指定しつつマイグレーション用のフラグを立てる
writer: rds.ClusterInstance.serverlessV2('Instance1', {
  instanceType: new ec2.InstanceType('serverless'),
  isFromLegacyInstanceProps: true
}),
readers: [
  rds.ClusterInstance.serverlessV2('Reader', {
    scaleWithWriter: true,
    isFromLegacyInstanceProps: true
  })
],
```

今度こそ、と思いながら実行しましたが、`isFromLegacyInstanceProps`が存在しないという警告が出てハマります。

```bash
Object literal may only specify known properties, and 'isFromLegacyInstanceProps' does not exist in type 'ServerlessV2ClusterInstanceProps'.
```

なぜだ。。。このために実装されたプロパティではなかったのか。。。
(ここでハマってアップデートできず)

＊＊＊

実はこの時点では`isFromLegacyInstanceProps`は `ProvisionedClusterInstanceProps`に実装されたプロパティであって、`ServerlessV2ClusterInstanceProps`には実装されていなかったのです。よく確認できていませんでした。。というか紛らわしい。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_rds.ServerlessV2ClusterInstanceProps.html

要望を出そうにもなんと説明するかとあれこれ悩んでいるうちに、これを解決するPRが立てられ、2.89.0で`ServerlessV2ClusterInstanceProps`にも`isFromLegacyInstanceProps`が実装されました。

https://github.com/aws/aws-cdk/pull/26472

> rds: isFromLegacyInstanceProps migration flag with ClusterInstance.serverlessV2

https://github.com/aws/aws-cdk/releases/tag/v2.89.0

これによりめでたくCDKを最新にすることができました。感謝ですね。[※]

```bash
$ cdk --version
2.89.0

$ cdk diff DatabaseStack
There were no differences
```

[※]: ちなみに、一度アップデートに成功したからといって `isFromLegacyInstanceProps`を消すことはできないようです（再びdestroy表示になる）。いずれ外せるのかもしれませんが。


# まとめ
CDK2.82.0未満で作られたAuroraServerlessV2があるときは以下のようにしましょう
- CDKを2.82.0以上にアップデートする
    - `ServerlessV2ClusterInstanceProps`を使用している場合は2.89.0以上
- インスタンスのIdentifier（名前）を一致させる
- `instance`,`instanceProps`をやめ、`writer`,`readers`に書き換える
- `isFromLegacyInstanceProps`を`true`に設定する


AWS CDKは次々と機能追加や仕様変更が入っています（現時点では2.114.1まで進んでいます）。基本的には新しいバージョンを使っていく方がよいですが、バージョンアップ時に予期せぬ変更が入ったり、インスタンスの置換や廃棄が行われてしまうことがあります。溜まっていくと多方面でdiffが発生して大変になってくるので、定期的にバージョンを上げるようにしたほうがいいですね。逆に<b>対応したからといってすぐに書き換えるのではなく</b>、変更点をよく読んで修正をあてていきましょう。