次の方法で共有


NoSQL アカウント用 API で Azure Cosmos DB Java SDK v4 を使用する場合の問題のトラブルシューティング

Important

この記事では、Azure Cosmos DB Java SDK v4 のみのトラブルシューティングについて説明します。 詳細については、Azure Cosmos DB Java SDK v4 リリース ノートMaven リポジトリおよびパフォーマンスのヒント を参照してください。 v4 より古いバージョンを現在使用している場合は、v4 へのアップグレードに関するヘルプについては、 Azure Cosmos DB Java SDK v4 への移行 ガイドを参照してください。

この記事では、Azure Cosmos DB for NoSQL アカウントで Azure Cosmos DB Java SDK v4 を使用する場合の一般的な問題、回避策、診断手順、およびツールについて説明します。 Azure Cosmos DB Java SDK v4 は、Azure Cosmos DB for NoSQL にアクセスするためのクライアント側の論理表現を提供します。 この記事では、問題が発生した場合に役立つツールとアプローチについて説明します。

次の一覧から始めます。

  • この記事の一般的な問題と対処法のセクションを確認します。
  • GitHub のオープン ソースである Azure Cosmos DB 中央リポジトリの Java SDK を確認します。 アクティブに監視されている 問題セクション があります。 回避策が既に提出済みの同様の問題がないか確認します。 役立つヒントの 1 つは、 *cosmos:v4-item* タグで問題をフィルター処理することです。
  • Azure Cosmos DB Java SDK v4 のパフォーマンスに関する ヒント を確認し、推奨されるプラクティスに従ってください。
  • 解決策が見つからない場合は、この記事の残りの部分をお読みください。 次に、 GitHub の問題を報告します。 GitHub の問題にタグを追加するオプションがある場合は、 *cosmos:v4-item* タグを追加します。

診断をキャプチャする

Java V4 SDK のデータベース、コンテナー、項目、クエリの応答には、Diagnostics プロパティがあります。 このプロパティでは、再試行または一時的な失敗があるかどうかなど、1 つの要求に関連するすべての情報が記録されます。

診断は文字列として返されます。 文字列は、さまざまなシナリオのトラブルシューティングを改善するために改善されるため、各バージョンで変更されます。 SDK の各バージョンでは、文字列の形式が壊れる可能性があります。 破壊的変更を避けるために、文字列を解析しないでください。

次のコード サンプルは、Java V4 SDK を使用して診断ログを読み取る方法を示しています。

Important

Java V4 SDK の最小推奨バージョンを検証し、このバージョン以上を使用していることを確認することをお勧めします。 推奨バージョン はこちらで確認できます。

データベース操作

CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists(databaseName);
CosmosDiagnostics diagnostics = databaseResponse.getDiagnostics();
logger.info("Create database diagnostics : {}", diagnostics); 

コンテナー操作

CosmosContainerResponse containerResponse = database.createContainerIfNotExists(containerProperties,
                  throughputProperties);
CosmosDiagnostics diagnostics = containerResponse.getDiagnostics();
logger.info("Create container diagnostics : {}", diagnostics);

項目の操作

// Write Item
CosmosItemResponse<Family> item = container.createItem(family, new PartitionKey(family.getLastName()),
                    new CosmosItemRequestOptions());
        
CosmosDiagnostics diagnostics = item.getDiagnostics();
logger.info("Create item diagnostics : {}", diagnostics);
        
// Read Item
CosmosItemResponse<Family> familyCosmosItemResponse = container.readItem(documentId,
                    new PartitionKey(documentLastName), Family.class);
        
CosmosDiagnostics diagnostics = familyCosmosItemResponse.getDiagnostics();
logger.info("Read item diagnostics : {}", diagnostics);

クエリ操作

String sql = "SELECT * FROM c WHERE c.lastName = 'Witherspoon'";
        
CosmosPagedIterable<Family> filteredFamilies = container.queryItems(sql, new CosmosQueryRequestOptions(),
                    Family.class);
        
//  Add handler to capture diagnostics
filteredFamilies = filteredFamilies.handle(familyFeedResponse -> {
    logger.info("Query Item diagnostics through handle : {}", 
    familyFeedResponse.getCosmosDiagnostics());
});
        
//  Or capture diagnostics through iterableByPage() APIs.
filteredFamilies.iterableByPage().forEach(familyFeedResponse -> {
    logger.info("Query item diagnostics through iterableByPage : {}",
    familyFeedResponse.getCosmosDiagnostics());
});

Azure Cosmos DB の例外

