Cloudflare Workers の TCP ソケットで Amazon Aurora DSQL に直接接続してみた
前回の記事では AWS Lambda から Aurora DSQL に接続し、書き込み・読み取り性能を検証しました。
今回は Cloudflare Workers の TCP ソケットから Aurora DSQL に接続可能か確認してみます。
なぜ Workers から DSQL に接続したいのか
ブログ機能の一部の API を Cloudflare Workers + D1 でマイクロサービス化することを検討しています。D1 は SQLite ベースでエッジでの読み取り性能に優れる一方、複雑な JOIN やウィンドウ関数など、高度な集計処理には不向きな側面があります。
そこで、Postgres互換のSQLが利用できる Aurora DSQL を「マスターデータ・集計用 DB」として併用し、その結果を Cron Trigger で D1 へ定期同期する仕組みを考えています。この構成を実現するには、Workers から DSQL を直接参照できることが大前提となります。
Cloudflare Workers は 2023年5月から TCP ソケット(connect() API)をサポートしており、PostgreSQL への直接接続自体は可能になっています。しかし、DSQL 固有の IAM トークン認証が Workers のランタイム環境で正常に動作するかは未知数だったため、まずはその疎通確認から着手することにしました。
検証環境
| 項目 | 値 |
|---|---|
| DSQL リージョン | us-west-2 |
| Docker イメージ | node:22-slim + wrangler 4.71.0 |
| バンドルサイズ | 147.79 KiB / gzip: 37.37 KiB |
| Worker Startup Time | 14〜16 ms |
Cloudflare Workers の開発 CLI である wrangler を Docker コンテナに入れて、ローカル環境を汚さずに検証できる構成にしました。
FROM node:22-slim
RUN npm install -g wrangler
WORKDIR /app
EXPOSE 8787
# compose.yaml
services:
worker:
build: .
volumes:
- ./app:/app
ports:
- "8788:8787"
environment:
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
working_dir: /app
command: sleep infinity
wrangler.toml です。nodejs_compat フラグが必須で、これがないと TCP ソケットが使えません。
name = "dsql-test"
main = "src/index.ts"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]
npm install postgres @aws-sdk/dsql-signer @aws/aurora-dsql-postgresjs-connector
公式コネクタで接続
公式コネクタ @aws/aurora-dsql-postgresjs-connector を使って接続します。トークン生成や接続パラメータの最適化をコネクタが透過的に行ってくれるため、コード量が少なく済みます。
import { auroraDSQLPostgres } from "@aws/aurora-dsql-postgresjs-connector";
interface Env {
AWS_ACCESS_KEY_ID: string;
AWS_SECRET_ACCESS_KEY: string;
AWS_SESSION_TOKEN: string;
DSQL_ENDPOINT: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const sql = auroraDSQLPostgres({
host: env.DSQL_ENDPOINT,
username: "admin",
customCredentialsProvider: async () => ({
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
sessionToken: env.AWS_SESSION_TOKEN,
}),
});
try {
const [row] = await sql`SELECT current_timestamp as ts`;
await sql.end();
return Response.json({ ok: true, timestamp: row.ts });
} catch (e: any) {
try { await sql.end(); } catch {}
return Response.json({ ok: false, error: e.message }, { status: 500 });
}
},
};
wrangler deploy してリクエストを送ってみます。
{ "ok": true, "timestamp": "2026-03-08T08:49:29.290Z" }
問題なく接続できました。
実データクエリ
続いて、実際のテーブルからもデータを取得してみます。
const posts = await sql`SELECT id, title FROM blogpost_meta LIMIT 3`;
const tags = await sql`SELECT * FROM tag_master LIMIT 3`;
{
"posts": [
{ "id": "00oAKWv1w3mtdzVecJwdU", "title": "[2025年7月21日から]BigQueryの..." }
],
"tags": [
{ "tag_name": "1on1", "usage_count": 14, "tag_category": "Methodology" }
]
}
問題なく取得できました。日本語も文字化けなく返ってきています。
レイテンシ計測
先ほどのコードに Date.now() で計測を加え、Worker 内の処理時間(コネクション確立 + クエリ実行)を測りました。Worker エッジ(PDX)と同一リージョンの us-west-2、および太平洋を越える ap-northeast-1 の2クラスタで計測した結果です。
| 計測 | us-west-2(エッジ近傍) | ap-northeast-1(太平洋越え) |
|---|---|---|
| SELECT 1件 | median 100〜170ms | median 1,400〜1,800ms |
| INSERT 100件(直列) | 1件あたり ~16ms | 1件あたり ~147ms |
Worker エッジ(PDX)に近い us-west-2 では SELECT が 100〜170ms 程度に収まりますが、太平洋を越える ap-northeast-1 では約10倍のレイテンシになりました。TCP/TLS ハンドシェイク + IAM トークン生成 + クエリ実行をすべて含めた数値です。
なお、今回はオレゴンの EC2 から Worker を呼び出したため、Worker はオレゴンのエッジ(PDX = ポートランド国際空港のIATAコード)で起動しています。この点は次のセクションで詳しく触れます。
Workers × DSQL の注意点
AWS 認証情報の管理
Lambda であれば実行ロールにより IAM 認証が透過的に行われますが、Workers は AWS 外の環境であるため、アクセスキーを明示的に渡す必要があります。今回の検証では手軽さのため STS の assume-role で発行した一時認証情報(アクセスキー + セッショントークン)を使用しました。ただし、STS の一時トークンは最大12時間で失効するため、Cloudflare Secrets に静的に登録する用途には向きません。実運用では、DSQL 接続用に最小権限を持たせた IAM ユーザーのアクセスキーをシークレットとして安全に管理する構成をおすすめします。
エッジロケーションが制御できない
Workers はリクエストの発信元に最も近いエッジで起動します。どのエッジで実行されるかをアプリケーション側で制御できません。今回の検証ではオレゴンの EC2 から呼び出したため、Worker もオレゴンエッジ(PDX)で起動し、同一リージョンの DSQL に近い位置から接続できました。
しかし、たとえば日本のユーザーがアクセスすると東京エッジで起動し、us-west-2 の DSQL への接続は太平洋を越える RTT が加算されます。Lambda であれば「東京リージョンにデプロイして東京の DSQL に接続」とリージョンを固定できますが、Workers ではそれができません。DSQL との距離に起因するレイテンシ増加を前提とした設計が必要です。
コネクションの再利用ができない
「グローバル変数にコネクションを保持して再利用すればいいのでは?」と考えるのは自然ですが、Workers ではこれは動作しません。
Cannot perform I/O on behalf of a different request.
I/O objects (such as streams, request/response bodies, and others)
created in the context of one request handler cannot be accessed
from a different request's handler.
Workers はリクエストごとに I/O コンテキストが分離されており、あるリクエストで作成した TCP ソケットを別のリクエストから再利用できません。全リクエストが都度接続(TCP/TLS ハンドシェイク + IAM トークン生成)となります。
前回の Lambda 記事ではコネクション再利用が最大のポイントでした。再利用により INSERT は 10ms 以下まで改善し、高いスループットを維持できましたが、Workers ではこの恩恵を受けられません。
さらに注意が必要なのが、DSQL の接続レート制限(毎秒100接続、緩和不可のハードリミット)との相性です。Workers ではリクエスト数と新規 DB 接続数が 1:1 になるため、トラフィックが 100 rps を超えると DSQL 側で接続が弾かれます。高頻度アクセスが想定される場合は、Workers から DSQL への直接接続ではなく、間にキャッシュや非同期処理を挟む構成を検討する必要があります。
参考: コネクタを使わない実装
公式コネクタを使わず、@aws-sdk/dsql-signer で直接トークンを生成して postgres.js に渡す方法もあります。
import { DsqlSigner } from "@aws-sdk/dsql-signer";
import postgres from "postgres";
const signer = new DsqlSigner({
hostname: env.DSQL_ENDPOINT,
region: "us-west-2",
credentials: {
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
sessionToken: env.AWS_SESSION_TOKEN,
},
});
const token = await signer.getDbConnectAdminAuthToken();
const sql = postgres({
host: env.DSQL_ENDPOINT,
port: 5432,
database: "postgres",
username: "admin",
password: token,
ssl: "require",
});
こちらも Workers ランタイムで問題なく動作しました。SigV4 は HTTP ベースの署名処理なので Workers との互換性に問題はありません。ただ、公式コネクタを使えばトークン生成や idleTimeoutMillis / maxLifetimeSeconds の最適化が不要になるので、特別な理由がなければコネクタの利用をおすすめします。
まとめ
Workers の TCP ソケットと公式コネクタの組み合わせで Aurora DSQL に問題なく接続できることを確認できました。
一方で、Workers にはエッジロケーションを制御できない点や、コネクションの再利用ができず DSQL の接続制限(100接続/秒)に到達しやすい点など、Lambda とは異なる制約があります。これらの特性を踏まえると、Workers から DSQL への直接接続は、Cron Trigger による定期同期やバックグラウンド処理のように、レイテンシ要件が厳しくないユースケースに適しています。
ユーザーリクエストパスで低レイテンシや高スループットが求められる場合は、DSQL のデータを Cron Trigger で D1 に同期してエッジ読み取りを活用したり、Workers のページキャッシュを効かせるといった構成が現実的と思われます。






