[アップデート] Amazon Aurora DSQL に .NET(Npgsql)と Rust(SQLx)向けコネクターが追加されたので .NET で使ってみた
いわさです。
Aurora DSQL の各言語向けコネクターが続々とリリースされています。
コネクターは既存のデータベースドライバーをラップして IAM トークンの自動生成や SSL 設定、コネクションプーリング、OCC リトライなどを透過的に提供してくれるもので、先日は Ruby 向けのコネクターを紹介しました。
今回、新たに .NET(Npgsql)と Rust(SQLx)向けのコネクターがリリースされました。
これで Python、Go、Node.js、Java、Ruby に加えて .NET と Rust もカバーされ、主要な言語のコネクターがほぼ出揃った形ですね。
.NET の場合、従来は AWSSDK.DSQL の DSQLAuthTokenGenerator でトークンを生成し、Npgsql の接続文字列にパスワードとして渡す必要がありました。コネクターを使うとこのあたりが全部自動化されます。
今回こちらを確認してみたので紹介します。
使ってみた
では早速 .NET のコネクターを使ってみましょう。
以下のあたりのドキュメントを参考にしました。
まず、従来の方法とコネクターを使った方法を比較してみます。
従来は AWSSDK.DSQL と Npgsql を使って以下のように書く必要がありました。
using Amazon.DSQL;
using Amazon.DSQL.Model;
using Amazon.Runtime;
using Npgsql;
var clusterEndpoint = "ivtvahlywbnhlbci5ujpzw5wja.dsql.ap-northeast-1.on.aws";
var region = Amazon.RegionEndpoint.APNortheast1;
// トークンを生成
var credentials = FallbackCredentialsFactory.GetCredentials();
var token = DSQLAuthTokenGenerator.GenerateDbConnectAdminAuthToken(
credentials, region, clusterEndpoint);
// Npgsql で接続
var connStr = new NpgsqlConnectionStringBuilder
{
Host = clusterEndpoint,
Port = 5432,
Database = "postgres",
Username = "admin",
Password = token,
SslMode = SslMode.VerifyFull
}.ToString();
await using var conn = new NpgsqlConnection(connStr);
await conn.OpenAsync();
await using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT 'Hello from Aurora DSQL!' AS greeting";
var greeting = await cmd.ExecuteScalarAsync();
Console.WriteLine(greeting);
トークン生成、SSL モード、リージョン指定、ポート番号など、接続に必要なパラメータを全て自分で管理する必要があります。
さらに、トークンの有効期限は 15 分なので、長時間動作するアプリケーションではトークンのリフレッシュも自前で実装する必要がありました。
これを今回のコネクターで行うと次のようになります。
using Amazon.AuroraDsql.Npgsql;
var clusterEndpoint = "ivtvahlywbnhlbci5ujpzw5wja.dsql.ap-northeast-1.on.aws";
await using var conn = await AuroraDsql.ConnectAsync(new DsqlConfig
{
Host = clusterEndpoint,
Profile = "hoge"
});
await using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT 'Hello from Aurora DSQL!' AS greeting";
var greeting = await cmd.ExecuteScalarAsync();
Console.WriteLine(greeting);
DsqlConfig にホスト名を渡すだけです。かなりシンプルになりますね。
IAM トークン生成、SSL 設定、リージョン検出がすべて自動で行われます。ホスト名からリージョンが自動検出されるので、リージョンの指定も不要です。
セットアップ
.NET 8.0 以上が必要です。コンソールアプリのプロジェクトを作成して NuGet パッケージをインストールします。
% dotnet new console -n DsqlSample
The template "Console App" was created successfully.
Processing post-creation actions...
Restoring /Users/iwasa.takahito/work/hoge0405dsqldotnet/DsqlSample/DsqlSample.csproj:
Restore succeeded.
% cd DsqlSample
% dotnet add package Amazon.AuroraDsql.Npgsql
info : X.509 certificate chain validation will use the fallback certificate bundle at '/usr/local/share/dotnet/sdk/10.0.101/trustedroots/codesignctl.pem'.
:
info : Writing assets file to disk. Path: /Users/iwasa.takahito/work/hoge0405dsqldotnet/DsqlSample/obj/project.assets.json
log : Restored /Users/iwasa.takahito/work/hoge0405dsqldotnet/DsqlSample/DsqlSample.csproj (in 2.93 sec).
事前に東京リージョンでクラスターを作成しておきます。
接続テスト
まずは最もシンプルな接続テストから。
using Amazon.AuroraDsql.Npgsql;
var clusterEndpoint = "ivtvahlywbnhlbci5ujpzw5wja.dsql.ap-northeast-1.on.aws";
await using var conn = await AuroraDsql.ConnectAsync(new DsqlConfig
{
Host = clusterEndpoint,
Profile = "hoge"
});
await using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT 'Hello from Aurora DSQL!' AS greeting";
var greeting = await cmd.ExecuteScalarAsync();
Console.WriteLine(greeting);
% dotnet run
Hello from Aurora DSQL!
AuroraDsql.ConnectAsync にホスト名を渡すだけで接続できていることがわかります。
コネクションプールと CRUD 操作
次に、コネクションプールを使ったテーブルの作成とデータの読み書きを試してみます。
AuroraDsql.CreateDataSourceAsync で NpgsqlDataSource ベースのコネクションプールを作成できます。
using Amazon.AuroraDsql.Npgsql;
var clusterEndpoint = "ivtvahlywbnhlbci5ujpzw5wja.dsql.ap-northeast-1.on.aws";
// コネクションプール作成(OCC リトライ有効)
await using var ds = await AuroraDsql.CreateDataSourceAsync(new DsqlConfig
{
Host = clusterEndpoint,
Profile = "hoge",
OccMaxRetries = 3
});
// テーブル作成
await ds.ExecWithRetryAsync(
"CREATE TABLE IF NOT EXISTS hogeusers (id UUID DEFAULT gen_random_uuid() PRIMARY KEY, name TEXT NOT NULL)");
// データ挿入(トランザクション + OCC リトライ)
await ds.WithTransactionRetryAsync(async conn =>
{
await using var cmd1 = conn.CreateCommand();
cmd1.CommandText = "INSERT INTO hogeusers (name) VALUES (@name)";
cmd1.Parameters.AddWithValue("name", "hoge");
await cmd1.ExecuteNonQueryAsync();
await using var cmd2 = conn.CreateCommand();
cmd2.CommandText = "INSERT INTO hogeusers (name) VALUES (@name)";
cmd2.Parameters.AddWithValue("name", "fuga");
await cmd2.ExecuteNonQueryAsync();
});
// データ取得
await using (var conn = await ds.OpenConnectionAsync())
{
await using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT id, name FROM hogeusers";
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
Console.WriteLine($"{reader["id"]} : {reader["name"]}");
}
}
% dotnet run
04848cd0-1916-43d2-a50f-72286995c750 : hoge
3cc92d8e-e1b2-4740-8a13-2534e849d232 : hoge
4f87f7ee-c51a-448e-acc5-5cf10f7bf2d7 : fuga
eaa3c339-a726-4346-a31a-254ac28dc80f : fuga
テーブル作成、データ挿入、取得がすべて問題なく動作しました。
DDL の実行には ExecWithRetryAsync を使い、トランザクション付きの書き込みには WithTransactionRetryAsync を使います。
WithTransactionRetryAsync は内部で BEGIN/COMMIT/ROLLBACK を管理してくれるので、コールバック内ではデータベース操作だけを書けば良いです。
OCC リトライ
Aurora DSQL は楽観的同時実行制御(OCC)を採用しています。2 つのトランザクションが同じデータを変更した場合、先にコミットした方が勝ち、後からコミットしようとした方は OCC エラーを受け取ります。
コネクターでは DsqlConfig の OccMaxRetries を設定することで、OCC 競合時に指数バックオフ付きで自動リトライしてくれます。
// OCC リトライ付き更新
await ds.WithTransactionRetryAsync(async conn =>
{
await using var cmd = conn.CreateCommand();
cmd.CommandText = "UPDATE hogeusers SET name = @newName WHERE name = @oldName";
cmd.Parameters.AddWithValue("newName", "piyo");
cmd.Parameters.AddWithValue("oldName", "hoge");
await cmd.ExecuteNonQueryAsync();
});
WithTransactionRetryAsync はリトライのたびに新しいコネクションを取得して BEGIN からやり直してくれるので、コールバック内はリトライセーフな操作だけを書く必要があります。
設定オプション
DsqlConfig で設定できるオプションは以下のとおりです。
| オプション | デフォルト | 説明 |
|---|---|---|
| Host | (必須) | クラスターエンドポイントまたは 26 文字のクラスター ID |
| Region | (自動検出) | AWS リージョン。Host がクラスター ID の場合は必須 |
| User | "admin" | データベースユーザー |
| Database | "postgres" | データベース名 |
| Port | 5432 | ポート番号 |
| Profile | null | AWS プロファイル名 |
| CustomCredentialsProvider | null | カスタム AWS 認証情報プロバイダー |
| TokenDurationSecs | null(SDK デフォルト 900 秒) | トークンの有効期間(秒) |
| OccMaxRetries | null(無効) | OCC リトライの最大回数 |
| LoggerFactory | null | ロガーファクトリ(リトライ時の警告ログなど) |
| ConfigureConnectionString | null | Npgsql 接続文字列のカスタマイズコールバック |
接続文字列形式もサポートされています。
await using var ds = await AuroraDsql.CreateDataSourceAsync(
"postgres://admin@ivtvahlywbnhlbci5ujpzw5wja.dsql.ap-northeast-1.on.aws/postgres?profile=dev");
また、プールのデフォルト設定として MaxPoolSize=10、ConnectionLifetime=3300(55 分)、ConnectionIdleLifetime=600(10 分)が適用されます。ConfigureConnectionString コールバックでこれらをカスタマイズすることも可能です。ただし、SSL(SslMode=VerifyFull、SslNegotiation=Direct)と Enlist=false はセキュリティ上の理由でオーバーライドできないみたいです。
await using var ds = await AuroraDsql.CreateDataSourceAsync(new DsqlConfig
{
Host = clusterEndpoint,
ConfigureConnectionString = csb =>
{
csb.MaxPoolSize = 20;
csb.MinPoolSize = 2;
csb.CommandTimeout = 60;
}
});
Rust コネクターについて
今回のアップデートでは Rust(SQLx)向けのコネクターも同時にリリースされています。
Rust のコネクターも設計思想は .NET と同じで、接続文字列を渡すだけで IAM トークン生成や SSL 設定が自動化されます。
pool と occ の feature フラグを opt-in で有効にする形式で、SQLx の慣習に沿った設計になっています。
use sqlx::Row;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pool = aurora_dsql_sqlx_connector::pool::connect(
"postgres://admin@ivtvahlywbnhlbci5ujpzw5wja.dsql.ap-northeast-1.on.aws/postgres",
)
.await?;
let row = sqlx::query("SELECT 'Hello, DSQL!' as greeting")
.fetch_one(&pool)
.await?;
let greeting: &str = row.get("greeting");
println!("{}", greeting);
pool.close().await;
Ok(())
}
Rust のコネクターではプール利用時にバックグラウンドタスクがトークンの有効期限の 80% 経過時点で自動リフレッシュしてくれるみたいです。
Connection pools: A background task refreshes the token at 80% of the token duration. Call
pool.close().awaitto stop the refresh task and release pool resources.
さいごに
本日は Amazon Aurora DSQL に .NET(Npgsql)と Rust(SQLx)向けコネクターが追加されたので .NET の方を中心に確認してみました。
DsqlConfig にホスト名を渡すだけで接続でき、WithTransactionRetryAsync で OCC リトライも簡単に組み込めるので、Npgsql を普段使っている方にはかなり馴染みやすい API だと思います。
これで主要な言語のコネクターがほぼ出揃ったので、Aurora DSQL を本格的に使う際にはコネクターを活用していきたいですね。