try {
  CosmosItemResponse<Family> familyCosmosItemResponse = container.readItem(documentId,
                    new PartitionKey(documentLastName), Family.class);
} catch (CosmosException ex) {
  CosmosDiagnostics diagnostics = ex.getDiagnostics();
  logger.error("Read item failure diagnostics : {}", diagnostics);
}

診断のログ記録

Java V4 SDK バージョン v4.43.0 以降では、すべての要求またはエラーが特定の条件を満たしている場合、Cosmos Diagnostics の自動ログ記録がサポートされます。 アプリケーション開発者は、待機時間のしきい値 (ポイント (作成、読み取り、置換、アップサート、パッチ) またはポイント以外の操作 (クエリ、変更フィード、一括、バッチ))、要求の料金、ペイロード サイズを定義できます。 要求がこれらの定義されたしきい値を超えると、それらの要求に対するCosmos診断が自動的に出力されます。

既定では、Java v4 SDK は、これらの診断を特定の形式で自動的にログに記録します。 ただし、これを変更するには、 CosmosDiagnosticsHandler インターフェイスを実装し、独自のカスタム診断ハンドラーを提供します。

これらのCosmosDiagnosticsThresholdsCosmosDiagnosticsHandlerCosmosClientTelemetryConfigオブジェクトで使用することができ、そのオブジェクトは同期または非同期クライアントを作成する際にCosmosClientBuilderに渡す必要があります。

注: これらの診断しきい値は、ログ記録、トレース、クライアント テレメトリなど、さまざまな種類の診断に適用されます。

次のコード サンプルは、診断しきい値、カスタム診断ロガーを定義し、クライアント テレメトリ構成を使用して使用する方法を示しています。

カスタム診断しきい値の定義

//  Create diagnostics threshold
CosmosDiagnosticsThresholds cosmosDiagnosticsThresholds = new CosmosDiagnosticsThresholds();
//  These thresholds are for demo purposes
//  NOTE: Do not use the same thresholds for production
cosmosDiagnosticsThresholds.setPayloadSizeThreshold(100_00);
cosmosDiagnosticsThresholds.setPointOperationLatencyThreshold(Duration.ofSeconds(1));
cosmosDiagnosticsThresholds.setNonPointOperationLatencyThreshold(Duration.ofSeconds(5));
cosmosDiagnosticsThresholds.setRequestChargeThreshold(100f);

カスタム診断ハンドラーの定義

//  By default, DEFAULT_LOGGING_HANDLER can be used
CosmosDiagnosticsHandler cosmosDiagnosticsHandler = CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER;

//  App developers can also define their own diagnostics handler
cosmosDiagnosticsHandler = new CosmosDiagnosticsHandler() {
    @Override
    public void handleDiagnostics(CosmosDiagnosticsContext diagnosticsContext, Context traceContext) {
        logger.info("This is custom diagnostics handler: {}", diagnosticsContext.toJson());
    }
};

CosmosClientTelemetryConfig の定義

//  Create Client Telemetry Config
CosmosClientTelemetryConfig cosmosClientTelemetryConfig =
    new CosmosClientTelemetryConfig();
cosmosClientTelemetryConfig.diagnosticsHandler(cosmosDiagnosticsHandler);
cosmosClientTelemetryConfig.diagnosticsThresholds(cosmosDiagnosticsThresholds);

//  Create sync client
CosmosClient client = new CosmosClientBuilder()
    .endpoint(AccountSettings.HOST)
    .key(AccountSettings.MASTER_KEY)
    .clientTelemetryConfig(cosmosClientTelemetryConfig)
    .buildClient();

再試行設計

耐障害性のあるアプリケーションの設計方法に関するガイダンスについては、Azure Cosmos DB SDK での耐障害性のあるアプリケーションの設計に関するガイドを参照してください。また SDK の再試行セマンティクスの内容について学習してください。

一般的な問題と対処法

ポータルのメトリックを確認する

ポータルメトリックを確認すると、クライアント側の問題であるか、サービスに問題があるかどうかが判断されます。 たとえば、要求が調整されていることを意味する、高いレートのレート制限された要求 (HTTP 状態コード 429) がメトリックに含まれていれば、「要求率が大きすぎる」のセクションを確認してください。

ネットワークの問題、Netty 読み取りタイムアウトエラー、低スループット、高待機時間

一般的な推奨事項

パフォーマンスを最大限高めるためのヒントを示します。

  • アプリが Azure Cosmos DB アカウントと同じリージョンで実行されていることを確認します。
  • アプリが実行されているホストの CPU 使用率を確認します。 CPU 使用率が 50% 以上の場合は、より高い構成でホストでアプリを実行します。 または、より多くのマシンに負荷を分散させることができます。

