![[新機能] AWS が DynamoDB API 互換のオープンソースアダプタ ExtendDB を発表したので試してみました](https://devio2024-media.developers.io/image/upload/f_auto,q_auto,w_3840/v1779328530/user-gen-eyecatch/fwi1zbfkzpozt2scv8kb.png)
[新機能] AWS が DynamoDB API 互換のオープンソースアダプタ ExtendDB を発表したので試してみました
クラウド事業本部の石川です。AWSがDynamoDB互換のオープンソースアダプタ「ExtendDB」をApache 2.0ライセンスで公開しましたので、実際にmacOS上で動かしてみました。
2026年5月20日、AWSはDynamoDB互換のオープンソースアダプタ「ExtendDB」v0.1.0をGitHubで公開しました。ExtendDBはDynamoDBのワイヤプロトコルを実装したクリーンルーム実装で、PostgreSQLをバックエンドとして動作します。既存のAWS SDK・CLI・ツールに一切変更を加えずに、ローカル開発、CI/CD、オンプレミス、エアギャップ環境でDynamoDBのプログラミングモデルを利用できます。
ExtendDBはAmazon DynamoDBそのものではなく、DynamoDBのソースコードも一切含んでいません。AWSのエンジニアによって開発が始められた独立したオープンソースプロジェクトです。
ExtendDBとは
ExtendDBはRust 1.85+で書かれたシングルバイナリで、tokio による非同期処理、PostgreSQL 14+ をストレージバックエンドとして使用します。ストレージレイヤはRustトレイトで抽象化されており、将来的にApache Cassandraなど他のバックエンドを差し替えられる設計になっています。
主な仕様は以下の通りです。
- DynamoDBワイヤプロトコル: CRUD、Query、Scan、Batch、Transactions、Streams、TTL、Import/Export
- 認証: SigV4必須、独自のIAM風機能(ユーザー、グループ、ロール、ポリシー、Permissions Boundary)
- 通信: TLS必須(自己署名証明書を自動生成、CA署名証明書への差し替えも可)
- 管理: Web管理コンソール、Prometheus互換メトリクス、syslogロギング
- ライセンス: Apache-2.0
DynamoDB Local(公式の開発用エミュレータ)とは異なり、ExtendDBはPostgreSQLを使うため、PostgreSQLのバックアップ・レプリケーション・高可用性ツールがそのまま流用できる点が特徴です。
DynamoDBとの差異(v0.1.0時点)も明文化されています。特に以下は未実装です。
- Global Tables、PartiQL、DAX
- フェデレーテッドロール(SAML/Web Identity)、ロールチェーン
- S3経由のImport/Export(ローカルファイルシステムのみ対応)

引用: Introducing ExtendDB: An open source DynamoDB-compatible adapter with pluggable storage backends
アーキテクチャ

引用: Introducing ExtendDB: An open source DynamoDB-compatible adapter with pluggable storage backends
やってみた
前提条件
- macOS(Apple Silicon、Darwin 25.4.0)
- Homebrew 5.1.12
- AWS CLI v2.34.42
- PostgreSQL 14+(今回はHomebrewのpostgresql@14を使用)
- Rust 1.85+(今回はHomebrewのrust 1.95.0を使用)
- Git 2.53.0
ExtendDBのクローン
GitHubからExtendDBをクローンします。最新リリース v0.1.0 を取得します。
% git clone https://github.com/ExtendDB/extenddb.git
% cd extenddb
Rustのインストール
未導入の場合はHomebrewでRustをインストールします。
% rustc --version
rustc 1.95.0 (59807616e 2026-04-14) (Homebrew)
cargo build --release でビルド
リリースビルドを実行します。手元のApple Silicon Macでは依存クレートのコンパイル込みで約1分40秒で完了しました。
% cargo build --release
Compiling extenddb-server v0.1.0 (...)
Compiling extenddb-storage-postgres v0.1.0 (...)
Finished `release` profile [optimized] target(s) in 1m 39s
生成されたバイナリのバージョンを確認します。
% ./target/release/extenddb --version
extenddb 0.1.0
catalog 0.0.2 (postgres)
commit c20b2d6
built 2026-05-21T01:12:27Z
PostgreSQL を起動
HomebrewのpostgreSQL@14を起動します。
$ brew services start postgresql@14
:
:
% pg_isready
/tmp:5432 - accepting connections
extenddb init で初期化
Homebrew版のPostgreSQLではadminロールがOSユーザー名($(whoami))になるため、--pg-user でそれを渡します。
% ./target/release/extenddb init --pg-user $(whoami)
実行すると、extenddb PostgreSQLロールの作成、extenddb_catalog / extenddb の2データベース作成、暗号化キーの生成、デフォルトアカウントとadminユーザーの作成、TLS自己署名証明書(~/.extenddb/tls/cert.pem)の生成、設定ファイル extenddb.toml の生成までが一括で実行されます。
=== extenddb init (backend: postgres) ===
--- Ensuring application user 'extenddb' exists...
Created user 'extenddb'.
--- Creating database 'extenddb_catalog'...
--- Creating database 'extenddb'...
--- Running catalog migrations...
--- Generating AES-256-GCM encryption key...
--- Creating default account '<AWS_ACCOUNT_ID>'...
--- Creating admin user 'admin'...
┌─────────────────────────────────────────────────┐
│ Admin credentials (shown once, save them now) │
│ │
│ Username: admin │
│ Password: ************************ │
└─────────────────────────────────────────────────┘
...
=== extenddb init complete ===
adminパスワードはこの時1度しか表示されません。安全な場所に保管します。
verify で構成検証
% ./target/release/extenddb verify --config extenddb.toml
=== extenddb verify ===
Config: extenddb.toml
Catalog database: extenddb_catalog
--- Checking catalog connection...
OK: Connected to catalog.
--- Checking catalog version...
OK: Catalog version 0.0.2
--- Checking data database...
OK: Connected to data database 'extenddb'.
--- Enumerating tables...
Tables: 1
Indexes: 0
=== HEALTHY: All checks passed ===
サーバ起動とヘルスチェック
$ ./target/release/extenddb serve --config extenddb.toml
extenddb 0.1.0 (catalog 0.0.2) starting on 127.0.0.1:8000
storage: postgres (postgresql://extenddb:***@localhost:5432/extenddb_catalog)
extenddb server started (pid 32658, 127.0.0.1:8000)
serve はデーモン化するため、コマンドはすぐに戻ります。status で動作を確認します。
$ ./target/release/extenddb status --config extenddb.toml
extenddb is running on port 8000 (pid 32658)
自己署名証明書を信頼してヘルスチェックを叩きます。
$ curl --cacert ~/.extenddb/tls/cert.pem https://127.0.0.1:8000/health
{"status":"healthy"}
参考: ウェブUI
管理用のウェブUIも提供されています。データを操作する機能はありませんでした。
ログイン画面

Dashboard / トップ画面

Accounts

Metrics

Settings

IAMユーザーとアクセスキーの発行
ExtendDBは認証必須で、すべてのリクエストがSigV4署名されている必要があります。テスト用のIAMユーザーを作成し、アクセスキーを発行します。
$ export EXTENDDB_PASSWORD='<initで表示されたadminパスワード>'
ACCOUNT_ID=<AWS_ACCOUNT_ID>
$ ./target/release/extenddb manage --user admin create-user \
--account-id "${ACCOUNT_ID}" --user-name blogtest
$ ./target/release/extenddb manage --user admin create-access-key \
--account-id "${ACCOUNT_ID}" --user-name blogtest
IAM user created
{
"access_key_id": "AKIAEXTENDDB****************",
"secret_access_key": "extenddb********************************"
}
アクセスキーIDのプレフィックスは AKIAEXTENDDB、シークレットアクセスキーのプレフィックスは extenddb で、本物のAWS IAM認証情報と混同しないよう区別されています。
DynamoDB全操作を許可するインラインポリシーを付与します。
$ ./target/release/extenddb manage --user admin put-user-policy \
--account-id "${ACCOUNT_ID}" --user-name blogtest \
--policy-name DynamoDBFullAccess \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"dynamodb:*","Resource":"*"}]}'
list-user-policies で確認すると、自分自身のアクセスキーを管理できる SelfServicePolicy も自動付与されていました。
AWS CLI からテーブル操作
ここからは普通のAWS CLIです。--endpoint-url でローカルのExtendDBに向けます。
$ export AWS_ACCESS_KEY_ID='AKIAEXTENDDB****************'
$ export AWS_SECRET_ACCESS_KEY='extenddb********************************'
$ export AWS_CA_BUNDLE=~/.extenddb/tls/cert.pem
$ export AWS_DEFAULT_REGION=us-east-1
$ ENDPOINT=https://127.0.0.1:8000
$ aws dynamodb list-tables --endpoint-url "${ENDPOINT}"
{
"TableNames": []
}
PAY_PER_REQUESTモードで複合キーテーブルを作成します。
$ aws dynamodb create-table --table-name Orders \
--attribute-definitions AttributeName=PK,AttributeType=S AttributeName=SK,AttributeType=S \
--key-schema AttributeName=PK,KeyType=HASH AttributeName=SK,KeyType=RANGE \
--billing-mode PAY_PER_REQUEST \
--endpoint-url "${ENDPOINT}"
{
"TableDescription": {
"TableName": "Orders",
"TableStatus": "CREATING",
"TableArn": "arn:aws:dynamodb:us-east-1:<AWS_ACCOUNT_ID>:table/Orders",
...
}
}
ARNは arn:aws:dynamodb:<region>:<account_id>:table/<name> 形式で、ExtendDBが採番したアカウントIDが入ります。
数秒待ってからdescribe-tableを呼ぶと ACTIVE に遷移しています。
$ aws dynamodb describe-table --table-name Orders --endpoint-url "${ENDPOINT}"
{
"Table": {
"TableName": "Orders",
"TableStatus": "ACTIVE",
...
}
}
データのCRUDとQuery/Scan
サンプルアイテムを3件投入します。
$ aws dynamodb put-item --table-name Orders \
--item '{"PK":{"S":"CUSTOMER#123"},"SK":{"S":"ORDER#2026-001"},"Status":{"S":"PENDING"},"Amount":{"N":"4800"},"Items":{"L":[{"S":"item-A"},{"S":"item-B"}]}}' \
--endpoint-url "${ENDPOINT}"
$ aws dynamodb put-item --table-name Orders \
--item '{"PK":{"S":"CUSTOMER#123"},"SK":{"S":"ORDER#2026-002"},"Status":{"S":"PAID"},"Amount":{"N":"12000"}}' \
--endpoint-url "${ENDPOINT}"
$ aws dynamodb put-item --table-name Orders \
--item '{"PK":{"S":"CUSTOMER#456"},"SK":{"S":"ORDER#2026-003"},"Status":{"S":"PAID"},"Amount":{"N":"3200"}}' \
--endpoint-url "${ENDPOINT}"
get-item で1件取得します。
$ aws dynamodb get-item --table-name Orders \
--key '{"PK":{"S":"CUSTOMER#123"},"SK":{"S":"ORDER#2026-001"}}' \
--endpoint-url "${ENDPOINT}"
{
"Item": {
"Amount": {"N": "4800"},
"Items": {"L": [{"S": "item-A"}, {"S": "item-B"}]},
"PK": {"S": "CUSTOMER#123"},
"SK": {"S": "ORDER#2026-001"},
"Status": {"S": "PENDING"}
}
}
query で同一PKの注文を全件取得します。
$ aws dynamodb query --table-name Orders \
--key-condition-expression "PK = :pk" \
--expression-attribute-values '{":pk":{"S":"CUSTOMER#123"}}' \
--endpoint-url "${ENDPOINT}"
{
"Items": [
{"PK": {"S": "CUSTOMER#123"}, "SK": {"S": "ORDER#2026-001"}, "Status": {"S": "PENDING"}, ...},
{"PK": {"S": "CUSTOMER#123"}, "SK": {"S": "ORDER#2026-002"}, "Status": {"S": "PAID"}, ...}
],
"Count": 2,
"ScannedCount": 2,
"ConsumedCapacity": null
}
レスポンス構造は本物のDynamoDBと同一で、Count ScannedCount も同じセマンティクスで返ります。
scan に --filter-expression を付け、Status=PAID のアイテムだけ抽出します。
$ aws dynamodb scan --table-name Orders \
--filter-expression "#st = :st" \
--expression-attribute-names '{"#st":"Status"}' \
--expression-attribute-values '{":st":{"S":"PAID"}}' \
--endpoint-url "${ENDPOINT}"
ScannedCount=3, Count=2 が返り、フィルタも期待通りに動きました。
update-item で属性を更新します。
$ aws dynamodb update-item --table-name Orders \
--key '{"PK":{"S":"CUSTOMER#123"},"SK":{"S":"ORDER#2026-001"}}' \
--update-expression "SET #st = :st, ShippedAt = :ts" \
--expression-attribute-names '{"#st":"Status"}' \
--expression-attribute-values '{":st":{"S":"SHIPPED"},":ts":{"S":"2026-05-21T00:55:00Z"}}' \
--return-values ALL_NEW \
--endpoint-url "${ENDPOINT}"
Status が SHIPPED に変わり、ShippedAt が追加された全属性が Attributes として返ってきます。
条件式の動作確認
既存キーへの上書きを条件付きputで防げるかを確認します。
$ aws dynamodb put-item --table-name Orders \
--item '{"PK":{"S":"CUSTOMER#123"},"SK":{"S":"ORDER#2026-001"},"Status":{"S":"OVERWRITE"}}' \
--condition-expression "attribute_not_exists(PK)" \
--endpoint-url "${ENDPOINT}"
aws: [ERROR]: An error occurred (ConditionalCheckFailedException) when calling the PutItem operation: The conditional request failed
本家DynamoDBと同じ ConditionalCheckFailedException が返り、データも上書きされず保護されています。
aws dynamodb get-item --table-name Orders \
--key '{"PK":{"S":"CUSTOMER#123"},"SK":{"S":"ORDER#2026-001"}}' \
--query 'Item.Status' --endpoint-url "${ENDPOINT}"
{
"S": "SHIPPED"
}
運用エンドポイントの確認
ヘルスチェック・メトリクス・Web管理コンソールもデフォルトで有効です。
% curl --cacert ~/.extenddb/tls/cert.pem https://127.0.0.1:8000/metrics
{"buckets":[{"count":28,"dimensions":[],"max":1.0,"metric":"PoolActiveConnections","min":0.0,"sum":1.0,"timestamp":"2026-05-21T01:25:00Z"},....,"source":"database"}%
操作ごとの認証時間(auth_us)、認可時間(authz_us)、ディスパッチ時間(dispatch_us)がマイクロ秒単位で記録されます。Web管理コンソールは https://127.0.0.1:8000/console/ でアクセスでき、ブラウザでアカウント・ユーザー・アクセスキーの管理画面が利用できます。
クリーンアップ
検証が終わったらテーブル削除、サーバ停止、DB破棄の順でクリーンアップします。
aws dynamodb delete-table --table-name Orders --endpoint-url "${ENDPOINT}"
./target/release/extenddb stop --config extenddb.toml
./target/release/extenddb destroy --config extenddb.toml --yes
brew services stop postgresql@14
extenddb destroy は extenddb_catalog と extenddb の両データベースを物理削除します。再度試したい場合は extenddb init からやり直すかたちです。
考察
実際に動かしてみて感じたポイントを整理します。
- 既存のAWS SDKやCLIがそのまま動く:
--endpoint-urlを切り替えるだけで、コードを1行も書き換えずにDynamoDBアプリケーションをローカル実行できます。CIや開発環境でAWS課金を発生させずに統合テストを回したい場合の選択肢として有力です。 - 本家との差異は明文化されている:
docs/differences-from-dynamodb.mdでGlobal Tables、PartiQL、DAX、フェデレーテッドロール、S3 Import/Exportなどの未実装機能が一覧化されています。バックアップやレプリケーションはPostgreSQL側に任せる設計思想です。 - アクセスキープレフィックスが分離されている:
AKIAEXTENDDBextenddbプレフィックスでExtendDBの認証情報と本物のAWS認証情報が混在しないよう配慮されています。 - PostgreSQLが裏側にいる:
extenddb_catalogとextenddbの2データベースの監視・バックアップ・容量設計にはPostgreSQLの運用ナレッジがそのまま使えます。一方で、本物のDynamoDBの分散ストレージとはスケーリング特性が大きく異なるため、本番想定の性能試験ではDynamoDBそのものとの差を意識する必要があります。 - TTL属性名の制約: TTL属性名が
[a-zA-Z0-9._-]+に制限されている点はSQLインジェクション対策として理にかなっており、本家より厳格です。 - 本番利用は時期尚早: GitHubのリリースは v0.1.0 で、公式にも本番環境推奨ではないとされています。ローカル開発・CI・PoC・エアギャップ環境などのユースケースを想定して評価するのが現実的です。
ストレージレイヤはRustトレイトで設計されており、将来Apache Cassandraなど他のバックエンドが追加される設計だと公式ブログでも言及されています。プラグイン構造の進化は今後の注目ポイントです。
DynamoDB Local との違い
ExtendDB と従来からある DynamoDB Local は、どちらも「ローカル環境で DynamoDB API を使える」という点では共通していますが、想定する用途やアーキテクチャに大きな違いがあります。両者の特徴を整理すると以下のとおりです。
| 観点 | DynamoDB Local | ExtendDB |
|---|---|---|
| 提供形態 | AWS 純正の無償配布バイナリ(独自 EULA) | AWS が公開する OSS(Apache 2.0) |
| 実装言語 | Java(DynamoDBLocal.jar) |
Rust(単一バイナリ) |
| ストレージ | SQLite(-sharedDb)またはインメモリ |
プラグイン式。参照実装は PostgreSQL、Cassandra 等の追加が可能 |
| 永続化 | プロセス内で完結。テスト用途を想定 | バックエンド DB に永続化。本番ワークロードも視野 |
| 認証 | 不要(ダミー資格情報で動作) | SigV4 必須(IAM とは独立した認証情報) |
| 通信 | HTTP(平文) | TLS 必須 |
| 想定ユースケース | ユニットテスト、ローカルでの動作確認 | ローカル開発、CI/CD 統合テスト、オンプレ/エアギャップ、エッジ、ハイブリッド |
| スケーラビリティ | 単一プロセス、テスト規模を想定 | 単一バイナリだがバックエンド次第で水平展開可能 |
| バックエンド拡張 | 内部実装固定 | Rust トレイトで独自バックエンドを追加可能 |
| 管理 UI | JavaScript シェル(/shell/) |
Web 管理コンソール(/console/) |
| 接続例 | --endpoint-url http://localhost:8000 |
--endpoint-url https://127.0.0.1:8000(TLS 必須) |
DynamoDB Local は、JVM 上で動く軽量なテスト用ツールとして、長らくユニットテストやハンズオンで利用されてきました。一方の ExtendDB は、Rust 実装の単一バイナリでパフォーマンスと運用性を意識し、ストレージ層をプラグイン化することで「ローカル開発からオンプレミス本番運用まで」を一つの実装でカバーすることを狙っている点が大きな違いです。
そのため、純粋なユニットテストや「とりあえず DynamoDB を触ってみたい」用途であれば従来どおり DynamoDB Local が手軽です。ExtendDB は、CI/CD での高忠実度な統合テストや、オンプレ/エッジでの本番採用、マルチクラウド/ハイブリッド構成といった、より広い守備範囲を必要とする場面で力を発揮します。
最後に
ExtendDB v0.1.0 は、DynamoDB と同じワイヤプロトコルを話すクリーンルームなオープンソース実装でした。AWS CLI や SDK を改変せずに、ローカルでDynamoDBアプリを動かせる選択肢が公式に増えたことになります。DynamoDB Localと比較すると、PostgreSQLバックエンドによる運用ツールチェーンの活用、IAM風の独立した認証/認可機能、Web管理コンソールが特徴です。
オンプレミスや閉域網環境でDynamoDBプログラミングモデルを使いたい方、CIで安定的にDynamoDB系の統合テストを回したい方には、評価する価値のある選択肢です。本物のDynamoDBとは分散特性が異なる点に留意しつつ、まずはローカルで v0.1.0 を触ってみるところから始めるのが良さそうです。この記事がどなたかのお役に立てば幸いです。







