Durable Task SDK に関する一般的な問題のトラブルシューティング

この記事は、ポータブル Durable Task SDK を使用してアプリケーションをビルドするときの一般的な問題を診断して修正するのに役立ちます。 次の一覧でシナリオを見つけ、リンクされた手順に従って問題を診断して解決します。

一般的なシナリオ

接続とセットアップ

オーケストレーション

活動

gRPC

ログと診断

言語固有

これらの SDK は、Durable Task Scheduler バックエンドに接続し、Azure Container Apps、Kubernetes、VM など、任意のホスティング プラットフォームで実行されます。

このガイドでは、 ポータブル Durable Task SDK について説明します。 Durable Task Scheduler サービスに固有の問題については、「 Durable Task Scheduler のトラブルシューティング」を参照してください。 Durable Functions拡張機能に固有の問題については、Durable Functionsトラブルシューティング ガイドを参照してください。

ヒント

Durable Task Scheduler 監視ダッシュボードは、オーケストレーションの状態の検査、実行履歴の表示、およびエラーの識別に役立ちます。 トラブルシューティングを高速化するには、このガイドと共に使用します。

問題を見つける

エラー メッセージまたは症状 セクション
connection refusedまたは起動時にfailed to connect エミュレーターが実行されていないか、到達できない
接続文字列の解析エラーまたは起動時の認証エラー 接続文字列の形式が正しくありません
ワーカーは接続しますが、オーケストレーションは開始されません タスク ハブが存在しない
Azureでの 401 Unauthorized エラーまたは ID/ロール エラー Azure でのアイデンティティベースの認証エラー
オーケストレーションが [保留中] で停止する オーケストレーションが「保留中」状態で詰まっている
オーケストレーションが [実行中] で停止する [実行中] 状態で停止しているオーケストレーション
再生エラー、無限ループ、または予期しない動作 非決定的オーケストレーター コード
型の不一致または JSON シリアル化エラー シリアル化と逆シリアル化のエラー
activity not found アクティビティが見つかりません
RESOURCE_EXHAUSTED または message too large gRPC メッセージ サイズの制限を超えました
CANCELLED: Cancelled on client シャットダウン中 シャットダウン中のストリーム キャンセル エラー
CS0419 / VSTHRD105 警告がビルドを中断する ソース ジェネレーターの警告によってビルドが中断される (C#)
OrchestratorBlockedException (Java) OrchestratorBlockedException (Java)
retry_policy の使用時に役に立たないエラー (Python) Retry ポリシーには、max_retry_interval (Python)

接続とセットアップの問題

エミュレーターが実行されていないか、到達できない

アプリが起動時に "接続拒否" や "接続に失敗しました" などの接続エラーで失敗した場合は、Durable Task Scheduler エミュレーターが実行されていてアクセス可能であることを確認します。

  1. エミュレーターの Docker コンテナーが実行されていることを確認します。

    docker ps | grep durabletask
    
  2. ポート マッピングが正しいことを確認します。 エミュレーターは、次の 2 つのポートを公開します。

    • 8080 - gRPC エンドポイント (アプリで使用)
    • 8082 - ダッシュボード UI

    カスタム ポート マッピングを使用している場合は、コンテナー ポート 8080 にマップされているホスト ポートと一致するように接続文字列を更新します。

  3. gRPC エンドポイントへの接続をテストします。

    curl -v http://localhost:8080
    

    接続の拒否は、コンテナーが実行されていないか、ポート マッピングが正しくないことを示します。

接続文字列の形式が正しくありません

接続文字列エラーは、スタートアップ エラーの一般的な原因です。 接続文字列が想定される形式と一致することを確認します。

ローカル開発 (エミュレーター):

Endpoint=http://localhost:8080;Authentication=None

Azure (マネージド ID):

Endpoint=https://<scheduler-name>.durabletask.io;Authentication=ManagedIdentity

Azure (ユーザー割り当てマネージド ID):

Endpoint=https://<scheduler-name>.durabletask.io;Authentication=ManagedIdentity;ClientID=<client-id>

一般的な間違い

  • ローカル エミュレーターに https を使用する (エミュレーターは httpを使用します)
  • Azure エンドポイントに http を使用する (Azure には https が必要)
  • Authentication パラメーターの省略
  • gRPC ポート (8082) ではなくダッシュボード ポート (8080) を使用する

クライアントまたはワーカーの接続に失敗する

クライアントとワーカーが正しい接続文字列とタスク ハブ名で構成されていることを確認します。

using Microsoft.DurableTask.Client.AzureManaged;
using Microsoft.DurableTask.Worker.AzureManaged;

var connectionString = "Endpoint=http://localhost:8080;Authentication=None";

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddDurableTaskWorker()
    .AddTasks(registry =>
    {
        registry.AddOrchestrator<MyOrchestrator>();
        registry.AddActivity<MyActivity>();
    })
    .UseDurableTaskScheduler(connectionString);

builder.Services.AddDurableTaskClient()
    .UseDurableTaskScheduler(connectionString);

タスク ハブが存在しない

オーケストレーションの開始に失敗した場合、またはワーカーが接続しても処理が行われない場合は、タスク ハブがスケジューラに存在しない可能性があります。 エミュレーターは通常、 DTS_TASK_HUB_NAMES 環境変数を使用してタスク ハブを自動的に作成します。

エミュレーターが正しいタスク ハブ名で起動されたことを確認します。

docker run -d -p 8080:8080 -p 8082:8082 \
  -e DTS_TASK_HUB_NAMES="my-taskhub" \
  mcr.microsoft.com/dts/dts-emulator:latest

Azureホステッド スケジューラの場合は、Azure CLIを使用してタスク ハブを作成します。

az durabletask taskhub create \
  --resource-group <resource-group> \
  --scheduler-name <scheduler-name> \
  --name <taskhub-name>

Azureでの ID ベースの認証エラー

アプリがローカルで実行されていても、Azureにデプロイされたときに失敗した場合、問題は認証に関連している可能性があります。

  1. マネージド ID がアプリに割り当てられている (システム割り当てまたはユーザー割り当て) ことを確認します。
  2. スケジューラ リソースまたは特定のタスク ハブで、ID に Durable Task Data Contributor ロールがあることを確認します。
  3. 接続文字列で正しい Authentication 値 (ManagedIdentity) が使用されていることを確認します。 Pythonでは、接続文字列を使用する代わりに、DefaultAzureCredential() インスタンスを token_credential パラメーターとして渡します。
  4. ユーザー割り当て ID の場合は、接続文字列の ClientIDが ID のクライアント ID と一致することを確認します。

詳細な手順については、「 Durable Task Scheduler のマネージド ID を構成する」を参照してください。

オーケストレーションの問題

オーケストレーションが「保留中」状態で停止している

"Pending (保留中)" の状態にあるオーケストレーションは、そのオーケストレーションがスケジュール設定されたが、ワーカーがそれをまだ取得していないことを示します。 次の項目を確認します。

  • ワーカーが動作しています。 ワーカー プロセスが実行され、オーケストレーションがスケジュールされたのと同じタスク ハブに接続されていることを確認します。
  • タスク ハブ名が一致します。 ワーカーとクライアントの両方が同じタスク ハブ名を参照していることを確認します。 不一致が原因で、ワーカーは別のタスク ハブをポーリングします。
  • オーケストレーターが登録されています。 スケジュール時に参照されるオーケストレーター関数またはクラスをワーカーに登録する必要があります。

起動時にオーケストレーター クラスがワーカーに登録されていることを確認します。 ソース ジェネレーター ([DurableTask] 属性) を使用する場合、登録は自動的に行われます。 それ以外の場合は、手動で登録します。

builder.Services.AddDurableTaskWorker()
    .AddTasks(registry =>
    {
        registry.AddOrchestrator<MyOrchestrator>();
        registry.AddActivity<MyActivity>();
    })
    .UseDurableTaskScheduler(connectionString);

[実行中] 状態で停止しているオーケストレーション

オーケストレーションが "実行中" で停止している場合は、通常、完了していないタスクを待機していることを意味します。 診断するには、 Durable Task Scheduler ダッシュボード を開き、オーケストレーションの実行履歴を調べます。 最後に完了したイベントを探します。シーケンス内の次のイベントがブロックしているものです。

一般的な原因:

  • アクティビティが登録されていません。 オーケストレーションは、ワーカーに登録されていないアクティビティ名を呼び出します。 ダッシュボードには、対応するTaskScheduledのないTaskCompleted イベントが表示されます。 オーケストレーター コードと worker 登録の間でアクティビティ名が一致することを確認します ( 「アクティビティが見つかりません」を参照)。
  • 外部イベントを待機しています。 オーケストレーションは waitForExternalEvent を呼び出しますが、イベントはまだ発生していません。 ダッシュボードには、 EventRaised イベントが予想されますが、見つからないと表示されます。 イベント名と、送信者が正しいオーケストレーション インスタンス ID をターゲットにしていることを確認します。
  • 耐久性のあるタイマーを待っています。 オーケストレーションによって、まだ期限切れになっていないタイマーが作成されます。 ダッシュボードには、 TimerCreated イベントが表示されます。 タイマーが起動するのを待つか、タイマーの継続時間が予想よりも長いかどうかを確認します。
  • アクティビティが未処理の例外を発生させます。 ダッシュボードには、 TaskFailed イベントが表示されます。 例外メッセージとスタック トレースのエラーの詳細を確認します。

非決定的オーケストレーター コード

オーケストレーター コードは 決定論的である必要があります。 非決定的コードでは、再生エラーが発生し、予期しない動作、無限ループ、またはエラーが発生します。 オーケストレーター コードでは、現在の時刻、乱数、GUID、または I/O (HTTP 呼び出しなど) を直接使用しないでください。 コンテキストによって提供される代替手段を使用するか、アクティビティに委任します。

// ❌ Wrong - non-deterministic
var now = DateTime.UtcNow;
var id = Guid.NewGuid();
var data = await httpClient.GetAsync("https://example.com/api");

// ✅ Correct - deterministic
var now = context.CurrentUtcDateTime;
var id = context.NewGuid();
var data = await context.CallActivityAsync<string>("FetchData");

シリアル化と逆シリアル化のエラー

シリアル化エラーは、オーケストレーションの入力、出力、またはアクティビティの結果に使用される型が呼び出し元と呼び出し先の間で一致しない場合に発生します。 これらのエラーは、オーケストレーション履歴で予期しない null 値、 JsonException、または型キャストエラーとして表示されることがあります。

診断方法:

  1. Durable Task Scheduler ダッシュボードを開き、オーケストレーション履歴を調べます。 失敗したアクティビティについては、 Input フィールドと Result フィールドを参照してください。
  2. オーケストレーターが期待する型が、アクティビティによって返される型と一致するかどうかを確認します。 たとえば、アクティビティが string を返すが、オーケストレーターが intを期待している場合、逆シリアル化は失敗します。
  3. シリアル化できない型を確認します。 JSON にシリアル化できないカスタム型 (循環参照を含む型や既定のコンストラクターがない型など) は、警告なしに失敗するか、例外をスローします。

Java:アクティビティにStringを直接渡すと、二重引用符で囲まれた文字列 (たとえば、"\"hello\"" ではなく "hello") が発生する可能性があります。 この動作は 既知の問題です。 結果を明示的にキャストするか、ラッパー オブジェクトを使用します。

ヒント

オーケストレーションとアクティビティの入力と出力には、単純なデータ型 (文字列、数値、配列、プレーン オブジェクト、POJO/POCO/データクラス) を使用します。 カスタムシリアル化ロジックを使用して複合型を使用しないようにします。

アクティビティの問題

アクティビティが見つかりません

オーケストレーションが "アクティビティが見つかりません" エラーで失敗した場合、ワーカーに登録されているアクティビティ名がオーケストレーション コードで使用されている名前と一致しません。

.NETでは、アクティビティはクラス名で登録することも、ソース ジェネレーターで [DurableTask] 属性を使用して登録することもできます。 アクティビティ クラスがワーカー登録に含まれていることを確認します。

builder.Services.AddDurableTaskWorker()
    .AddTasks(registry =>
    {
        registry.AddActivity<SayHello>();
    })
    .UseDurableTaskScheduler(connectionString);

オーケストレーターからアクティビティを呼び出す場合は、クラス名を使用します。

string result = await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo");

アクティビティエラーの処理

アクティビティが例外をスローすると、オーケストレーターは TaskFailedException (または同等の言語) を受け取ります。 この例外をキャッチし、内部エラーの詳細を調べて根本原因を見つけます。 C# では、 ex.FailureDetails を使用してエラーの種類とメッセージにアクセスし、 IsCausedBy<T>() を使用して特定の例外の種類を確認します。

各言語でのエラー処理と再試行ポリシーの例の詳細については、「 エラー処理と再試行」を参照してください。

gRPC の問題

gRPC メッセージ サイズの制限を超えました

RESOURCE_EXHAUSTEDまたはmessage too largeエラーが表示された場合、オーケストレーションまたはアクティビティの入力/出力が、gRPC の既定の最大メッセージ サイズである 4 MB を超えています。

緩和 策:

  • 入力と出力のサイズを小さくします。 Azure Blob Storageなどの外部ストレージに大きなペイロードを格納し、参照のみを渡します。
  • 大きなファンアウトの結果を、より小さい複数のバッチに分割します。サブオーケストレーションがそれらを処理します。

シャットダウン中のストリーム キャンセル エラー

ワーカーを停止すると、 CANCELLED: Cancelled on client エラーが表示されることがあります。 通常、これらのエラーは無害であり、ワーカーとスケジューラの間の gRPC ストリームがシャットダウン中に閉じるので発生します。 .NET、Python、およびJava SDK は、これらのエラーを内部的に処理します。

JavaScript では、Stream error Error: 1 CANCELLED: Cancelled on client を呼び出す際に SDK が worker.stop() をスローすることがあります。 このエラーは 既知の問題です。 エラーがシャットダウン ロジックに影響する場合は、try-catch で停止呼び出しをラップします。

try {
  await worker.stop();
} catch (error) {
  // Ignore stream cancellation errors during shutdown
  if (!error.message.includes("CANCELLED")) {
    throw error;
  }
}

ログ記録と診断

詳細ログ設定の構成

SDKの操作、gRPC通信、オーケストレーションの再生イベントなどの詳細を把握できるように、ログの詳細度を上げます。

appsettings.jsonまたはログ記録の構成ファイルで、次の手順を実行します。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.DurableTask": "Debug"
    }
  }
}

