Amazon Web Services ブログ

Amazon Aurora 向け Advanced JDBC Wrapper Driver のご紹介

本記事は 2023/09/11に投稿された Introducing the Advanced JDBC Wrapper Driver for Amazon Aurora を翻訳した記事です。
翻訳はソリューションアーキテクトの Kenta Nagasue が担当しました。

現代のモダンアプリケーションには、スケーラビリティと耐障害性が求められています。その中でも最も重要なのがスケーラビリティで、アプリケーションの規模によっては、数百万人のユーザーにオンデマンドで対応できる能力が必要とされます。
e コマース、金融サービス、ゲームなどのステートフルアプリケーションでは、高可用性のデータベースが不可欠です。

2015年の Amazon Aurora リリース以降、お客様は Aurora クラスターでリレーショナルデータベースを実行できるようになりました。このクラスターは1つのライターノードと最大15個の低レイテンシーなリーダーノードで構成されています。これにより、アプリケーションは読み取りを大幅にスケーリングできるようになりました。
しかし、マルチインスタンスをサポートするデータベースと同様に、開発者は特別なイベントに対処するため、スイッチオーバーやフェイルオーバーなどの複雑なアプリケーションロジックを構築する必要がありました。

複雑なエンドポイントロジックをアプリケーションに実装する代わりに、スイッチオーバーやフェイルオーバーなどを処理する機能が、Advanced JDBC (Java Database Connectivity) Wrapper Driver に用意されています。さらに、このドライバーは AWS Secrets Manager または AWS Identity and Access Management (IAM) を使った認証処理も統合しています。
Advanced JDBC Wrapper Driver は Apache 2.0 ライセンスのオープンソースプロジェクトとしてリリースされています。プロジェクトの詳細は GitHub で確認できます。

この記事では、Advanced JDBC Wrapper Driver のいくつかの機能の使い方について詳しく説明します。

Advanced JDBC Wrapper Driver の機能

Advanced JDBC Wrapper Driver は、ネイティブの PostgreSQL または MySQL の JDBC ドライバーを”ラップ”し、基盤となるドライバーへの関数呼び出しの前にフェイルオーバーと認証の機能を追加します。API の実行にネイティブドライバーを使用することで、コードの複雑さを減らし、機能追加に集中できます。

Aurora は単一インスタンスではなく、DB インスタンスのクラスターを提供します。各接続は特定の DB インスタンスによって処理されます。Aurora クラスターに接続する際、指定するホスト名とポートは、エンドポイントと呼ばれる仲介ハンドラーを指します。Aurora はこのエンドポイント機構を使ってこれらの接続を抽象化しています。

以下のセクションでは、このドライバーが軽減するいくつかの課題について説明します。

スイッチオーバー

スイッチオーバーとは、リーダーがライターに昇格することを指します。これは、ライターの設定変更によりライターが再起動する必要がある場合に発生します。
クライアントには、クラスターのライターエンドポイントまたはリーダーエンドポイントに接続するオプションがあります。通常はライターエンドポイントに接続します。このエンドポイントは現在のライターインスタンスを指しています。クラスターのパラメーター変更やフェイルオーバーによりクラスターが再起動すると、リーダーの1つが新しいライターに昇格します。この際、クラスターライターエンドポイントが更新され、DNS でインスタンスエンドポイントが新しいライターを指すよう変更されます。DNS の伝播に時間がかかるため、この変更に最大30秒かかる可能性があります。通常、アプリケーションにはデータベースが一時的に利用できなくなった後の再接続ロジックが必要です。Advanced JDBC Wrapper Driver には、自動的に検出して接続を新しいライターに切り替える機能が組み込まれています。Aurora データベース内のトポロジー情報を利用することで、ドライバーは約6秒で切り替えを行えます。

IAM との統合

IAM を使うと、AWS でどのユーザがサービスとリソースにアクセスできるかを管理し、細かいアクセス許可を一元管理でき、AWS 全体のアクセスを分析して許可を最適化できます。IAM はトークン(パスワード)を自動的に定期的に変更することで、セキュリティを強化します。

