1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CAP Java + SAP Cloud SDKでリモートサービス呼び出し:キャッシュとリトライの実装

Last updated at Posted at 2025-10-07

はじめに

CAPからリモートサービスを呼び出すとき、マスタデータなど変更が少ないデータはCAP側でキャッシュしておくことで、レスポンスタイムを短縮することができます。
また、リモートサービスが一時的に落ちていた場合は、何度かリトライすることで接続できる可能性があります。

CAP自体にはキャッシュのやリトライ仕組みはありませんが、SAP Cloud SDKのResilience機能を使うことでこれらの機能を組み込むことができます。

シナリオ

CAP(Java)からリモートのBusiness Partnerサービスにアクセスし、データを取得します。
「Business Partnerサービス」は、ログなどを見られるようにローカルで作成します。

CAP caching.png

以下が作成したプロジェクトです。

バージョン

  • SAP Cloud SDK: 5.22.0
  • @sap/cds-dk: 9.3.0
  • CAP Java SDK: 4.2.0

前提

Business Partnerサービスのメタデータをプロジェクトに取り込み、以下のエンティティを定義します。(以下のブログのステップ1~2を参照)
https://qiita.com/tami/items/441aae3330cb1e678ef2

using { BusinessPartnerService as external } from './external/BusinessPartnerService.cds';

service ConsumeBPService {
    entity LocalBusinessPartners as projection on external.BusinessPartners;
}

キャッシュとリトライの実装

以下のステップでキャッシュとリトライ機能を実装します。

  1. Resilience APIをDependencyに追加
  2. キャッシュの実装
  3. リトライの実装

1. Resilience APIをDependencyに追加

