Azure Cosmos DB Java SQL SDK v4 での transactional batch サポート

このエントリは2020/12/13現在の情報に基づいています。今後の機能追加・変更に伴い、記載内容との乖離が発生する可能性があります。

以下のエントリの通り、先月、Azure Cosmos DB Java SDK v4でTransactional Batchをサポートするようになった。.NET用のSDKでは2019年9月の3.2.0からTransactional Batchをサポートしているため、.NET SDKに遅れること1年と2ヶ月でサポートしたことになる。

Azure Cosmos DB transactional batch support for Java SDK 4.7.0 and above is now available
https://azure.microsoft.com/updates/azure-cosmos-db-transactional-batch-support-for-java-sdk-470-and-above/

類似するものに.NETではBulk Operationがあるが、Transactional Batchとの違いは、Transactionalという表現がある通りトランザクションとして取り扱う点。そのため、Transactional Batchでは失敗すればRollbackするが、Bulk OperationはRollbackせず続行する点が異なる。

.NET SDK を使用して Azure Cosmos DB SQL API アカウントにデータを一括インポートする / Bulk import data to Azure Cosmos DB SQL API account by using the .NET SDK
https://docs.microsoft.com/azure/cosmos-db/tutorial-sql-api-dotnet-bulk-import

どういうものか

TransactionalBatchクラスのAPIドキュメントを見ると以下のように記載がある。つまるところ、同一パーティションキーに対する操作のみがTransactionalな操作の対象。

Represents a batch of operations against items with the same PartitionKey in a container that will be performed in a transactional manner at the Azure Cosmos DB service.(コンテナ内の同じPartitionKeyを持つアイテムに対し、Azure Cosmos DBサービスでトランザクショナルな方法で実行するバッチ操作を表す)

TransactionalBatch Class
https://docs.microsoft.com//java/api/com.azure.cosmos.transactionalbatch?view=azure-java-stable

同様の内容がTransactional Batchの概要を説明したドキュメントにも記載がある(説明で使っているランタイムは.NETではあるが、内容自体はJavaでも同じなので…)。

Transactional batch describes a group of point operations that need to either succeed or fail together with the same partition key in a container. In the .NET SDK, the TransactionalBatch class is used to define this batch of operations. If all operations succeed in the order they are described within the transactional batch operation, the transaction will be committed. However, if any operation fails, the entire transaction is rolled back.(トランザクション バッチでは、コンテナー内の同じパーティション キーと共に成功または失敗のいずれかになる必要があるポイント操作のグループを記述します。 .NET SDK では、この操作のバッチを定義するために TranscationalBatch クラスを使用します。 すべての操作がトランザクション バッチ操作内で記述されている順序で成功した場合、トランザクションはコミットされます。 ただし、いずれかの操作が失敗した場合、トランザクション全体がロールバックされます。)

.NET SDK を使用した Azure Cosmos DB でのトランザクション バッチ操作 / Transactional batch operations in Azure Cosmos DB using the .NET SDK
https://docs.microsoft.com/azure/cosmos-db/transactional-batch

Cosmos DBのTransactionについては以下のドキュメントに説明がある。

トランザクションとオプティミスティック同時実行制御 / Transactions and optimistic concurrency control
https://docs.microsoft.com/azure/cosmos-db/database-transactions-optimistic-concurrency

何がうれしいのか

ドキュメントにもある通り、ストアドプロシージャだとJavaScriptで書くしかないが、SDKでTransactional Batchをサポートしていれば、言語の選択肢があること、コードのバージョン管理ができること、性能もよいところ、などが利点としてあげられる。詳細は以下。

トランザクション バッチ操作とストアド プロシージャの比較 / Transactional batch operations Vs stored procedures
https://docs.microsoft.com/azure/cosmos-db/transactional-batch#transactional-batch-operations-vs-stored-procedures

試してみる

実際にどんなものかを試してみる(2020/12/13現在、サンプルコードはAzureのGitリポジトリのテストコードにしか存在しない)。必要な依存関係は2020/12/13現在以下の通り。

<!-- As of 13, December, 2020 -->
<dependency>
  <groupId>com.azure</groupId>
  <artifactId>azure-cosmos</artifactId>
  <version>4.8.0</version>
</dependency>

TransactionalBatch#createTransactionalBatchでバッチを作成し、そこにJSONにシリアライズできるアイテムを引数として各種オペレーションを司るメソッドに渡し、バッチに突っ込んでいく。CosmosContainer#executeTransactionalBatchでTransactionを発行する。

TransactionalBatchクラスのメソッドの詳細は以下のドキュメントを参照。

TransactionalBatch Class
https://docs.microsoft.com/java/api/com.azure.cosmos.transactionalbatch?view=azure-java-stable

今回使うオペレーションに関連するメソッドは以下のあたり。バッチの中で以下のメソッドを複数呼び出せる。

メソッド用途バッチ実行時のステータスコード
createItemOperationアイテムを作成するオペレーションをバッチに追加する(すでにアイテムが存在する場合は失敗する)成功: 201
既にある場合: 409
replaceItemOperationアイテムを置き換えるオペレーションをバッチに追加する(置き換え対象のアイテムが存在しない場合は失敗する)成功: 200
存在しないものへの操作: 404
upsertItemOperationアイテムを作成もしくは更新するオペレーションをバッチに追加する
(すでにアイテムが存在する場合はUpdate、ない場合はinsert)
Insert成功: 201
Update成功: 200
deleteItemOperationアイテムを削除するオペレーションをバッチに追加する(削除対象のアイテムが存在しない場合は失敗する)成功: 204
存在しないものへの操作: 404
readItemOperationアイテムを読み取るオペレーションをバッチに追加する(読み取り対象のアイテムが存在しない場合は失敗する)成功: 200
存在しないものへの操作: 404