アプリケーション内で IAM トークンの取得と使用のロジックを記述することも全く問題ありませんが、トークンの取得とキャッシングを扱う必要があるためアプリケーションが複雑になります。このドライバーにはその機能が組み込まれているので、スイッチオーバー時でも、アプリケーションはこれらの複雑さを考慮する必要がありません。IAM ポリシーの作成と、IAM データベースアクセス用の使い方の詳細については、「IAM データベースアクセス用の IAM ポリシーの作成と使用」を参照してください。

Secrets Manager との統合

Secrets Manager を使えば、資格情報をまとめて保存・管理できます。資格情報を管理することで、セキュリティ要件に応じて定期的に資格情報を更新(ローテーション)できます。Secrets Manager には、接続認証に使うユーザー名とパスワードを保存できます。この機能を有効にすると、ドライバーは Secrets Manager から資格情報を取得し、接続認証に使用します。

ソリューションの概要

Advanced JDBC Wrapper を使わない場合、データベースに接続してクエリを実行する典型的な Java コードは以下のようになります。

while(true) {
    try (Connection connection = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD)) {
        try (PreparedStatement preparedStatement = connection.prepareStatement("SELECT SUM(a) FROM IntegerOverflowTest;")) {
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                resultSet.next();
                long l = resultSet.getLong(1);
                System.out.println("sum(a) -> " + l);
            }
        }catch (SQLException ex){
        /*
        * catch exceptions relative to the select and handle them 
        * if the exception is connection closed then reconnect and try again
        */
        }
    }catch (SQLException ex){
    /*
     * catch and handle connection errors here
     */
    }
Java

スイッチオーバーが原因で接続に失敗した場合、新しいライターがオンラインになるまで接続は失敗します。Advanced JDBC Wrapper Driver を使わない場合、DNS の伝播を待つ必要があり、アプリケーションはおよそ30秒間ライターに接続できなくなります。
一方、Advanced JDBC Wrapper Driver を使えば、再接続を自動で試みます。初期接続時に、ドライバーはクラスターのトポロジーを読み取り、インスタンスエンドポイントを取得します。そのため、接続が失敗した場合でも、ドライバーはクラスター内の全インスタンスの IP アドレスを認識しています。ユーザーアプリケーションで接続が失敗すると、ドライバーはインスタンスをポーリングし、接続を試みます。接続が確立されると、新しいトポロジーを読み取り、新しいライターの IP アドレスを取得して接続します。この一連の処理がドライバー内で行われるため、ユーザーアプリケーションは接続の切断や失敗を認識することはありません。ドライバーは例外をスローし、接続が新しいサーバーに切り替わったことをユーザーアプリケーションに通知します。処理中のトランザクションで障害が発生した場合、トランザクションは失敗し、リトライする必要がある事を示す例外がユーザアプリケーションに投げられます。その為、ユーザーアプリケーションではこの例外を適切に特定し、処理するための小さな変更が必要になります。

接続 URL は、PostgreSQL JDBC ドライバーの場合は jdbc:aws-wrapper:postgresql:、MySQL JDBC ドライバーの場合は jdbc:aws-wrapper:mysql: となります。Advanced JDBC Wrapper Driver は jdbc:aws-wrapper で始まる部分を受け取り、次の部分からラップする基盤ドライバーを判別します。
以下のコードは、Aurora PostgreSQL クラスターに接続し、フェイルオーバーを設定する方法を示しています。

properties.setProperty(PropertyDefinition.PLUGINS.name,
                        "auroraConnectionTracker,failover,efm");
while( true ) {
    try (Connection connection =
        DriverManager.getConnection("jdbc:aws-wrapper:postgresql://" + WRITER + "/read_db", properties)) {
        try (Statement statement = connection.createStatement()) {
            try (ResultSet rs = statement.executeQuery("select 1")) {
                while (rs.next()) ;
            }
        } catch (FailoverSuccessSQLException ex) {
            /*
            we can safely ignore this error
            the connection was re-established
            create the statement again and execute the query.
            */

        } catch (TransactionStateUnknownSQLException ex) {
        // ignore this as well since we aren't really in a transaction
        }

    } catch (FailoverFailedSQLException ex) {
    /*
      At this point the driver was unable to re-establish the connection so
      just loop and get a new connection.
    */
    } catch (SQLException ex) {
    /*
    any number of exceptions above, handle as normal
    */
    }
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    //ignore
    }
}
Java

