SQL Server → Babelfish for Aurora PostgreSQL DMSで移行してみた

# はじめに
　普段、私はオンプレシステムのリプレイスを検討しているため、昨年12月に開催されたre:invent2024に参加した際もマイグレーションについて学んできました。
　その中で、SQL Serverのマイグレーション手段として、RDS for SQL Server以外にBabelfish for Aurora PostgreSQLのお話を聞く機会がありました。せっかくなので、オンプレSQL ServerからBabelfish for Aurora PostgreSQLへDMSを使ってフルロードで移行するマイグレーションをイメージして、動作を確認しながら取り組んでみました。

　Babelfishを触ってみたところ、SQL Server完全互換ではないこともあり、いくつか引っ掛かるポイントがありました。Babelfish関連の情報が少ないこともあり、備忘の意味も込めて、私が躓いたポイントを残していこうと思います。

# Babelfish for Aurora PostgreSQLとは

https://aws.amazon.com/jp/rds/aurora/babelfish/

　サービス紹介では、`コードをほとんどまたはまったく変更せずに、PostgreSQL で Microsoft SQL Server アプリケーションを実行`と謳われています。
　これを見た私の感想は、『`ほとんどまたはまったく変更せずに`とあるけど、少しは変更があるのか、まったく変更がないのか、違いはなんだろう？』でした。まずは触ってみるという方針で試行しはじめました。

　触れてみて、ドキュメントにも目を通して、当たり前ですが違いがありました。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/babelfish-compatibility.html

　ただ、システム規模、SQL Serverの使い方によって、この差異による影響度も大きく変わると思います。サービス紹介で`ほとんどまたはまったく変更せずに`と書かれていたのも、なんとなく納得しました。なお、Babelfishはバージョンアップの都度、SQL serverとの互換性が上がってきているようですので、今後にさらに期待できそうです。

# Data Migration Service (DMS)とは
https://docs.aws.amazon.com/dms/

オンプレやEC2上のRDBMS、RDSからRDSへデータ移行が行えるサービスです。SQL ServerからSQL Serverへの移行はフルバックアップ、リストアで移行することもできますが、今回は異種DB間の移行となるため、DMSを使いました。

# 検証のために準備した環境
　今回はSQL serverもあり、維持するとお金が結構かかってしまうため、消したり作ったりができるようにCloudFormation化しました。

<details>
<summary>(参考)CloudFormation yaml</summary>

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
# SQL ServerとAuroraのパスワード。念のためCloudFormation上でパスワードをセキュアに表示
  MasterUserPassword:
    Type: String
    NoEcho: true  
    Description: 'Password for SQL Server/Aurora PostgreSQL'

# Cloudformation外で作成するデータベース名を指定。PostgreSQLセッションで移行する場合は'babelfish_db'を入力
  DatabaseName:
    Type: String  
    Description: 'DatabaseName for SQL Server/Aurora PostgreSQL'

Resources:
# VPCを作成
  DMSVPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: "10.0.0.0/16"
      Tags:
        - Key: Name
          Value: "DMS-VPC"

# サブネットをマルチAZで作成
  Subnet1:
    Type: "AWS::EC2::Subnet"
    DependsOn: DMSVPC
    Properties:
      VpcId: !Ref DMSVPC
      CidrBlock: "10.0.1.0/24"
      AvailabilityZone: "ap-northeast-1a"
      Tags:
        - Key: Name
          Value: "Subnet1"

  Subnet2:
    Type: "AWS::EC2::Subnet"
    DependsOn: DMSVPC
    Properties:
      VpcId: !Ref DMSVPC
      CidrBlock: "10.0.2.0/24"
      AvailabilityZone: "ap-northeast-1c"
      Tags:
        - Key: Name
          Value: "Subnet2"

# 先ほど作ったサブネットをDBサブネットグループとして指定
  DBSubnetGroup:
    Type: "AWS::RDS::DBSubnetGroup"
    DependsOn:
      - Subnet1
      - Subnet2
    Properties:
      DBSubnetGroupDescription: "DB Subnet Group"
      SubnetIds:
        - !Ref Subnet1
        - !Ref Subnet2
      DBSubnetGroupName: "db-subnet-group"