オーケストレーションの再生中にログ エントリが重複しないようにするには、リプレイ セーフ ロガーを使用します。

public override async Task<string> RunAsync(
    TaskOrchestrationContext context, string input)
{
    ILogger logger = context.CreateReplaySafeLogger<MyOrchestrator>();
    logger.LogInformation("Processing input: {Input}", input);
    // ...
}

Application Insights の統合

運用アプリケーションの場合は、Durable Task SDK アプリケーションからテレメトリを収集するように Application Insights を構成します。 統合アプローチは、ホスティング プラットフォームによって異なります。

ホスティング プラットフォーム セットアップ手順
Azure Container Apps Azure Container Apps のログを Log Analytics で監視する
Azure App Service Azure App Service でアプリの診断ログを有効にする
Azure Kubernetes Service Azure Kubernetes Service の監視

診断の詳細については、「 Durable Task SDK の診断」を参照してください。

言語固有の問題

C#

ソース ジェネレーターの警告によってビルドが中断される

プロジェクトで <TreatWarningsAsErrors>true</TreatWarningsAsErrors> を使用すると、Durable Task ソース ジェネレーターによって、ビルドを中断する警告 (CS0419VSTHRD105) が生成されることがあります。 次の特定の警告を抑制します。

<PropertyGroup>
  <NoWarn>$(NoWarn);CS0419;VSTHRD105</NoWarn>