プロジェクトルートのpom.xmlに以下の設定を追加します。

	<properties>
		<cloud.sdk.version><最新のSDKバージョン></cloud.sdk.version>
        ...
  	</properties>

	<dependencyManagement>
		<dependencies>
            ...
			<dependency>
				<groupId>com.sap.cloud.sdk</groupId>
				<artifactId>sdk-modules-bom</artifactId>
				<version>${cloud.sdk.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
   		</dependencies>
   	</dependencyManagement>

次に、srv/pom.xmlに以下の設定を追加します。

	<dependencies>
		<!-- SAP CLOUD SDK RESILIENCE MODULE -->
		<dependency>
			<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
			<artifactId>resilience</artifactId>
		</dependency>

		<!-- JCache -->
		<dependency>
			<groupId>com.github.ben-manes.caffeine</groupId>
			<artifactId>jcache</artifactId>
			<version>3.1.8</version>
			<scope>runtime</scope>
		</dependency>
	</dependencies>

2. キャッシュの実装

2.1. キャッシュ実装前

キャッシュを実装する前のイベントハンドラは以下のようになっています。

@Component
@ServiceName(ConsumeBPService_.CDS_NAME)
public class ConsumeBPServiceHandler implements EventHandler {

    @Autowired
    @Qualifier(BusinessPartnerService_.CDS_NAME)
    CqnService bupa;

    @On(event = CqnService.EVENT_READ, entity = LocalBusinessPartners_.CDS_NAME)
    public Result onReadBusinessPartners(CdsReadEventContext context) {
        // リモートサービスでクエリを実行
        return bupa.run(context.getCqn());
    }
}

2.2. キャッシュ実装

以下のステップで、リモートサービスの呼び出し結果をキャッシュします。

  • CacheConfigurationを作成CacheConfigurationにはキャッシュの有効期間とパラメータ(キャッシュを保管する際のキー)を指定する。このほかに、有効期間の計算の起点を表すexpirationStrategyを指定することもできる
  • ResilienceConfigurationを作成ResilienceConfigurationを作成し、キャッシュの設定を渡す
  • リモートサービスを呼び出しResilienceDecorator.executeSupplier()でリモートサービスの呼び出しを実行する。引数にリモートサービスの呼び出し処理と、ResilienceConfigurationを渡す

参考:Class ResilienceConfiguration.CacheConfiguration

    @On(event = CqnService.EVENT_READ, entity = LocalBusinessPartners_.CDS_NAME)
    public Result onReadBusinessPartners(CdsReadEventContext context) {
        String queryKey = context.getCqn().toString();

        // CacheConfigurationを作成
        ResilienceConfiguration.CacheConfiguration cacheConfig = ResilienceConfiguration.CacheConfiguration
            .of(Duration.ofSeconds(30)) // キャッシュの有効期間
            .withParameters(queryKey); //キャッシュのパラメータを指定

        // ResilienceConfigurationを作成
        ResilienceConfiguration resilienceConfig = ResilienceConfiguration.of(BusinessPartnerService.class)
            .cacheConfiguration(cacheConfig);      

        // リモートサービス呼び出し
        Result result = ResilienceDecorator.executeSupplier(() -> callBusinessPartnerService(context), resilienceConfig);

        return result;
    }

    private Result callBusinessPartnerService(CdsReadEventContext context) {
        return bupa.run(context.getCqn());
    }            

キャッシュのパラメータについて
パラメータを指定しない場合、クエリ条件を変えても同じ結果(キャッシュされた結果)が返ってきました。パラメータを指定することで実行条件と結果をひもづけておく必要があります。

2.3. 動作確認

リモートサービスへのアクセスのアクセス時間を計測できるようにログを追加して、動作を確認してみます。

        Instant startTime = Instant.now();
        
        // リモートサービスへのアクセス
        
        long totalTimeMs = Duration.between(startTime, Instant.now()).toMillis();
        logger.info("Request completed - ExecutionTime: {}ms", totalTimeMs);

以下のリクエストでビジネスパートナー全件(10件)を取得します。

リクエスト
GET {{server}}/odata/v4/ConsumeBPService/LocalBusinessPartners
Authorization: Basic {{username}}:{{password}}

キャッシュを使うことで、2回目以降のレスポンスが早くなっています。

初回アクセス(キャッシュなし):30ms
image.png

2回目のアクセス(キャッシュあり):5ms
image.png

30秒経過後(キャッシュなし):16ms
image.png

キャッシュされたデータはメモリに格納されるので、大量のデータを少しずつ条件を変えながら取得するケースではメモリの使用量が増加すると予想されます。キャッシュ対象のリクエストとキャッシュの保存期間について、慎重に検討する必要があります。

3. リトライの実装

3.1. リトライ実装

リトライ機能もResilienceConfigurationの一部として提供されています。以下では、ステップ2.で作成したResilienceConfigurationにリトライの設定を追加します。

  • RetryConfigurationを作成RetryConfigurationにはリトライの回数と、リトライする前の待ち時間などを指定できる

参考:Class ResilienceConfiguration.RetryConfiguration

        // RetryConfigurationを作成
        ResilienceConfiguration.RetryConfiguration retryConfig = ResilienceConfiguration.RetryConfiguration
            .of(3)
            .waitDuration(Duration.ofSeconds(10));
        
        // キャッシュの設定(省略)

        // Create Resilience Configuration with Cache and Retry settings
        ResilienceConfiguration resilienceConfig = ResilienceConfiguration.of(BusinessPartnerService.class)
            .retryConfiguration(retryConfig)
            .cacheConfiguration(cacheConfig);

3.2. 動作確認

リモートサービスを停止させた状態で実行します。3回呼び出しを行った後、エラーで終了しました。

image.png

1回目の呼び出しの直後にリモートサービスを起動すると、2回目のリクエストは成功し、正常にレスポンスが返ってきます。
image.png

おわりに

この記事では、SAP Cloud SDKを使いCAP Javaのアプリケーションにキャッシュとリトライ機能を組み込みました。複雑なコーディングなしにこれらの機能を使えるのがうれしく感じました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?