# DMS用のセキュリティグループを作成。作成量を減らすため、SQL Server、Auroraも共用
  DmsSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    DependsOn: DMSVPC
    Properties:
      GroupDescription: "Security group for DMS"
      VpcId: !Ref DMSVPC
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 1433
          ToPort: 1433
          CidrIp: "0.0.0.0/0"
        - IpProtocol: "tcp"
          FromPort: 5432
          ToPort: 5432
          CidrIp: "0.0.0.0/0"
      SecurityGroupEgress:
        - IpProtocol: "-1"
          CidrIp: "0.0.0.0/0"

# SQL Server用のパラメータグループを作成
  SqlServerParameterGroup:
    Type: 'AWS::RDS::DBParameterGroup'
    Properties:
      Description: 'Parameter group for SQL Server'
      DBParameterGroupName: 'sqlserver'
      Family: sqlserver-se-15.0

# RDS for SQL Serverを作成
  RdsSqlServer:
    Type: "AWS::RDS::DBInstance"
    DependsOn:
      - DBSubnetGroup
      - DmsSecurityGroup
    Properties:
      DBInstanceIdentifier: "sqlserver2019"
      DBInstanceClass: "db.r6i.large"
      Engine: "sqlserver-se"
      EngineVersion: "15.00.4415.2.v1"
      MasterUsername: "sa"
      MasterUserPassword: !Ref MasterUserPassword
      CharacterSetName: 'japanese_cs_as' # 大文字・小文字を区別できるよう'cs'
      AllocatedStorage: "20"
      StorageType: "gp3"
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !Ref DmsSecurityGroup
      AvailabilityZone: "ap-northeast-1a"
      MultiAZ: false
      Timezone: 'Tokyo Standard Time'
      PubliclyAccessible: false
      DBParameterGroupName: !Ref SqlServerParameterGroup
      StorageEncrypted: true
      BackupRetentionPeriod: 1
      LicenseModel: "license-included"

# Aurora用パラメータグループを作成
  BabelfishClusterParameterGroup:
    Type: AWS::RDS::DBClusterParameterGroup
    Properties:
      Description: 'Babelfish for Aurora PostgreSQL'
      DBClusterParameterGroupName: 'Babelfish'
      Family: aurora-postgresql16
      Parameters:
        rds.babelfish_status: 'on'
        babelfishpg_tsql.default_locale: 'ja-JP'
        babelfishpg_tsql.server_collation_name: 'japanese_ci_as' # 'ci'しか指定できず
        babelfishpg_tds.port: '1433'
        babelfishpg_tsql.migration_mode: 'multi-db' #シングルデータベースモードの場合は、'single-db'を指定する

# Auroraクラスタを作成
  BabelfishCluster:
    Type: AWS::RDS::DBCluster
    DependsOn:
      - DBSubnetGroup
      - DmsSecurityGroup
      - BabelfishClusterParameterGroup
    Properties:
      DBClusterIdentifier: babelfish-test
      Engine: aurora-postgresql
      EngineVersion: '16.4'
      MasterUsername: "sa"
      MasterUserPassword: !Ref MasterUserPassword
      DBClusterParameterGroupName: !Ref BabelfishClusterParameterGroup
      VpcSecurityGroupIds:
        - !Ref DmsSecurityGroup
      DBSubnetGroupName: !Ref DBSubnetGroup
      BackupRetentionPeriod: 1
      StorageEncrypted: true
      EnableCloudwatchLogsExports:
        - iam-db-auth-error
        - instance
        - postgresql
      PerformanceInsightsEnabled: true
      PerformanceInsightsRetentionPeriod: 7

# Auroraインスタンスを作成
  BabelfishInstance:
    Type: AWS::RDS::DBInstance
    DependsOn: BabelfishCluster
    Properties:
      DBInstanceClass: db.t3.medium
      Engine: aurora-postgresql
      EngineVersion: '16.4'
      DBClusterIdentifier: !Ref BabelfishCluster
      AvailabilityZone: ap-northeast-1a
      StorageEncrypted: true
      PubliclyAccessible: false