</PropertyGroup>

この既知の問題は GitHub で追跡され、今後のリリースで対処されます。

Roslyn アナライザーが foreach ループ内で例外をスローする

オーケストレーターのラムダ コードが ArgumentNullException ループ内にあるときに、Durable Task Roslyn アナライザーが foreach をスローすることがあります。 この動作は、ランタイムの動作に影響しない 既知の問題 です。 最新のアナライザー パッケージ バージョンに更新して、修正プログラムを取得します。

Java

Gradle のアクセス許可が拒否されたエラー

macOS または Linux では、 ./gradlew の実行が失敗し、"アクセス許可が拒否されました" というエラーが表示されることがあります。 ファイルを実行可能にすることで、このエラーを修正します。

chmod +x gradlew

OrchestratorBlockedException

OrchestratorBlockedExceptionは、オーケストレーター コードがブロック操作を実行した結果、SDKがそれを非決定的であると検出したときに発生します。 この例外は、オーケストレーター コードが オーケストレーター コードの制約に違反するのを防ぐためのセーフガードです。

一般的な原因:

  • オーケストレーター コードでのブロッキング外部 API の呼び出し。
  • Thread.sleep()の代わりにctx.createTimer()を直接使用する。
  • オーケストレーター コードでファイルまたはネットワーク I/O を実行する。