接続の調整

接続の調整は、 ホスト コンピューターの接続制限 または Azure SNAT (PAT) ポートの枯渇が原因で発生する可能性があります。

ホスト コンピューターの接続制限

Red Hat などの一部の Linux システムでは、開いているファイルの合計数に上限があります。 Linux のソケットはファイルとして実装されるため、この数によって接続の合計数も制限されます。 次のコマンドを実行します。

ulimit -a

"nofile" として識別される、許可される最大開いているファイルの数は、接続プールのサイズの少なくとも 2 倍である必要があります。 詳細については、Azure Cosmos DB Java SDK v4 のパフォーマンスに関するヒントを参照してください。

Azure SNAT (PAT) ポート不足

アプリがパブリック IP アドレスなしで Azure Virtual Machines にデプロイされている場合、既定では 、Azure SNAT ポート は VM の外部にある任意のエンドポイントへの接続を確立します。 VM から Azure Cosmos DB エンドポイントへの許可される接続の数は、Azure SNAT 構成によって制限されます。

Azure SNAT ポートは、VM にプライベート IP アドレスがあり、VM からのプロセスがパブリック IP アドレスへの接続を試みる場合にのみ使用されます。 Azure SNAT の制限を回避するには、次の 2 つの回避策があります。

  • Azure Virtual Machines 仮想ネットワークのサブネットに Azure Cosmos DB サービス エンドポイントを追加します。 詳細については、Azure 仮想ネットワーク サービス エンドポイントに関するページを参照してください。

    サービス エンドポイントが有効になると、要求はパブリック IP から Azure Cosmos DB に送信されなくなります。 代わりに、仮想ネットワークとサブネット ID が送信されます。 この変更により、パブリック IP のみが許可された場合はファイアウォール ドロップが発生することがあります。 ファイアウォールを使用している場合、サービス エンドポイントを有効にするときに、Virtual Network ACL を使用してファイアウォールにサブネットを追加します。

  • Azure VM にパブリック IP を割り当てます。

サービスに到達できない - ファイアウォール

ConnectTimeoutException は、SDK がサービスに到達できないことを示します。 ダイレクト モードを使用すると、次のようなエラーが発生する場合があります。