# DMS用のIAMロールを作成
  DmsVpcRole:
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: "dms-vpc-role"
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: 
                - "dms.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      ManagedPolicyArns: 
        - "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole"

  DMSCloudWatchLogsRole:
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: "dms-cloudwatch-logs-role"
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: 
                - "dms.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      ManagedPolicyArns: 
        - "arn:aws:iam::aws:policy/service-role/AmazonDMSCloudWatchLogsRole"

# レプリケーションインスタンス用のサブネットを指定
  ReplicationSubnetGroup:
    Type: "AWS::DMS::ReplicationSubnetGroup"
    DependsOn:
      - RdsSqlServer
      - DBSubnetGroup
    Properties:
      ReplicationSubnetGroupIdentifier: "dms-replication-subnet-group"
      ReplicationSubnetGroupDescription: "dms replication subnet group"
      SubnetIds:
        - !Ref Subnet1
        - !Ref Subnet2

# DMS ソースエンドポイントを作成
  SourceEndpoint:
    Type: 'AWS::DMS::Endpoint'
    DependsOn: RdsSqlServer
    Properties:
      EndpointIdentifier: source-endpoint
      EndpointType: source
      EngineName: sqlserver
      Username: "sa"
      Password: !Ref MasterUserPassword
      ServerName: !Ref RdsSqlServer
      Port: 1433
      DatabaseName: !Ref DatabaseName
      SslMode: none

# DMS ターゲットエンドポイントを作成
  TargetEndpoint:
    Type: 'AWS::DMS::Endpoint'
    Properties:
      EndpointIdentifier: target-babelfish-endpoint
      EndpointType: target
      EngineName: Babelfish # PostgreSQLセッションにするなら aurora-postgresql を指定
      Username: "sa"
      Password: !Ref MasterUserPassword
      ServerName: !Ref BabelfishInstance
      Port: 1433 # PostgreSQLセッションにするなら 5432 を指定
      DatabaseName: !Ref DatabaseName
      SslMode: none

# レプリケーションインスタンスを作成
  ReplicationInstance:
    Type: 'AWS::DMS::ReplicationInstance'
    Properties:
      ReplicationInstanceClass: dms.t3.medium
      ReplicationInstanceIdentifier: ReplicationInstance
      AllocatedStorage: 50
      VpcSecurityGroupIds:
        - !Ref DmsSecurityGroup
      ReplicationSubnetGroupIdentifier: !Ref ReplicationSubnetGroup
      EngineVersion: '3.5.4'
      MultiAZ: false
      AvailabilityZone: ap-northeast-1c
      PubliclyAccessible: false

# DMS 移行タスクを作成。rename schemaのルールはマルチデータベースモードの時のみ必要
  DMSTask:
    Type: 'AWS::DMS::ReplicationTask'
    DependsOn:
      - SourceEndpoint
      - TargetEndpoint
      - ReplicationInstance
    Properties:
      ReplicationTaskIdentifier: dmstask-2019-to-babelfish
      SourceEndpointArn: !Ref SourceEndpoint
      TargetEndpointArn: !Ref TargetEndpoint
      MigrationType: full-load
      ReplicationInstanceArn: !Ref ReplicationInstance
      TableMappings: |
        {
          "rules": [
            {
              "rule-type": "selection",
              "rule-id": "1",
              "rule-name": "schema",
              "object-locator": {
                "schema-name": "dbo",
                "table-name": "%"
              },
              "rule-action": "include"
            },
            {
              "rule-type": "transformation", 
              "rule-id": "2",
              "rule-name": "rename schema",
              "rule-target": "schema",
              "object-locator": {
                "schema-name": "dbo",
                "table-name": "%"
              },
              "rule-action": "rename",
              "value": "testdb_dbo"
            }
          ]
        }
      ReplicationTaskSettings: |
        {
          "FullLoadSettings": {
            "TargetTablePrepMode": "DO_NOTHING",
            "CommitRate": 10000,
            "MaxFullLoadSubTasks": 8
          },
          "TargetMetadata": {
            "LimitedSizeLobMode": true,
            "LobMaxSize": 32,
            "SupportLobs": true
          },
          "ValidationSettings": {
            "EnableValidation": false
          },
          "Logging": {
            "EnableLogging": true,
            "EnableLogContext": true
          }
        }