インスタンスエンドポイントを使って新しいライターを探索し、通常はドライバーが障害を検知してから約6秒で再接続できます。DNS キャッシュを待つ必要がないので、はるかに高速な再接続が可能になっています。

前提条件

Advanced JDBC Wrapper Driver は、Java Proxy Pattern を利用して実装されています。つまり、ネイティブの PostgreSQL または MySQL ドライバーを依存関係として追加する必要があります。

Advanced JDBC Wrapper Driver を使用するアプリケーションの作成

次のセクションでは、Advanced JDBC Wrapper Driver を使用するアプリケーションを作成するための手順を説明します。

Maven の依存関係追加

Maven を使ってビルドする場合は、pom.xml ファイルに以下の Maven の依存関係を追加してください。

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version
</dependency
<dependency>
<groupId>software.amazon.jdbc</groupId>
<artifactId>aws-advanced-jdbc-driver</artifactId>
<version>2.2.3</version>
</dependency>
Bash

IAM プラグインを使用したい場合は、以下も追加する必要があります。

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>rds</artifactId>
    <version>2.20.49</version>  
</dependency>
Bash

Secrets Manager プラグインを使用したい場合は、以下も追加してください。

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>secretsmanager</artifactId>
    <version>2.20.88</version>    
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>    
</dependency>
Bash

コードを更新する

正しい依存関係を追加した後、Advanced JDBC Wrapper Driver を使うためにアプリケーションにいくつかの変更が必要です。 接続 URL は、PostgreSQL の場合は jdbc:aws-wrapper:postgresql、MySQL の場合は jdbc:aws-wrapper:mysql にする必要があります。URLの最初の部分で AWS Advanced JDBC Wrapper がロードされ、2番目の部分でどの基盤ドライバーをロードしてプロキシ(Java Proxy Pattern)するかを指示します。

ドライバー機能を指定する

使用したいドライバーの機能を決定します。Advanced JDBC Wrapper Driver はプラグインを使って JDBC メソッドを実行します。プラグインは、JDBC メソッド呼び出しの前後に追加のロジックを加えるための拡張可能なコードモジュールと考えられます。Advanced JDBC Wrapper Driver には、いくつかの組み込みプラグインがあります。これは、wrapperPlugins ドライバープロパティで設定します。詳細は「Connection Plugin Manager Parameters」を参照してください。デフォルト設定は auroraConnectionTracker,failover,efm です。auroraConnectionTracker プラグインは、フェイルオーバー発生時に障害ノードへのオープン接続をすべて閉じるようにします。failover プラグインは、実際のフェイルオーバーの検知と再接続を処理します。efm プラグインは、フェイルオーバー時の再接続時間を減らすため、積極的にホストを監視します。

Secrets Manager との統合

AWS には Secrets Manager というサービスがあり、秘密情報を安全に保存できます。Advanced JDBC Wrapper Driver にはプラグインが用意されており、ユーザー名とパスワードの値を Secret に保存し、認証時に USER と PASSWORD の値として取得することができます。
必要な設定は、REGION_PROPERTYSECRET_ID_PROPERTYPLUGINS の3つのプロパティのみです。

以下のコードブロックが例となります:

public static void main(String[] args) throws SQLException {
    /* Set the AWS Secrets Manager Connection Plugin parameters and
    the JDBC Wrapper parameters.
    */
    final Properties properties = new Properties();
    REGION_PROPERTY.set(properties, "us-east-2");
    SECRET_ID_PROPERTY.set(properties, "secretId");

    // Enable the AWS Secrets Manager Connection Plugin.
    PLUGINS.set(properties, "awsSecretsManager");

    // Try and make a connection:
    try (final Connection conn = DriverManager.getConnection(CONNECTION_STRING, properties);
         final Statement statement = conn.createStatement();
         final ResultSet rs = statement.executeQuery("SELECT * FROM employees")) {
      while(rs.next()){
        System.out.println(rs.getString()...);
      }
    }
  }