すべてのブロック操作または I/O 操作をアクティビティに移動します。

Python

再試行ポリシーにはmax_retry_intervalが必要です

Pythonで retry_policy を構成すると、max_retry_interval パラメーターを省略すると、原因を明確に示さないエラーが発生します。 常に max_retry_intervalを指定します。

from datetime import timedelta
from durabletask import task

retry_policy = task.RetryPolicy(
    max_number_of_attempts=3,
    first_retry_interval=timedelta(seconds=5),
    max_retry_interval=timedelta(minutes=1),  # Required
)

WhenAllTask 例外の動作

when_allを使用して複数のタスクを並列で実行する場合、1 つ以上のタスクが失敗した場合、例外の動作が期待値と一致しない可能性があります。 最初の例外のみが発生し、残りのタスク例外が失われる可能性があります。 完全なエラー情報が必要な場合は、個々のタスクの結果を検査します。

tasks = [ctx.call_activity(process_item, input=item) for item in items]
try:
    results = yield task.when_all(tasks)
except TaskFailedError as e:
    # Only the first failure is raised
    # Check individual tasks for comprehensive error handling
    print(f"At least one task failed: {e}")

サポートを受ける

質問とバグの報告については、関連する SDK の GitHub リポジトリで問題を開きます。 バグを報告する場合は、次を含めます。

  • 影響を受けるオーケストレーション インスタンス ID
  • 問題を示す UTC の時間範囲
  • アプリケーション名とデプロイ リージョン (該当する場合)
  • SDK のバージョンとホスティング プラットフォーム
  • 関連するログまたはエラー メッセージ
SDK GitHub リポジトリ
.NET microsoft/durabletask-dotnet
Java microsoft/durabletask-java
JavaScript microsoft/durabletask-js
Python microsoft/durabletask-python