```

</details>

イメージとしては以下のような構成です。
![アーキテクチャイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/e938e58e-d80e-4efb-8002-5951a5d2ed7d.png)


（1）ネットワーク周りの構築
（2）RDS for SQL Serverの構築
（3）Aurora PostgreSQL（with Babelfish）の構築
（4）DMS関連（IAMロール、ソースエンドポイント、ターゲットエンドポイント、レプリケーションインスタンス、移行タスク）の構築

CloudFormationについて、いくつか補足していきます。

## ネットワーク周り
　VPC、サブネット、DBサブネット、セキュリティグループを新規作成する形にしています。セキュリティグループはRDS、DMSで1つにまとめてしまいました。環境に合わせてカスタマイズしてください。

## RDS for SQL Serverの構築
　オンプレ相当のSQL Serverとして準備しました。CloudFormationの外で、インスタンス起動後、SQL Server側にサンプルDBを構築しておきました。
　DMSのソースエンドポイント作成時にデータベース名を指定する必要があるため、構築するサンプルデータベース名をCloudFormationのパラメータで予め指定しておきます。


## Aurora PostgreSQL（with Babelfish）の構築
　Babelfishを使うと以下の2つのポートでアクセスできます。
　分類は本記事で私が使い分けるためだけに記載するもので正式な名称ではありません。

|分類               |ポート   |アクセス方法例(ツール)        |
|:------------------:|--------|--------------------------|
|Babelfishセッション |1433/tcp|SQL Server Management Studio|
|PostgreSQLセッション|5432/tcp|pgAdmin                     |

Babelfish for Aurora PostgreSQLでは、インスタンス内に作成できるデータベース数の違いで以下の2つのモードがあります。
|モード                |動作                                |
|:-------------------:|------------------------------------|
|シングルデータベースモード|Aurora内でSQL Serverのデータベースが1つしか作れない|
|マルチデータベースモード  |Aurora内でSQL Serverの複数のデータベースが作れる  |
 
　移行対象として想定するデータベース数に合わせて選択することになります。パラメータグループで指定できますが、指定できるのは最初に作る時のみで、後から変えられません。ご注意ください。なお、Aurora PostgreSQL 16だとデフォルトがマルチデータベースモードになっていました。

（参考）パラメータのドキュメントはこちら

https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/babelfish-configuration.html

シングルデータベースモードとマルチデータベースモードについてはこちらに記載があります。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/babelfish-architecture.html

大きな違いはスキーマ名の違いと認識しました。

・シングルデータベースモード
SQL Serverのスキーマ名とBabelfishのスキーマ名が同じになります。
以下の形になります。
![スキーマの関係_シングルデータベース.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/25fc3c58-2293-4b71-9197-bfffed2ce795.jpeg)
左がSSMSを利用してBabelfishセッションで見えるデータベース・スキーマです。
右がpgAdminを利用してPostgreSQLセッションで見えるデータベース・スキーマです。
データベース名はtestdbとbabelfish_dbで変わっていますが、スキーマ名は同じdboになっています。

・マルチデータベースモード
SQL Serverのデータベース名とスキーマ名を組み合わせたものが、PostgreSQLのスキーマ名になります。
以下の形になります。
![スキーマの関係_マルチデータベース.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/cca2ed7c-5c48-4c22-b373-b87cafdf48c6.jpeg)
先ほどと同様に左がSSMSを利用してBabelfishセッションで見えるデータベース・スキーマで、右がpgAdminを利用してPostgreSQLセッションで見えるデータベース・スキーマです。
先ほどと同様にデータベース名は変わっているのに加えて、スキーマ名がdboに対してtestdb_dboになっていることが分かります。

マルチデータベースモードはPostgreSQLセッションで、テーブルを参照する際にスキーマ名の脳内変換が必要になるので、最初はちょっと面倒に感じました。

## DMS関連
ソースエンドポイント、ターゲットエンドポイント、レプリケーションインスタンスを作成。必要なIAMロールも準備します。
　レプリケーションインスタンスは、Aurora PostgreSQLインスタンスと同じサブネットに立てる形としました。

### DMSのタスク定義
今回はフルロード前提で検討を開始し、DMSによる移行に際しては以下のドキュメントを参考にしました。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/babelfish-migration.html

　今回はBabelfishセッションで移行をするパターンと、PostgreSQLセッションで移行するパターンの両方を実施しました。

　前述の通り、Babelfishセッションで見えるスキーマはSQL Serverで見えるものと同じです。一方でPostgreSQLセッションで見えるデータベース、スキーマは、SQL Serverと異なりますので、DMSで移行する際にはスキーマ名の変換を行う必要があります。

DMSの定義の中に変換ルールがあり、今回、マルチデータベースモードにてPostgreSQLセッションで移行を行うパターンを検証した際に、スキーマ名の変換を設定しました。
![DMS_スキーマ名変換.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/2220a097-4a62-42e7-a544-b7b86e12ecae.png)

なお、Babelfishの制約として移行タスク内のターゲットテーブル準備モードは「何もしない」を選択しないとエラーになりました。


## （その他）作業用EC2
　運用も考えると個人的にSSMSやpgAdmin4を使いたかったので、準備しました。
　※こちらについては、CloudFormation はありません。

# 躓きポイント
　以降、私が躓いたポイントです。自身の備忘録も兼ねて残していきます。


# その１：ロール
　Babelfishセッションでログインして、データベースとユーザー、ロールを作成。
いつものSQL Serverの流れでBabelfish側にマスターユーザーでログインしてユーザーを作成し、ロールを付与しようとしたところ```ALTER ROLE db_datareader```でエラーになりました。

```sql
-- ログインの作成
CREATE LOGIN TestUser WITH PASSWORD = 'TestPassword';