トランザクション成功時・失敗時のステータスコード

  • 成功時: 200
  • 失敗時: 424

例えば、CREATE > READ > REPLACE > UPSERT > READ > DELETE の順でバッチに登録すると、順々に実行した結果、問題なければ200、あるオペレーションでエラーが出た場合には424が返る。

コード例

以下のコードは、CustomerというEntityBeanを格納するTransactional Batchで、String[] operation で複数のオペレーションを一つのバッチ内に入れて実行する、というもの。以下では同期クライアント(CosmosClient)を使っているが、非同期クライアント(CosmosAsyncClient)でも同様に利用できる。

このコード例では1個のCustomerインスタンスを作成したり、要素を変更したり、削除したりしている。もちろんArrayListなどで渡すよう変更しても動作する。

public void testBed(Customer targetCustomer, String[] operation) {
try (CosmosClient client = new CosmosClientBuilder()
.endpoint(ENDPOINT)
.key(KEY)
.consistencyLevel(ConsistencyLevel.SESSION)
.contentResponseOnWriteEnabled(true)
.preferredRegions(List.of("Japan East"))
.readRequestsFallbackEnabled(true)
.endpointDiscoveryEnabled(true)
.gatewayMode()
.connectionSharingAcrossClientsEnabled(true)
.buildClient()) {
CosmosContainer container = client
.getDatabase(DATABASE)
.getContainer(CONTAINER);
TransactionalBatch txBatch = TransactionalBatch
.createTransactionalBatch(new PartitionKey(targetCustomer.getMyPartitionKey()));
boolean illegalOperation = false;
for (String s : operation) {
switch (s.toUpperCase()) {
case "CREATE":
// add Create Operation
txBatch.createItemOperation(targetCustomer);
break;
case "REPLACE":
// add Replace Operation
targetCustomer.setCity("きょうと");
show(s.toUpperCase(), targetCustomer);
txBatch.replaceItemOperation(targetCustomer.getId(), targetCustomer);
break;
case "UPSERT":
// add Upsert Operation
targetCustomer.setRegion("日本のどこか");
show(s.toUpperCase(), targetCustomer);
txBatch.upsertItemOperation(targetCustomer);
break;
case "DELETE":
// add Delete Operation
show(s.toUpperCase(), targetCustomer);
txBatch.deleteItemOperation(targetCustomer.getId());
break;
case "READ":
// add read operation
txBatch.readItemOperation(targetCustomer.getId()).getItem();
break;
default:
illegalOperation = true;
break;
}
}
if (!illegalOperation) {
TransactionalBatchResponse txResponse = container.executeTransactionalBatch(txBatch);
txResponse.getResults().forEach(txResult -> logger.info(
"Result [" + txResult.getStatusCode() + "] - [" + txResult.getSubStatusCode() + "] Operation: " + txResult.getOperation().getOperationType().name()));
}
} catch (CosmosException e) {
e.printStackTrace();
}
}

例えば CREATE > READ > REPLACE > UPSERT > READ の順で実行するTransactional Batchを実行する場合には、引数として

CREATE READ REPLACE UPSERT READ

を指定すると以下の結果が返る。

Result [201]-[0] CREATE
Result [200]-[0] READ
Result [200]-[0] REPLACE
Result [200]-[0] UPSERT
Result [200]-[0] READ

この例の場合は、

作成>読み取り>要素変更>Upsert(この例ではUpdate)>読み取り

なので、Cosmos DBのコンテナにはUpsertしたデータが残る。以下はその例。

{
"id": "19b24962-2a8a-47ec-913d-69c6aeab4ac8",
"name": "中村 亮",
"city": "きょうと",
"zipCode": "72341",
"region": "日本のどこか",
"myPartitionKey": "19b24962-2a8a-47ec-913d-69c6aeab4ac8",
"userDefinedId": 454,
"_rid": "kfh1ANEmDq4iAAAAAAAAAA==",
"_self": "dbs/kfh1AA==/colls/kfh1ANEmDq4=/docs/kfh1ANEmDq4iAAAAAAAAAA==/",
"_etag": "\"02003c33-0000-2300-0000-5fd614b50000\"",
"_attachments": "attachments/",
"_ts": 1607865525
}
view raw TxBatchResult.json hosted with ❤ by GitHub

対して、

CREATE DELETE READ

の場合、CREATEで作成したCustomerがDELETEで削除されるため、READに失敗する。この場合、CREATE操作がRollbackされるため、Cosmos DBにはデータは格納されない。

Result [424]-[0] CREATE
Result [424]-[0] DELETE
Result [404]-[0] READ

制限

Cosmos DBの制限自体に変更はないので、以下のURLに記載の制限はそのまま適用される。

要求あたりの制限 / Per-request limits
https://docs.microsoft.com/azure/cosmos-db/concepts-limits#per-request-limits

具体的には以下の通り。Transactional Batch内の操作の上限が100である点には注意が必要。

ResourceDefault limit
1 つの操作 (ストアド プロシージャの実行や 1 回のクエリ ページ取得など) の最大実行時間
Maximum execution time for a single operation (like a stored procedure execution or a single query page retrieval)
5 sec
最大要求サイズ (ストアド プロシージャ、CRUD など)
Maximum request size (for example, stored procedure, CRUD)
2 MB
最大応答サイズ (ページ分割されたクエリなど)
Maximum response size (for example, paginated query)
4 MB
トランザクション バッチ内の操作の最大数
Maximum number of operations in a transactional batch
100

作成したサンプルコードは以下。

TransactionalBatch – Sample of Transactional batch for CosmosDB
https://github.com/anishi1222/TransactionalBatch

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中