AWS HealthOmics の Nextflow ワークフローでタスクレベルタイムアウトをを試してみた

AWS HealthOmics の Nextflow ワークフローでタスクレベルタイムアウトをを試してみた

AWS HealthOmics の Nextflow ワークフローで `time` ディレクティブを使ったタスクレベルのタイムアウト制御を実装し、その挙動を詳しく検証してみました。指定時間を超えたタスクの停止、リトライとの組み合わせ、ログ確認方法まで、実運用に必要な知見をまとめています。
2026.08.22

はじめに

AWS HealthOmics は、ゲノム解析などのバイオインフォマティクスワークフローを実行できるマネージドサービスです。プライベートワークフローで Nextflow を使う場合、Nextflow 標準の time ディレクティブでタスクごとの最大実行時間を指定できます。この標準機能を HealthOmics が 1 年前からサポートしていたと知ったので、実際の挙動を確認してみました。

hero-b-flow.png

https://aws.amazon.com/jp/about-aws/whats-new/2025/08/aws-healthomics-task-level-timeout-nextflow-workflows/

確認結果

エンジンは Nextflow 26.04.0 です。HealthOmics ではワークフローの 1 回の実行を run と呼び、run の中で個々のプロセスがタスクとして実行されます。time が働くのはタスク単位です。

  • time '2m' を指定したタスクは指定時間の経過後に停止し、run とタスクのステータスが FAILED になりました
  • errorStrategy 'retry' を併用すると、タイムアウトしたタスクもリトライされました。合計時間はリトライなしのおよそ 2 倍でした
  • タスクが実際に止まるまでには、指定した 120 秒に停止処理のオーバーヘッドが加わりました。オーバーヘッドはタイムアウトした 3 タスクで 54〜74 秒とばらつきました
  • run の statusMessage が名指しするのは最初の試行のタスク ID でした。リトライの有無は list-run-tasks のタスク件数で判別できました
  • タイムアウトを示す文言は CloudWatch Logs には出ず、get-run-taskfailureReason とコンソールの「エラーの理由」で確認できました

hero-c-retry.png

なにが嬉しいのか

HealthOmics へ移植したワークフロー定義の time がそのまま使える

time は HealthOmics 独自の属性ではなく、Nextflow が元々持っているプロセスディレクティブです。HealthOmics は Nextflow v23 以降のワークフローでこれをサポートしています。手元や他の実行環境向けに書いたワークフロー定義を HealthOmics へ移行する場合、すでに time が入っていても書き換えは不要です

config 側でもタイムアウトを設定できる

Nextflow のディレクティブはワークフロー定義だけでなく config ファイル側でも指定できます。ワークフロー定義そのものを編集しなくても、まとめてタイムアウトを設定できます。

time ディレクティブの仕様

最大実行時間は、mssmhd を組み合わせた文字列で指定します。'1h30m''3d5h4m' のように書きます。

公式ドキュメントでは、HealthOmics での time ディレクティブについて以下が挙げられています。

  • 粒度は 1 分単位で、指定できる範囲は 60 秒から run の最大実行時間まで
  • 60 秒未満の値は 60 秒に切り上げられ、60 秒を超える値は分単位で切り捨てられる
  • タイムアウトしたタスクのキャンセルには 1〜2 分かかることがある
  • run とタスクのステータスは failed になり、同じ run 内の他タスク(Starting、Pending、Running)もキャンセルされる
  • タイムアウト前に完了していたタスクの出力は S3 の出力先にエクスポートされる
  • pending 状態で待機していた時間は、タスクの経過時間に算入されない
  • run をまとめて制御する run グループのタイムアウトが先に来た場合は、run とタスクが failed になる

https://docs.aws.amazon.com/omics/latest/dev/workflow-definition-nextflow.html

run 全体の最大実行時間には Workflows - Maximum run duration サービスクォータがあり、既定値は 604,800 秒(7 日)です。time はこれよりも細かく、タスク単位で区切ります。

https://docs.aws.amazon.com/omics/latest/dev/service-quotas.html