-- データベースユーザーの作成
CREATE USER TestUser FOR LOGIN TestUser;

-- ロールの付与
ALTER ROLE db_datareader ADD MEMBER TestUser;

-> role "db_datareader" does not exist

-- カスタムロールの作成
CREATE ROLE TestRole;

-- カスタムロールに権限付与
GRANT SELECT ON SCHEMA::dbo TO TestRole;

-- カスタムロールにユーザー追加
ALTER ROLE TestRole ADD MEMBER TestUser;
```
カスタムロールの作成と権限付与、ユーザー追加は問題なく実施できました。

db_datareaderやdb_datawriteロールが存在しないということで、ドキュメントを読んだら、サポート外の旨とアクセスコントロール用のSQLが準備されていました。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/babelfish-permissions.html#babelfish-permissions-supported

それぞれユーザーを作って権限を付与するなら以下のような形になると思います。
```sql
-- sysadmin権限付与
CREATE LOGIN User10 WITH PASSWORD = 'Test1234!';
ALTER SERVER ROLE sysadmin ADD MEMBER User10;

-- db_datareader相当の権限付与
CREATE LOGIN User11 WITH PASSWORD = 'Test1234!';
CREATE USER User11 FOR LOGIN User11;
GRANT SELECT ON SCHEMA::dbo TO User11;