Java

IAM 認証の統合

Advanced JDBC Wrapper Driver は、IAM 認証をサポートしています。IAM データベース認証を使用する場合、ホスト URL はカスタムドメインや IP アドレスではなく、有効な Amazon エンドポイントでなければなりません。例: db-identifier.cluster-XYZ.us-east-2.rds.amazonaws.com
IAM 認証には AWS Java SDK RDS v2.xCLASSPATH に別途含まれる必要があります。AWS Java SDK RDS はランタイム依存関係であり、解決されなければなりません。AWS SDK を使う場合は、AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY を設定して認証する必要があります。
IAM データベース認証は、特定のデータベースエンジンに限定されています。制限事項と推奨事項の詳細については、「MariaDB、MySQL、および PostgreSQL の IAM データベース認証」を参照してください。

IAM 認証の設定

IAM 認証を設定するには、以下の手順を実行します。

  1. Amazon RDS コンソールで、既存のデータベースもしくは新規作成のデータベースに対して IAM データベース認証を有効化します。
  2. IAM データベース認証用の IAM ポリシーを設定します。
  3. IAM データベース認証を使ってデータベースアカウントを作成します。選択したプライマリ DB インスタンスに接続します。
    1. MySQL データベースの場合、以下のコマンドで新しいユーザーを作成します。
      CREATE USER example_user_name IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
      SQL
    2. PostgreSQL データベースの場合、以下のコマンドで新しいユーザーを作成します。
      CREATE USER db_userx; GRANT rds_iam TO db_userx;
      SQL

Advanced JDBC Wrapper Driver で IAM 認証を使うには

Advanced JDBC Wrapper Driver で IAM 認証を使う場合のサンプルコードは次の通りです:

public class AwsIamAuthenticationPostgresqlExample {
  public static final String POSTGRESQL_CONNECTION_STRING =
      "jdbc:aws-wrapper:postgresql://db-identifier.XYZ.us-east-2.rds.amazonaws.com:5432/employees";
  private static final String USERNAME = "john_smith";

  public static void main(String[] args) throws SQLException {

    final Properties properties = new Properties();

    // Enable AWS IAM database authentication and configure driver property values
    properties.setProperty(PropertyDefinition.PLUGINS.name, "iam");
    properties.setProperty(PropertyDefinition.USER.name, USERNAME);

    // Attempt a connection
    try (Connection conn = DriverManager.getConnection(POSTGRESQL_CONNECTION_STRING,
    properties);
        Statement statement = conn.createStatement();
        ResultSet result = statement.
                    executeQuery("select aurora_db_instance_identifier()")) {

      System.out.println(Util.getResult(result));
    }
  }
}
Java

クリーンアップ

この記事を通して作成した Aurora クラスターの IAM 認証情報、Secrets Manager の認証情報がある場合は、必ず削除するようにしてください。

まとめ

Advanced JDBC Wrapper Driver は、AWS の IAM や Secrets Manager と Aurora が提供するクラスター構成を利用して、認証とフェイルオーバーのソリューションを実現しています。さらに、Aurora の機能を活用することで、フェイルオーバー時間を大幅に短縮できます。このドライバーにはまだ多くの機能があり、今後の投稿で紹介していく予定です。続報をお待ちください!
それまでの間、この投稿に対するコメントをお寄せいただき、GitHub プロジェクトを訪れて詳細と例を確認してください。

著者について

Dave Cramer はAmazon Web Services のシニアソフトウェアエンジニアです。また、PostgreSQL JDBC ドライバのメンテナーとして、PostgreSQL の主要な貢献者でもあります。彼の情熱はクライアントインターフェースとクライアントとの連携にあります。