WDL の omicsTimeout との違い

HealthOmics は WDL でも omicsTimeout 属性でタスクレベルのタイムアウトを指定できます。WDL への対応は Nextflow の 1 年後でした。目的は同じですが少し違いがあります。

観点 Nextflow の time WDL の omicsTimeout
出自 Nextflow 標準のディレクティブ HealthOmics 独自の属性
指定できる場所 ワークフロー定義の process ブロック、config ファイル タスクの runtime セクション
ドキュメントが挙げている単位 mssmhd smhd、および整数(秒)

単位は Nextflow 側にだけ ms が挙がっています。とはいえどちらも粒度は 1 分で、ミリ秒まで書いても 60 秒に切り上げられます。

https://dev.classmethod.jp/articles/aws-healthomics-wdl-task-level-timeout/

タイムアウトしたタスクはリトライされるのか

HealthOmics のタスクリトライには 2 種類あります。サービスエラー(5XX)で失敗したタスクを HealthOmics 側がリトライする仕組みと、Nextflow の errorStrategy で失敗したタスクをリトライする仕組みです。今回検証で扱うのは後者です。errorStrategyretry を設定するとリトライが 1 回行われ、回数を増やしたい場合は maxRetries を併用します。

リトライとタイムアウトの関係は、time ディレクティブの項で説明がありました。タイムアウトしたタスクはリトライの対象になり、キャンセルされるのは最後のリトライがタイムアウトしたときです。

If the workflow supports retries for a task, HealthOmics retries the task if it times out.

If a task times out (or the last retry times out), HealthOmics cancels the task. This operation can have a duration of one to two minutes.

出典: Nextflow workflow definition specifics

タイムアウトの確認ついでに、リトライの挙動も確認してみます。

検証環境

項目
リージョン ap-northeast-1(東京)
ワークフローエンジン Nextflow 26.04.0(DSL 2、strict 構文)
コンテナイメージ ubuntu 24.04(同一リージョンの ECR プライベートリポジトリ、amd64)
ストレージタイプ DYNAMIC
タスクリソース cpu 2、memory 4 GB
time タイムアウト版とリトライ版は '2m'、ベースラインは未指定

S3 出力先、ECR リポジトリ、HealthOmics サービスロールの準備と、Nextflow 26.04.0 を使うための版指定は、以下の記事と共通です。

https://dev.classmethod.jp/articles/aws-healthomics-nextflow-26-04-hello-world/

ワークフロー定義

比較の対象を time まわりだけに絞りたいので、3 本のワークフローを用意しました。コンテナ、cpu、memory、publishDir、スクリプトの中身はすべて同じにしています。

ベースラインは time を指定していない版です。sleep 600 するだけの単一プロセスで、開始時刻と終了時刻を result.txt に書き出します。publishDir に指定した /mnt/workflow/pubdir が S3 へのエクスポート先です。スクリプト内の \$ はシェル変数として扱わせるためのエスケープです。

baseline.nf
process SleepTask {
    container '<account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/<repository-name>:24.04'
    cpus 2
    memory '4 GB'

    publishDir '/mnt/workflow/pubdir', mode: 'copy'

    output:
    path 'result.txt'

    script:
    """
    START=\$(date -u +"%Y-%m-%dT%H:%M:%SZ")
    sleep 600
    echo "\$START" > result.txt
    date -u +"%Y-%m-%dT%H:%M:%SZ" >> result.txt
    """
}

workflow {
    SleepTask()
}

タイムアウト版は time '2m' の 1 行を足しただけです。sleep 600 は 2 分に対して十分に長いので、タイムアウトが確実に発生します。