-- db_datawrite相当の権限付与
CREATE LOGIN User12 WITH PASSWORD = 'Test1234!';
CREATE USER User12 FOR LOGIN User12;
GRANT INSERT,UPDATE,DELETE ON SCHEMA::dbo TO User12;
```

なお、Babelfishセッションで作成したユーザー情報を、PostgreSQLセッションから変更、削除しようとすると、エラーになるのでご注意ください。

![babelfish1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/98dcf56f-866d-46b2-b771-e03ce711589c.png)



# その2：GUIではできないこと多数
はい。互換性を取っているのはあくまでもT-SQLなので、当たり前ですね。
SSMSのGUI操作では、オブジェクトのプロパティ参照すらできませんでした。

![babelfish2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/77369447-bcc5-4055-aa0f-6760be380fc7.png)

普段からSQLをしっかり書いている方は全く問題ない話ですね。

# その3：照合順序
　正直、これが一番ハマりました。
 　インスタンス作成時にドキュメントを見たところ、Babelfishにおいてサーバレベルで日本語をサポートする照合順序は`japanese_ci_as`だけでした。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/babelfish-collations.html#babelfish-collations.reference-tables-supported-collations

　念のため、大文字・小文字を区別したいと考えたのですが、「japanese_cs_as」は選択できませんでした。カスタムパラメータで制御できないかと思い、いろいろ試してみたのですが、選択肢に入っておらず反映できませんでした。大文字・小文字を区別しているアプリケーションだと困るなと思いつつ、選択できないので、仕方なく「japanese_ci_as」で構築しました。

※ここで選択した結果によって、後段のDMSで移行タスクを実行した際に引っかかりましたので、後ほど対応したことを記載します。


# DMSで移行
　環境ができあがった後、Babelfishセッション、PostgreSQLセッションで、それぞれSQL Serverから移行してみましたので、以下に記載します。

## Babelfishセッションで移行
　SQL ServerからDMS経由でBabelfishセッションに対して、そのまま移行してみたら見事に失敗しました。Babelfish経由だとテーブルは自動で作成できませんでした。DMSでデータ移行をする時、データベースさえ作っていればテーブル作成はなくても移行できる（注：意図しない変換がかからないよう、本来はテーブルを作成するべきです）認識だったのですが、Babelfishセッションの場合はダメでした。スクリプトを出力して、Babelfishセッション側でDDLを流し込んでテーブルを作成しました。
 
　Babelfishセッションでテーブルを作成後、再度DMSの移行タスクを実行しましたが、またエラーになりました。DMSのCloud Watch logsを見てみると、プライマリーキーの違反とのこと。
 
 ```
 [TARGET_LOAD     ]E:  SqlState: 23000 NativeError: 2627 Message: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]duplicate key value violates unique constraint "info_pkc"
 ```

　SQL Server側ではレコード挿入時にPK違反は起きていませんでした。改めてSQL Serverでサンプルデータベースのテーブルのレコードを見つつ、DMSでS3に出力してみていると、PKのカラムに「Temp」と「temp」が存在しているのを発見。。
 　SQL Serverのテーブルでは大文字・小文字が区別される照合順序`japanese_cs_as`だったので「Temp」と「temp」が区別されて、PK違反にはなりませんでした。一方で、移行先のPostgreSQLでは、前述の通りデフォルトが`japanese_ci_as`になっていて、大文字・小文字の区別がされません。結果、DMSでPostgreSQLへINSERTした際にPK違反でNGになっていました。

　取り急ぎ、Babelfishセッションでテーブル作成時に流すDDLにおいて、大文字・小文字を区別できるよう、オブジェクトレベルで`japanese_cs_as`を指定する形にしてみました。（もっと良い方法をご存知の方、ぜひ教えてください）
　今回はお試しだったので、簡単に照合順序を変えましたが、アプリの仕様によっては照合順序の変更は注意が必要ですね。

　照合順序を指定してテーブルを再作成して、タスクを再実行。無事、移行できました。

![Babelfishセッション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/5d1ee9a1-8746-43e0-85a8-97106cdc57bc.png)

## PostgreSQLセッションで移行
　PostgreSQLセッションで移行する際は、Babelfishセッション側でデータベースを作成後、マスターユーザーに権限付与をしました。（DMSの移行タスクにおける事前評価時に、権限を付与するよう指摘がありました）
 
```sql
 GRANT SELECT, DELETE, INSERT, UPDATE ON SCHEMA::sys TO sa
