繰り返し
Queuetyワークフローは順序付けられたステップチェーンです。_next_stepのロジックを手書きする代わりに、繰り返し制御ステップを使って、以前の名前付きステップを再実行できます。
ワークフローに合った形式を選びます。
- 条件が単純な場合は、公開
state_key - 繰り返しに高度なロジックが必要な場合は、
condition_class - 繰り返し自体に厳格な停止条件が必要な場合は、
max_iterations
どちらのメソッドも以前の名前付きステップを対象にするため、繰り返しはシリアライズ可能で再実行でき、他のすべてのステップと同じワークフローランタイムの保護下にあります。
繰り返し条件は、前のステップ完了後に保存済みの公開ワークフロー状態から評価されるため、ワーカーの再起動をまたいでも各反復が確実に維持されます。
状態キーを使ったrepeat_until()
Queuety::workflow( 'approval_revision_loop' )
->max_transitions( 12 )
->then( DraftBriefStep::class, 'draft' )
->wait_for_decision( result_key: 'review' )
->then( NormalizeReviewOutcomeStep::class, 'review_outcome' )
->repeat_until( 'draft', 'review_approved', true, 'repeat_draft_until_approved' )
->then( PublishBriefStep::class )
->dispatch( [ 'brief_id' => 42 ] );
NormalizeReviewOutcomeStepは判断結果を単純な公開キーにまとめられます。
class NormalizeReviewOutcomeStep implements \Queuety\Step {
public function handle( array $state ): array {
return [
'review_approved' => 'approved' === ( $state['review']['outcome'] ?? null ),
];
}
public function config(): array {
return [];
}
}
review_approvedがまだtrueでない場合、繰り返しステップはdraftに戻ります。trueになると、ワークフローはPublishBriefStepへ進みます。
シグナルは保存され、消費されません。wait_for_decision()を2回目に通るときも、以前のrejectedシグナルが一致するため、待機はすぐに完了し、max_transitions()が停止させるまでループが回り続けます。各ラウンドに固有のキーを割り当てます。DraftBriefStepにインクリメントするreview_roundを返させ、correlation_key: 'review_round'をwait_for_decision()に渡し、レビュー担当者にシグナルペイロードで同じreview_roundを送ってもらいます。これにより、review_roundが現在の状態値と一致するシグナルだけが待機を完了させます。
条件クラスを使ったrepeat_until()
use App\Workflows\ReviewApprovedCondition;
Queuety::workflow( 'approval_revision_loop' )
->then( DraftBriefStep::class, 'draft' )
->wait_for_decision( result_key: 'review' )
->repeat_until(
target_step: 'draft',
condition_class: ReviewApprovedCondition::class,
max_iterations: 5,
name: 'repeat_draft_until_approved',
)
->then( PublishBriefStep::class )
->dispatch( [ 'brief_id' => 42 ] );
namespace App\Workflows;
use Queuety\Contracts\RepeatCondition;
final class ReviewApprovedCondition implements RepeatCondition {
public function matches( array $state ): bool {
return 'approved' === ( $state['review']['outcome'] ?? null );
}
}
条件クラスは公開ワークフロー状態を受け取るため、クロージャに依存せず、述語をシリアライズ可能で検査しやすい状態に保てます。
repeat_while()
Queuety::workflow( 'poll_remote_status' )
->max_transitions( 20 )
->then( PollRemoteStatusStep::class, 'poll' )
->delay( seconds: 30 )
->repeat_while( 'poll', 'should_poll_again', true, 'repeat_poll' )
->then( FinalizeImportStep::class )
->dispatch( [ 'import_id' => 99 ] );
このパターンは、ステップがshould_poll_again、has_more_pages、needs_revisionのような単純な真偽値を書き込む場合に便利です。
繰り返しを_next_stepと分ける理由
通常のステップから_next_stepを返せば、引き続き手動で分岐できます。繰り返し用ヘルパーは、次のような限定的なケースに対応します。
- 遷移先が前のステップである
- 条件が公開ワークフロー状態に基づく
- ワークフロー定義自体で繰り返しロジックを確認できるようにしたい
こうすることで、任意のステップコード内に戻り遷移を埋め込むよりも、エクスポート、イベントログ、ドキュメントで繰り返し処理を確認しやすくなります。
繰り返しのガード
特定の繰り返しにローカルなハードストップを設けるには、max_iterationsを使います。
Queuety::workflow( 'poll_remote_status' )
->then( PollRemoteStatusStep::class, 'poll' )
->repeat_while( 'poll', 'should_poll_again', true, max_iterations: 8 )
->dispatch();
ワークフロー全体に幅広いデッドマンスイッチを設けるには、max_transitions()を使います。
Queuety::workflow( 'safe_revision_loop' )
->max_transitions( 10 )
->then( DraftBriefStep::class, 'draft' )
->wait_for_decision( result_key: 'review' )
->then( NormalizeReviewOutcomeStep::class )
->repeat_until( 'draft', 'review_approved', true )
->dispatch();
繰り返しがmax_iterationsを超えるか、ワークフローがmax_transitions()を超えると、Queuetyは無限に実行され続ける代わりに実行を失敗させます。