timeout.nf(抜粋)
 process SleepTask {
     container '<account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/<repository-name>:24.04'
     cpus 2
     memory '4 GB'
+    time '2m'

リトライ版はさらに 2 行を足します。errorStrategy 'retry' だけでもリトライは 1 回なので、maxRetries 1 は合計で 2 回試行であることを明示するために書いています。

timeout-retry.nf(抜粋)
     cpus 2
     memory '4 GB'
     time '2m'
+    errorStrategy 'retry'
+    maxRetries 1

すべてに共通の nextflow.config はバージョン指定の 1 行だけです。

nextflow.config
manifest.nextflowVersion = '26.04.0'

ワークフローを登録して実行してみる

ワークフロー定義を main.nf として nextflow.config とともに zip にまとめ、--engine NEXTFLOW--main main.nf を指定して登録します。3 本とも同じ手順です。

cp baseline.nf main.nf
zip -j baseline.zip main.nf nextflow.config
aws omics create-workflow --name healthomics-nf-timeout-baseline --engine NEXTFLOW \
  --main main.nf --definition-zip fileb://baseline.zip --region ap-northeast-1

レスポンスの id が、この後の start-run で参照するワークフロー ID です。直後は CREATING で、今回は約 15 秒で 3 本とも ACTIVE になりました。

実行結果(抜粋)
{
    "id": "3134402",
    "status": "CREATING"
}

ACTIVE になったら run を開始します。--storage-type には DYNAMIC を指定します。以下はタイムアウト版(ワークフロー ID 2752491)の例です。ベースラインとは独立したワークフローなので、待ち時間を短縮するため並行して起動しました。

aws omics start-run \
  --workflow-id 2752491 \
  --role-arn arn:aws:iam::<account-id>:role/<service-role-name> \
  --output-uri s3://<output-bucket>/nf-timeout/ \
  --storage-type DYNAMIC \
  --name nf-timeout --region ap-northeast-1

レスポンスの id が run の追跡に使う ID です。

実行結果(抜粋)
{
    "id": "4039370",
    "status": "PENDING",
    "runOutputUri": "s3://<output-bucket>/nf-timeout/4039370"
}

実行中に get-run で確認したところ、3 run とも engineVersion26.04.0 でした。バージョン指定は問題ありませんでした。

タイムアウトの挙動を確認する

time なしなら sleep 600 は完走した

先にベースラインの結果です。ワークフロー ID 3134402 を同じ形式で起動し、run ID は 9341685 でした。タスクの stopTime から startTime を引くと 614.26 秒で、sleep 600 に対して約 14 秒のオーバーヘッドでした。

aws omics list-run-tasks --id 9341685 --region ap-northeast-1
実行結果(抜粋)
{
    "items": [
        {
            "taskId": "9824778",
            "status": "COMPLETED",
            "name": "SleepTask",
            "startTime": "2026-08-22T00:21:21.829000+00:00",
            "stopTime": "2026-08-22T00:31:36.089000+00:00"
        }
    ]
}

S3 に出力された result.txt には 00:21:21Z00:31:21Z が記録されており、sleep 600 はきっかり 600 秒動いていました。タスクの startTime とほぼ一致するので、差の約 14 秒はスクリプト終了後の後処理分です。

タイムアウト版は STOPPING を経て FAILED になった

タイムアウト版のステータスを 20 秒間隔でポーリングして見守りました。ステータスの遷移は PENDING から STARTINGRUNNINGSTOPPING を経て FAILED でした。

for i in $(seq 1 60); do
  echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") $(aws omics get-run --id 4039370 --region ap-northeast-1 --query 'status' --output text)"
  sleep 20
done
実行結果(抜粋、遷移箇所のみ)
2026-08-22T00:18:56Z PENDING
2026-08-22T00:19:17Z STARTING
2026-08-22T00:23:07Z RUNNING
2026-08-22T00:29:24Z STOPPING
2026-08-22T00:31:51Z FAILED

STOPPING はタイムアウトに固有の状態ではありません。ベースラインの run も RUNNING から STOPPING を経て COMPLETED になり、タスクの完了から run の完了まで約 2 分 20 秒かかりました。

run 単位の statusMessage には、どのタスクが何秒でタイムアウトしたかのメッセージがありました。

aws omics get-run --id 4039370 --region ap-northeast-1 \
  --query '{status:status,statusMessage:statusMessage,failureReason:failureReason,startTime:startTime,stopTime:stopTime}'
実行結果
{
    "status": "FAILED",
+   "statusMessage": "Task 4140624 timed out after 120 seconds.",
+   "failureReason": "TASK_TIMED_OUT",
    "startTime": "2026-08-22T00:22:56.861000+00:00",
    "stopTime": "2026-08-22T00:31:41.706190+00:00"
}

コンソールでも同じ内容を確認できました。実行の概要はステータスが「エラー」、エラーの理由が TASK_TIMED_OUT で、ステータスの説明には run 単位と同じ文言が入っていました。画面の下部にある実行タスクのタブには、失敗したタスクが 1 件だけ並んでいました。

Run_4039370___AWS_HealthOmics___ap-northeast-1_🔊.png

停止までに指定値より 1 分ほど長くかかった

停止までの時間は run 単位でなくタスク単位で測ります。run の startTimestopTime の差には、出力のエクスポートやリソースの解放など終了時の共通処理が混ざるためです。list-run-tasks でタスクの startTimestopTime を確認します。

aws omics list-run-tasks --id 4039370 --region ap-northeast-1
実行結果(抜粋)
{
    "items": [
        {
            "taskId": "4140624",
            "status": "FAILED",
            "name": "SleepTask",
            "startTime": "2026-08-22T00:25:13.974000+00:00",
            "stopTime": "2026-08-22T00:28:27.471000+00:00"
        }
    ]
}

00:28:27.47100:25:13.974 で 193.497 秒(約 3 分 13 秒)でした。指定した 120 秒に対して、停止処理のオーバーヘッドが 73.497 秒ありました。公式ドキュメントが「1〜2 分かかることがある」としている範囲に収まっています。

このオーバーヘッドは一定ではありませんでした。後述のリトライ版も含めた 3 回の実測では 54.309 秒、73.497 秒、74.000 秒とばらついていました。54.309 秒は公式の下限である 1 分を下回っています。指定値どおりに止まると考えず、1 分前後の余裕を見込んでおくのが安全です。

list-run-tasks のレスポンスに失敗理由は含まれません。個別タスクの statusMessagefailureReasonget-run-task で取得します。

aws omics get-run-task --id 4039370 --task-id 4140624 --region ap-northeast-1
実行結果(抜粋)
{
    "taskId": "4140624",
    "status": "FAILED",
    "startTime": "2026-08-22T00:25:13.974000+00:00",
    "stopTime": "2026-08-22T00:28:27.471000+00:00",
+   "statusMessage": "Task exceeded the time directive for the process after 120 seconds.",
+   "failureReason": "TASK_TIMED_OUT"
}

failureReasonTASK_TIMED_OUT になり、タイムアウトが原因だと判別できます。この値はコンソールの実行の概要にある「エラーの理由」と同じものです。

リトライの挙動を確認する

タイムアウトしたタスクもリトライされた

ここからがリトライ版(run ID 6540495)です。time '2m'errorStrategy 'retry'maxRetries 1 を加えています。リトライの有無は list-run-tasks に並ぶタスクの件数で判別できます。run が FAILED になってから取得しているので、これ以上タスクは増えません。

aws omics list-run-tasks --id 6540495 --region ap-northeast-1 \
  --query '{count:length(items),taskIds:items[].taskId,statuses:items[].status}'

タスクが 2 件並び、どちらも FAILED でした。タイムアウトしたタスクもリトライされています。

実行結果
{
+   "count": 2,
    "taskIds": ["9507045", "4728733"],
    "statuses": ["FAILED", "FAILED"]
}

各タスクの時刻と失敗理由を個別に確認します。試行 1 が 4728733、試行 2 が 9507045 です。

for t in 4728733 9507045; do
  aws omics get-run-task --id 6540495 --task-id $t --region ap-northeast-1 \
    --query '{taskId:taskId,status:status,creationTime:creationTime,startTime:startTime,stopTime:stopTime,statusMessage:statusMessage,failureReason:failureReason}'
done
実行結果
{
    "taskId": "4728733",
    "status": "FAILED",
    "creationTime": "2026-08-22T00:30:59.543755+00:00",
    "startTime": "2026-08-22T00:32:59.688000+00:00",
    "stopTime": "2026-08-22T00:35:53.997000+00:00",
+   "statusMessage": "Task exceeded the time directive for the process after 120 seconds.",
+   "failureReason": "TASK_TIMED_OUT"
}
{
    "taskId": "9507045",
    "status": "FAILED",
+   "creationTime": "2026-08-22T00:35:55.318012+00:00",
    "startTime": "2026-08-22T00:36:23.396000+00:00",
    "stopTime": "2026-08-22T00:39:37.396000+00:00",
+   "statusMessage": "Task exceeded the time directive for the process after 120 seconds.",
+   "failureReason": "TASK_TIMED_OUT"
}

2 試行とも TASK_TIMED_OUT で、文言も同じでした。試行 1 の stopTime から試行 2 の creationTime までは 1.321 秒で、キャンセルが終わるとほぼ間を置かずにリトライが投入されていました。

コンソールの実行タスクのタブにも、同じ名前の SleepTask が 2 件並んでいました。実行時間の列は dd:hh:mm:ss 形式で、試行 1(4728733)が 00:00:02:54、試行 2(9507045)が 00:00:03:14 です。キャプチャでは上の行が試行 2 になります。CLI で求めた 174.309 秒、194.000 秒と一致しました。

Run_6540495___AWS_HealthOmics___ap-northeast-1_🔊.png

リトライを挟むと合計時間はおよそ 2 倍になった

リトライなしの run も含めた 3 タスクの実測値です。

run タスク タスク所要時間 120 秒との差
4039370 タイムアウト版 4140624 193.497 秒 73.497 秒
6540495 リトライ版 試行 1 4728733 174.309 秒 54.309 秒
6540495 リトライ版 試行 2 9507045 194.000 秒 74.000 秒

リトライ版の 2 試行合計は 368.309 秒(約 6 分 8 秒)で、リトライなしの 193.497 秒のおよそ 2 倍でした。

リトライされたかどうかは run のステータスからは判別できなかった

run 単位の statusMessage はあてになりませんでした。リトライ版でも文言はタイムアウト版と同じ形で、名指ししているのは最後の試行ではなく最初の試行のタスク ID でした。

aws omics get-run --id 6540495 --region ap-northeast-1 \
  --query '{status:status,statusMessage:statusMessage,failureReason:failureReason}'
実行結果
{
    "status": "FAILED",
+   "statusMessage": "Task 4728733 timed out after 120 seconds.",
    "failureReason": "TASK_TIMED_OUT"
}

get-run の結果だけを追うと、リトライが起きたことを見落とします。判別には先ほどの list-run-tasks を使うか、run のマニフェストログ(ログストリーム run/{runID})で STARTING_TASK が何回出ているかを見ます。リトライ版では試行 1 の TASK_FAILED の後に、試行 2 の STARTING_TASK が続いていました。

aws logs get-log-events --log-group-name /aws/omics/WorkflowLog \
  --log-stream-name "run/6540495" --start-from-head --region ap-northeast-1 \
  --query 'events[].message' --output text
実行結果(抜粋)
{"logLevel":"INFO","runStatus":"RUNNING","logMessage":"RUNNING_TASK","message":"Running run task: name: SleepTask and taskId: 4728733"}
{"logLevel":"ERROR","runStatus":"RUNNING","logMessage":"TASK_FAILED","message":"Run task: name: SleepTask and taskId: 4728733 failed."}
+{"logLevel":"INFO","runStatus":"RUNNING","logMessage":"STARTING_TASK","message":"Starting run task with name: SleepTask and taskId: 9507045"}
+{"logLevel":"INFO","runStatus":"RUNNING","logMessage":"RUNNING_TASK","message":"Running run task: name: SleepTask and taskId: 9507045"}
{"logLevel":"ERROR","runStatus":"RUNNING","logMessage":"TASK_FAILED","message":"Run task: name: SleepTask and taskId: 9507045 failed."}

ログと出力を確認する

タイムアウトかどうかは CloudWatch Logs には出ていなかった

失敗理由を CloudWatch Logs から確認するつもりなら詰みます。タイムアウトしたタスクのログストリームには Task started の 1 行しか出ませんでした。なにか異常があるのか、まだ実行中なのかさっぱりわからない。

aws logs get-log-events --log-group-name /aws/omics/WorkflowLog \
  --log-stream-name "run/4039370/task/4140624" --start-from-head --region ap-northeast-1 \
  --query 'events[].message'
実行結果
[
    "Task started"
]

エンジンログにも、タイムアウトを示す語はありませんでした。終了ステータス 1 の汎用的なプロセス失敗として記録されていました。

aws logs get-log-events --log-group-name /aws/omics/WorkflowLog \
  --log-stream-name "run/4039370/engine" --start-from-head --region ap-northeast-1 \
  --query 'events[].message' --output text
実行結果(抜粋)
Aug-22 00:28:28.496 [TaskFinalizer-1] ERROR nextflow.processor.TaskProcessor - Error executing process > 'SleepTask'
Caused by:
Process `SleepTask` terminated with an error exit status (1)

エンジンログ全体を timed outtimeoutTASK_TIMED_OUTexceeded で検索しても該当する行はありませんでした。タイムアウトかどうかを知りたい場合は、ログではなく get-run-taskfailureReason かコンソールの実行の概要を見てください。

なおリトライ版(6540495)のエンジンログを同じコマンドで取得すると、リトライを示す行が出ていました。Execution is retried (1)attempt 2 の 2 行に注目します。attempt 1 はリトライを設定していないタイムアウト版の run にも出ていたので、実際のリトライを示すのはこの 2 行です。

実行結果(抜粋)
Aug-22 00:35:54.927 [TaskFinalizer-1] INFO  nextflow.processor.TaskProcessor - [1f/5be733] NOTE: Process `SleepTask` terminated with an error exit status (1) -- Execution is retried (1)
Aug-22 00:35:54.929 [pool-1-thread-2] INFO  nextflow.processor.TaskProcessor - [SleepTask] Task retrying, not resuming from cache - attempt 2

タイムアウトしたタスクの出力は S3 に残らなかった

タイムアウトした 2 つの run の出力先には logs/engine.log だけが残り、pubdir/ は作られませんでした。今回のタスクは sleep 600 の後に result.txt を書く作りなので、停止した時点でファイル作成できていないのは想定どうりです。

aws s3 ls s3://<output-bucket>/nf-timeout/ --recursive --region ap-northeast-1

完走したベースライン(9341685)だけが pubdir/result.txt が保存されていた。

実行結果
2026-08-22 09:18:35          0 nf-timeout/4039370/
2026-08-22 09:28:31     162058 nf-timeout/4039370/logs/engine.log
2026-08-22 09:30:16          0 nf-timeout/6540495/
2026-08-22 09:39:42     186121 nf-timeout/6540495/logs/engine.log
2026-08-22 09:18:34          0 nf-timeout/9341685/
2026-08-22 09:31:44     125020 nf-timeout/9341685/logs/engine.log
+2026-08-22 09:31:43         42 nf-timeout/9341685/pubdir/result.txt

まとめ

AWS HealthOmics の Nextflow ワークフローで time ディレクティブを設定すると、指定時間を超えたタスクが停止し、run が FAILED になることを確認しました。指定した 2 分を過ぎてから実際に止まるまでには、さらに 54〜74 秒かかりました。errorStrategy 'retry' を併用するとタイムアウトしたタスクもリトライされます。合計時間はリトライなしのおよそ 2 倍でした。リトライの有無は list-run-tasks のタスク件数、原因の判別は get-run-taskfailureReason で確認できました。

おわりに

今朝、WDL のタスクタイムアウトの記事を書いているときに Nextflow はどうなのと思って調べてみました。なんと 1 年前にサポートしたというアップデートがあり、把握できていなかったのでブログを書くがてら試してみました。すっかり夜になりました。

参考

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事