この記事は、ポータブル Durable Task SDK を使用してアプリケーションをビルドするときの一般的な問題を診断して修正するのに役立ちます。 次の一覧でシナリオを見つけ、リンクされた手順に従って問題を診断して解決します。
一般的なシナリオ
接続とセットアップ
- エミュレーターが実行されていないか、到達できない
- 接続文字列の形式が正しくありません
- クライアントまたはワーカーの接続に失敗する
- タスク ハブが存在しない
- Azure でのアイデンティティベースの認証エラー
オーケストレーション
活動
gRPC
ログと診断
言語固有
- C#: ソース ジェネレーターの警告がビルドを中断する
- C#: Roslyn アナライザーが foreach ループで例外をスローする
- Java: Gradle アクセス許可拒否エラー
- Java: OrchestratorBlockedException
- Python: 再試行ポリシーには max_retry_interval が必要です
- Python: WhenAllTask 例外動作
これらの 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) |
接続とセットアップの問題
エミュレーターが実行されていないか、到達できない
アプリが起動時に "接続拒否" や "接続に失敗しました" などの接続エラーで失敗した場合は、Durable Task Scheduler エミュレーターが実行されていてアクセス可能であることを確認します。
エミュレーターの Docker コンテナーが実行されていることを確認します。
docker ps | grep durabletaskポート マッピングが正しいことを確認します。 エミュレーターは、次の 2 つのポートを公開します。
- 8080 - gRPC エンドポイント (アプリで使用)
- 8082 - ダッシュボード UI
カスタム ポート マッピングを使用している場合は、コンテナー ポート
8080にマップされているホスト ポートと一致するように接続文字列を更新します。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にデプロイされたときに失敗した場合、問題は認証に関連している可能性があります。
- マネージド ID がアプリに割り当てられている (システム割り当てまたはユーザー割り当て) ことを確認します。
- スケジューラ リソースまたは特定のタスク ハブで、ID に Durable Task Data Contributor ロールがあることを確認します。
- 接続文字列で正しい
Authentication値 (ManagedIdentity) が使用されていることを確認します。 Pythonでは、接続文字列を使用する代わりに、DefaultAzureCredential()インスタンスをtoken_credentialパラメーターとして渡します。 - ユーザー割り当て 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、または型キャストエラーとして表示されることがあります。
診断方法:
-
Durable Task Scheduler ダッシュボードを開き、オーケストレーション履歴を調べます。 失敗したアクティビティについては、
InputフィールドとResultフィールドを参照してください。 - オーケストレーターが期待する型が、アクティビティによって返される型と一致するかどうかを確認します。 たとえば、アクティビティが
stringを返すが、オーケストレーターがintを期待している場合、逆シリアル化は失敗します。 - シリアル化できない型を確認します。 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 ソース ジェネレーターによって、ビルドを中断する警告 (CS0419、 VSTHRD105) が生成されることがあります。 次の特定の警告を抑制します。
<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 |