```
タスクを実行したところ、正常に完了しました。

![PostgreSQLセッション.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/c5338a75-b1a7-4cb6-af3f-cf2b3adac058.jpeg)

　なお、PostgreSQLセッションで移行した際には、テーブルを事前に作成しなくても移行できました。また、PK違反も発生しませんでした。PK違反が起きなかったのはなぜだろうと思い、テーブル定義を確認したところ、テーブル作成時にデータベースのデフォルトの照合順序```en_US.UTF-8```が反映されて作成されていました。

![babelfish3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/22b97c8f-988b-4e05-813e-98b413b4abb9.png)

select文で実際に確認したところ、大文字・小文字の区別がされた結果が返ってきました。照合順序はしっかりと考えて対処しないといけませんね。

## （参考）移行速度
　複数回移行を試しました。BabelfishセッションとPostgreSQLセッションで移行時間を比べると、PostgreSQLセッションで移行した方が、転送時間が早かったです。

　移行データは同一の約940万レコード。移行元RDS、移行先Aurora（シングルデータベースモード）、DMSレプリケーションの各インスタンスは全て同じです。
以下、3回計測してみた平均値になります。
|パターン            | 時間 |
|:----------------:|:----:|
|Babelfishセッション |39'21"|
|PostgreSQLセッション|5'24"|

　結果的にBabelfishセッションだと、PostgreSQLセッションの7倍以上の移行時間を要しました。
CloudWatch、Performance Insightsなどで各リソースの使用状況を見てみたところ、Aurora PostgreSQLのCPU使用率が張り付いている状態でした。PostgreSQLセッションの際はAurora PostgreSQLのCPU使用率はピーク時で60％程度でしたので、Babelfishのオーバーヘッドがそれなりにかかるかもしれません。

## （参考）シーケンスオブジェクト
　みんな使うシーケンスオブジェクト。DMSの移行対象外ですが、こちらは特にハマることなく、Babelfishセッションで淡々と以下のクエリで作成できました。
 （もちろん、SSMSのGUI操作では作れませんでした）

```sql
-- シーケンス作成
CREATE SEQUENCE dbo.TestSequence
 AS [bigint]
 START WITH 0
 INCREMENT BY 1
 MINVALUE 0
 MAXVALUE 999999
 CYCLE 
 CACHE 1000
GO

-- シーケンスの取得、カウントアップも可能
SELECT NEXT VALUE FOR TestSequence;
```

## （参考）ストアドプロシージャ
　こちらもみんな使うストアドプロシージャ。こちらも以下でお試ししてみた結果、正常に動作しました。

 ```sql
--- プロシージャの作成。指定したステータスと一致しないレコードの件数をカウント
CREATE PROCEDURE CountNonMatchingStatus
    @InputStatus NVARCHAR(10)
AS
BEGIN
    SET NOCOUNT ON;

    SELECT COUNT(*) AS NonMatchCount
    FROM [user]
    WHERE [status] <> @InputStatus;
END;

--- プロシージャの実行も問題なし
EXEC CountNonMatchingStatus @InputStatus = 'active'
 ```
 

# まとめ
　一筋縄ではいきませんでしたが、一度うまくいくと、あぁ、そういうことかと理解が進み、慣れていきました。私がハマったポイントについて、より良い対処方をご存知の方がいましたら、ぜひ教えていただけるとうれしいです。

　インスタンスの費用面を考えるとRDS for SQL ServerはSQL Serverのラインセンスでそれなりの金額になってしまうため、Aurora PostgreSQLにすることで費用が抑えられると考えます（Babelfish自体は無料）。
　一方で、BabelfishはSQL Server完全互換ではありませんので、アプリの作りによっては改修費がある程度発生するリスクがあるのと、運用面もSQL ServerとAuroraで大きく変わるので、運用担当の学習コストも考慮する必要があります。全体で見てトータルでコスト効果が出せるか、というところが判断基準になりそうです。
　コスト効果が出せるようであれば、まずはBabelfishを使った状態でAurora PostgreSQLへ移行し、SQL Serverと比べてランニングコストを抑えながらAWS上で稼働させる。徐々にpgSQLにポーティングしていって、最終的にはBabelfishから脱却するという移行シナリオがベストのように考えました。
  私個人としては色々触ってみて楽しかったので、変化が楽しめる人にはオススメです。
  
  この内容が、SQL Serverのマイグレーションを目指すどなたかの参考になれば幸いです。