Microservice Transaction API を使用してマイクロサービストランザクションをサポートするアプリケーションを作成する
このページは英語版のページが機械翻訳されたものです。英語版との間に矛盾または不一致がある場合は、英語版を正としてください。
このチュートリアルでは、Microservice Transaction API を使用して、マイクロサービストランザクションをサポートするサンプル電子商取引アプリケーションを作成する方法について説明します。
Microservice Transaction API を使用すると、2フェーズコミットプロトコルを自分で実装することなく、複数のプロセスにまたがる単一のトランザクションを実行できます。サービスが prepare、validate、または commit を呼び出すことはなく、ScalarDB Cluster がそれらを代行します。
そのため、同じアプリケーションコードをどちらの ScalarDB Cluster デプロイパターン(共有クラスターパターンまたは分離クラスターパターン)でも実行できます。このサンプルはそれを具体的に示すために構成されています。アプリケーションコードのコピーが1つと、2セットの設定ファイルが含まれています。このページ全体でタブを使用してパターンを選択してください。選択内容はすべてのステップに引き継がれます。
両方のデプロイパターンで ScalarDB Cluster ノードが実行されるため、どちらを選択してもライセンスキーが必要です。
サンプルマイクロサービスアプリケーションの概要
サンプル電子商取引アプリケーションは、ユーザーが信用枠を使用してアイテムを注文・支払いする方法を示しています。
サンプルアプリケーションには、database-per-service パターンに基づく Customer Service と Order Service という2つのマイクロサービスがあります:
- Customer Service は、信用枠情報、信用限度額、信用合計など、顧客情報を管理します。
- Order Service は、注文処理の実行や注文履歴の取得など、注文操作を担当します。
各サービスには gRPC エンドポイントがあります。クライアントはエンドポイントを呼び出し、サービスも相互にエンドポイントを呼び出します。
サンプルアプリケーションで使用するデータベースは MySQL、Cassandra、および PostgreSQL です。Customer Service と Order Service は ScalarDB Cluster を通じてそれぞれ MySQL と Cassandra を使用し、PostgreSQL は Coordinator テーブルを保持します。両方のデプロイパターンで同じ3つのデータベースを使用し、各名前空間を同じデータベースに配置するため、パターン間で変わるのは ScalarDB のトポロジーのみです。
2つのデプロイパターンは、ScalarDB Cluster がサービスとデータベースの間にどのように配置されるかが異なります。
共有クラスターパターンでは、両方のサービスが1つのクラスターノードに接続し、そのノードが3つのデータベースすべてにアクセスします:
分離クラスターパターンでは、各サービスがクラスターを所有し、Transaction Coordinator がトランザクションを駆動し、Coordinator テーブルはすべてのコンポーネントがアクセスできる独自のストアに移動します:
両サイドは対称的です:それぞれがサービス、クラスター、データベースを所有します。それらの外側にあるのは共有されたもの、つまりトランザクションを駆動する Transaction Coordinator と、Transaction Coordinator および両方のクラスターが読み書きする Coordinator テーブルです。
アプリケーションコードは両方で同じです。設定のみが異なります。
サンプルアプリケーションは Microservice Transaction API の使用方法を示すことに重点を置いているため、アプリケーション固有のエラー処理、認証処理、および同様の機能はサンプルアプリケーションに含まれていません。
認証と認可は、2つのデプロイパターンの違いを読みやすくするために無効にされています。このトポロジーを実際のシステムに適用する前に、サービスごとのユーザーと名前空間の権限を追加してください。詳細については、ユーザーの認証と認可を参照してください。
サービスエンドポイント
サービスで定義されているエンドポイントは次のとおりです:
-
Customer Service
getCustomerInfopaymentrepayment
-
Order Service
placeOrdergetOrdergetOrders
Customer Service には prepare、validate、commit、または rollback エンドポイントがありません。それらを必要としないことが Microservice Transaction API のポイントです。
このサンプルアプリケーションで実行できること
サンプルアプリケーションは次の種類のトランザクションをサポートしています:
- Customer Service の
getCustomerInfoエンドポイントを通じて顧客情報を取得します。 - Order Service の
placeOrderエンドポイントと Customer Service のpaymentエンドポイントを通じて、信用枠を使用して注文を行います。- 注文のコストが顧客の信用限度額を下回っているかどうかを確認します。
- チェックが成功した場合は、注文履歴を記録し、顧客が支払った金額を更新します。
- Order Service の
getOrderエンドポイントと Customer Service のgetCustomerInfoエンドポイントを通じて、注文 ID で注文情報を取得します。 - Order Service の
getOrdersエンドポイントと Customer Service のgetCustomerInfoエンドポイントを通じて、顧客 ID で注文情報を取得します。 - Customer Service の
repaymentエンドポイントを通じて支払いを行います。- 顧客が支払った金額を減らします。
placeOrder、getOrder、および getOrders はそれぞれ両方のサービスにまたがる1つのトランザクションとして実行されるため、MySQL と Cassandra にまたがります。getCustomerInfo と repayment は、直接呼び出されると Customer Service 内のみで実行されるため、代わりに通常の Transaction API を使用します。
Microservice Transaction API の仕組み
このセクションでは API 自体について説明します。これは両方のデプロイパターンに適用されます。完全な API リファレンスについては、ScalarDB Cluster Java API ガイドを参照してください。
この API はサービスにまたがるトランザクションにのみ使用してください。1つのサービス内に留まるトランザクションはプロセス間で調整するものがないため、代わりに通常の Transaction API (DistributedTransactionManager) を使用します。両方のマネージャーは同じ TransactionFactory と同じ設定ファイルから取得されます:
TransactionFactory factory = TransactionFactory.create("<CONFIGURATION_FILE_PATH>");
DistributedTransactionManager transactionManager = factory.getTransactionManager();
GlobalTransactionManager globalTransactionManager = factory.getGlobalTransactionManager();
サービスにまたがるトランザクションには2種類のハンドルがあります:
begin()またはbeginReadOnly()から取得したGlobalTransaction。トランザクション全体の結果を駆動し、データを保持しません。beginBranch(transactionId)から取得したサービスごとのBranchTransaction。そのサービスの作業の CRUD ハンドルです。
イニシエーター
1つのサービスがトランザクションを開始し、その ID を他のサービスと共有し、結果を決定します。このサンプルでは、Order Service がイニシエーターです:
GlobalTransaction global = globalTransactionManager.begin();
BranchTransaction branch = null;
try {
branch = globalTransactionManager.beginBranch(global.getId());
// ... this service's own CRUD ...
branch.end(BranchTransaction.Status.SUCCESS);
branch = null;
callCustomerService(global.getId()); // pass the transaction ID over gRPC
global.commit();
} catch (UnknownTransactionStatusException e) {
// ... end the branch with FAILURE; do not roll back ...
} catch (Exception e) {
// ... end the branch with FAILURE, then roll back ...
}
順序に注目してください:このサービスは他のサービスを呼び出す前に自身の作業を終了してブランチを終了します。リモート呼び出しをまたいでブランチを開いたままにすることも正しいですが、サービスが完了したらすぐにブランチを終了することが API が想定するイディオムです。
参加者
トランザクション ID を受け取ったサービスは、自身のブランチで同じトランザクションに参加し 、作業を行い、ブランチを終了します。コミットやロールバックは行いません—それはイニシエーターの責任です:
BranchTransaction branch = globalTransactionManager.beginBranch(transactionId);
// ... this service's own CRUD ...
branch.end(BranchTransaction.Status.SUCCESS);
このサンプルでは、Customer Service はリクエストにトランザクション ID が含まれている場合に参加者として実行されます。それ以外の場合は作業がサービス内に留まるため、Microservice Transaction API ではなく Transaction API を通じて通常のトランザクションとして実行されます。payment エンドポイントは常に参加者です。repayment は決して参加者になりません。getCustomerInfo は呼び出し元によって、どちらにもなります。
ブランチの終了は必須
すべてのブランチは、結果にかかわらず、正確に1回終了する必要があります。その義務はブランチを実行するプロセスに属し、他のどのプロセスもそれを代行することはできません。失敗パスでは、ブランチを Status.FAILURE で終了してください:
branch.end(BranchTransaction.Status.FAILURE);
end(FAILURE) は寛容であり、catch ブロックから呼び出すことができます:すでに終了したブランチを終了することは何もしません(no-op)。元の失敗を隠すこともありません。
end(Status)、commit()、および rollback() はそれぞれチェック例外を宣言するため、それらを呼び出す catch ブロックは独自の処理が必要です。
エラー処理
イニシエーターのみがトランザクションを再開始できるため、イ ニシエーターのみが再試行します。このサンプルでは、Order Service が再試行ループを持ち、Customer Service も自身で実行するトランザクションに対して再試行ループを持ちます。参加者パスは再試行しません。
すべての失敗が再試行に値するわけではありません。サンプルは gRPC の NOT_FOUND および FAILED_PRECONDITION ステータスを決定論的なビジネス失敗(不明なアイテム、または超過した信用限度額)として扱い、それらを再試行しません。
UnknownTransactionStatusException は個別に処理され、再試行されません。トランザクションがコミットされたかどうかが不明であることを意味します。再試行するとコミット済みトランザクションが重複する可能性があります。実際のステータスを特定することはアプリケーションの責任です。
前提条件
- 以下のいずれかの Java Development Kit (JDK):
- Oracle JDK: 8、11、17、または 21 (LTS バージョン)
- OpenJDK ディストリビューション (Eclipse Temurin、Amazon Corretto、または Microsoft Build of OpenJDK): 8、11、17、または 21 (LTS バージョン)
- Docker 20.10以降 (Docker Compose V2以降)
ScalarDB Cluster を使用するには、ライセンスキー (試用ライセンスまたは商用ライセンス) が必要です。ライセンスキーをお持ちでない場合は、試用ライセンスキーの設定方法に従ってすぐに使える試用ライセンスキーをご利用いただけます。商用ライセンスを取得するには、お問い合わせください。
ScalarDB Cluster のセットアップ
次のセクションでは、ScalarDB Cluster で Microservice Transaction API を使用してマイクロサービストランザクションをサポートするサンプルアプリケーションをセットアップす る方法について説明します。
ScalarDB サンプルリポジトリをクローンする
ターミナルを開き、次のコマンドを実行して ScalarDB サンプルリポジトリをクローンします:
git clone https://github.com/scalar-labs/scalardb-samples
次のコマンドを実行して、サンプルアプリケーションが含まれているディレクトリに移動します:
cd scalardb-samples/microservice-transaction-sample-with-cluster/