GoneException{error=null, resourceAddress='https://cdb-ms-prod-westus-fd4.documents.azure.com:14940/apps/e41242a5-2d71-5acb-2e00-5e5f744b12de/services/d8aa21a5-340b-21d4-b1a2-4a5333e7ed8a/partitions/ed028254-b613-4c2a-bf3c-14bd5eb64500/replicas/131298754052060051p//', statusCode=410, message=Message: The requested resource is no longer available at the server., getCauseInfo=[class: class io.netty.channel.ConnectTimeoutException, message: connection timed out: cdb-ms-prod-westus-fd4.documents.azure.com/101.13.12.5:14940]

アプリ コンピューターでファイアウォールが実行されている場合は、ポート範囲 10,000 ~ 20,000 を開きます。これはダイレクト モードで使用されます。 また、 ホスト コンピューターの接続制限に従います

UnknownHostException(不明ホスト例外)

UnknownHostException は、Java フレームワークで、影響を受けるマシンで Azure Cosmos DB エンドポイントの DNS エントリを解決できないことを意味します。 マシンで DNS エントリを解決できることを確認するか、カスタム DNS 解決ソフトウェア (VPN、プロキシ、カスタム ソリューションなど) がある場合は、エラーで解決できないことが示されている DNS エンドポイントに適切な構成が含まれていることを確認してください。 エラーがいつも同じである場合は、エラーで説明されているエンドポイントに対して curl コマンドを使用して、マシンの DNS 解決を確認することができます。

HTTP プロキシ

HTTP プロキシを使用する場合は、SDK ConnectionPolicyで構成されている接続の数をサポートできることを確認します。 そうしないと、接続の問題が発生します。

無効なコーディング パターン: Netty IO スレッドのブロック

SDK では 、Netty IO ライブラリを使用して Azure Cosmos DB と通信します。 SDK には非同期 API があり、Netty の非ブロッキング IO API を使用します。 SDK の IO 作業は、IO Netty スレッドで実行されます。 IO Netty スレッドの数は、アプリ コンピューターの CPU コア数と同じに構成されます。

Netty IO スレッドは、非ブロッキング Netty IO 作業にのみ使用されます。 SDK は、Netty IO スレッドの 1 つで API 呼び出しの結果をアプリのコードに返します。 Netty スレッドで結果を受け取った後にアプリが長時間の操作を実行する場合、SDK には内部 IO 処理を実行するのに十分な IO スレッドがない可能性があります。 このようなアプリのコーディングにより、スループットが低く、待機時間が長く、 io.netty.handler.timeout.ReadTimeoutException エラーが発生する可能性があります。 回避策は、操作に時間がかかることがわかっているときにスレッドを切り替える方法です。

たとえば、コンテナーに項目を追加する次のコード スニペットを見てみましょう (データベースとコンテナーの設定に関するガイダンス については、こちらを 参照してください)。Netty スレッドでは、数ミリ秒以上かかる長期的な作業を実行できます。 その場合、最終的には、IO 処理を処理するために Netty IO スレッドが存在しない状態になる可能性があります。 その結果、ReadTimeoutException エラーが発生します。

Java SDK V4 (Maven com.azure::azure-cosmos) Async API


//Bad code with read timeout exception

int requestTimeoutInSeconds = 10;

/* ... */

AtomicInteger failureCount = new AtomicInteger();
// Max number of concurrent item inserts is # CPU cores + 1
Flux<Family> familyPub =
        Flux.just(Families.getAndersenFamilyItem(), Families.getAndersenFamilyItem(), Families.getJohnsonFamilyItem());
familyPub.flatMap(family -> {
    return container.createItem(family);
}).flatMap(r -> {
    try {
        // Time-consuming work is, for example,
        // writing to a file, computationally heavy work, or just sleep.
        // Basically, it's anything that takes more than a few milliseconds.
        // Doing such operations on the IO Netty thread
        // without a proper scheduler will cause problems.
        // The subscriber will get a ReadTimeoutException failure.
        TimeUnit.SECONDS.sleep(2 * requestTimeoutInSeconds);
    } catch (Exception e) {
    }
    return Mono.empty();
}).doOnError(Exception.class, exception -> {
    failureCount.incrementAndGet();
}).blockLast();
assert(failureCount.get() > 0);

回避策は、時間がかかる作業を実行するスレッドを変更することです。 アプリのスケジューラのシングルトン インスタンスを定義します。

Java SDK V4 (Maven com.azure::azure-cosmos) Async API

// Have a singleton instance of an executor and a scheduler.
ExecutorService ex  = Executors.newFixedThreadPool(30);
Scheduler customScheduler = Schedulers.fromExecutor(ex);

計算負荷の高い作業や IO のブロックなど、時間がかかる作業が必要な場合があります。 この場合は、customScheduler API を使用して、.publishOn(customScheduler)によって提供されるワーカーにスレッドを切り替えます。

Java SDK V4 (Maven com.azure::azure-cosmos) Async API

container.createItem(family)
        .publishOn(customScheduler) // Switches the thread.
        .subscribe(
                // ...
        );

publishOn(customScheduler)を使用すると、Netty IO スレッドを解放し、カスタム スケジューラによって提供される独自のカスタム スレッドに切り替えます。 この変更により、問題が解決されます。 io.netty.handler.timeout.ReadTimeoutExceptionエラーは発生しなくなります。

要求率が大きすぎる

このエラーは、サーバー側の障害です。 これは、プロビジョニングされたスループットを使用したことを示します。 後で再試行してください。 このエラーが頻繁に発生する場合は、コレクションのスループットの増加を検討してください。

  • getRetryAfterInMilliseconds 間隔でバックオフを実装する

    パフォーマンス テスト中は、要求のレートが小さくなるまで負荷を増やす必要があります。 スロットルされた場合、クライアントアプリケーションはそのサーバーが指定する再試行間隔に応じて待機する必要があります。 バックオフを尊重することで、再試行の間に待機する時間を最小限に抑えることができます。

Java SDK リアクティブ チェーンからのエラー処理

Azure Cosmos DB Java SDK からのエラー処理は、クライアントのアプリケーション ロジックに関して重要です。 reactor-core フレームワークによって提供されるさまざまなエラー処理メカニズムがあり、さまざまなシナリオで使用できます。 これらのエラー処理演算子を詳しく理解し、再試行ロジックのシナリオに最適なものを使用することをお勧めします。

Important

onErrorContinue()演算子は、すべてのシナリオでサポートされているわけではないため、使用しないことをお勧めします。 onErrorContinue()は、反応性チェーンの動作を明確にできない専門オペレーターであることに注意してください。 ダウンストリームの演算子ではなくアップストリームで動作し、特定の演算子のサポートが必要であり、スコープはアップストリームを予測できなかったライブラリ コードに簡単に伝達できます (意図しない動作が発生します)。 この特別な演算子の詳細については、onErrorContinue()を参照してください。

Azure Cosmos DB Emulator への接続に失敗する

Azure Cosmos DB Emulator HTTPS 証明書は自己署名されています。 SDK でエミュレーターを操作するには、エミュレーター証明書を Java TrustStore にインポートします。 詳細については、「 Azure Cosmos DB Emulator 証明書のエクスポート」を参照してください。

依存関係の競合の問題

Azure Cosmos DB Java SDK は、多くの依存関係を取り込みます。一般に、プロジェクトの依存関係ツリーに Azure Cosmos DB Java SDK が依存する古いバージョンの成果物が含まれている場合、アプリケーションの実行時に予期しないエラーが生成される可能性があります。 アプリケーションが予期せず例外をスローする理由をデバッグする場合は、依存関係ツリーが 1 つ以上の Azure Cosmos DB Java SDK 依存関係の古いバージョンを誤ってプルしていないことを再確認することをお勧めします。

このような問題の回避策は、プロジェクトの依存関係のうち、古いバージョンを取り込むプロジェクトの依存関係を特定し、その古いバージョンに対する推移的な依存関係を除外し、Azure Cosmos DB Java SDK で新しいバージョンを取り込むことです。

Azure Cosmos DB Java SDK が依存する古いバージョンのプロジェクト依存関係を特定するには、プロジェクト pom.xml ファイルに対して次のコマンドを実行します。

mvn dependency:tree

詳細については、 Maven 依存関係ツリー ガイドを参照してください。

プロジェクトのどの依存関係が古いバージョンに依存しているかがわかったら、pom ファイル内のその lib への依存関係を変更し、次の例に従って推移的な依存関係を除外できます ( reactor-core が古い依存関係であると想定しています)。

<dependency>
  <groupId>${groupid-of-lib-which-brings-in-reactor}</groupId>
  <artifactId>${artifactId-of-lib-which-brings-in-reactor}</artifactId>
  <version>${version-of-lib-which-brings-in-reactor}</version>
  <exclusions>
    <exclusion>
      <groupId>io.projectreactor</groupId>
      <artifactId>reactor-core</artifactId>
    </exclusion>
  </exclusions>
</dependency>

詳細については、 推移的な依存関係の除外に関するガイドを参照してください。

クライアント SDK のログ記録を有効にする

Azure Cosmos DB Java SDK v4 では、log4j や logback などの一般的なログ記録フレームワークへのログ記録をサポートするログファサードとして SLF4j が使用されます。

たとえば、ログ記録フレームワークとして log4j を使用する場合は、Java クラスパスに次の lib を追加します。

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>${slf4j.version}</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>${log4j.version}</version>
</dependency>

log4j 構成も追加します。

# this is a sample log4j configuration

# Set root logger level to INFO and its only appender to A1.
log4j.rootLogger=INFO, A1

log4j.category.com.azure.cosmos=INFO
#log4j.category.io.netty=OFF
#log4j.category.io.projectreactor=OFF
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %5X{pid} [%t] %-5p %c - %m%n

詳細については、 sfl4j ログマニュアルを参照してください。

OS ネットワークの統計情報

netstat コマンドを実行して、 ESTABLISHEDCLOSE_WAITなどの状態にある接続の数を把握します。

Linux では、次のコマンドを実行できます。

netstat -nap

Windows では、異なる引数フラグを使用して同じコマンドを実行できます。

netstat -abn

結果をフィルター処理して、Azure Cosmos DB エンドポイントへの接続のみにします。

ESTABLISHED状態の Azure Cosmos DB エンドポイントへの接続の数は、構成された接続プール のサイズを超えることはできません。

Azure Cosmos DB エンドポイントへの多くの接続が CLOSE_WAIT 状態である可能性があります。 1,000 を超える可能性があります。 高い数値は、接続が確立され、迅速に切断されることを示します。 この状況により、問題が発生する可能性があります。 詳細については、「 一般的な問題と回避策 」セクションを参照してください。

一般的な問い合わせの問題

クエリ メトリックは、クエリがほとんどの時間を費やしている場所を判断するのに役立ちます。 クエリ メトリックから、バックエンドとクライアントでどれだけの時間が費やされているかを確認できます。 詳細については、クエリ パフォーマンス ガイドを参照してください。

次のステップ

  • Java SDK v4 のパフォーマンス ガイドラインについて説明します
  • Java SDK v4 のベスト プラクティスについて説明します