このエントリは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
.NET SDK を使用した Azure Cosmos DB でのトランザクション バッチ操作 / Transactional batch operations in Azure Cosmos DB using the .NET SDKTransactionalBatch
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
クラスを使用します。 すべての操作がトランザクション バッチ操作内で記述されている順序で成功した場合、トランザクションはコミットされます。 ただし、いずれかの操作が失敗した場合、トランザクション全体がロールバックされます。)
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 | |
} |
対して、
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である点には注意が必要。
Resource | Default 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