概要
複数のWorkflowを決まった順番で実行する同期実行の方法について説明します。
WorkflowのAddOrder処理と、NoticeBoardの機能を使った連携方法です。
前提条件
- JS7 jobscheduler の環境準備、初期設定を済ませていること。
サンプルWorkflowを使ってざっと学習していることが望ましい。
まだの方はこちらから
JS7® JobScheduler 初期設定、サンプルWorkFlowの登録 - WorkFlowの作成、実行、ログ確認などの基本操作ができること。
こちらで簡単な操作説明をしています
JS7 JobScheduler 初めてのWorkFlow実行 - NoticeBoard の基本的な使い方を理解していること。
サンプルWorkflow中の処理や、こちらの記事を学習していると理解しやすいと思います。
JS7® JobScheduler 複数Workflowの同期実行 - 本記事はJobScheduler Ver. 2.4.1 で確認した内容です。
今後内容が変わる可能性があります。
はじめに
Workflow処理の利用に慣れると、Workflowの定義数が増えていきます。
さらに、これらのWorkflowを、決まった順序で実行したいと考えるようになると思います。
単一Workflowにすべての処理を詰め込めば、順次処理は可能です。
しかしこの手段では、Job数の増加とともに、全体の見通しが悪くなり、特定の処理だけ修正、実行することも困難です。
※Ver. 2.4より、開始/終了Job指定が可能だが、数が多いと扱いづらい。
特定Workflowから、他のWorkflow処理を実行する手段として、AddOrder処理があります。
しかし、こちらは非同期処理となります。
※呼び出したWorkflowの終了を待たずに、後続処理が進んでしまう。
そのため各Workflowが同時に実行されてしまう。
そこで本記事では、NoticeBoardを利用することで、呼び出したWorkflowの終了を待ってから、次の処理に進む手段をご紹介します。
この手段ならば、各Workflowを決まった順序で実行することができます。
もちろん各Workflowは、通常どおり個別に実行することも可能です。
設定
実際に簡単な設定を行ってみます。
以降は、Workflowを呼び出す側を親Workflow、呼び出される側(実際の処理を行う側)を子Workflowとします。
親Workflowから、2つの子Workflowを順次実行する設定を作成してみます。
NorticeBoard定義
まずは、2つの子Workflow用に、それぞれのNoticeBoardを作成します。
Practice - Controller - Notice Boards と展開し、新規にNotice Boardを作成します。
名前はnotice_sub_A
としてください。
設定 | 値 |
---|---|
Name | notice_sub_A |
Notice ID for posting order | replaceAll($js7OrderId, '^#[0-9]{4}-[0-9]{2}-[0-9]{2}#.*?-(.+?)!.+?$', '$1') |
Notice ID for expecting order | replaceAll($js7OrderId, '^#([0-9]{4})-([0-9]{2})-([0-9]{2})#\w(.+?)-.+?$', '$1$2$3$4') |
End of Life of Notice | 60 Seconds |
設定できましたら、Deployしてください。
全く同じ設定で、notice_sub_B
を作成、Deployしてください。(Copy、Pasteで可)
設定 | 値 |
---|---|
Name | notice_sub_B |
Notice ID for posting order | replaceAll($js7OrderId, '^#[0-9]{4}-[0-9]{2}-[0-9]{2}#.*?-(.+?)!.+?$', '$1') |
Notice ID for expecting order | replaceAll($js7OrderId, '^#([0-9]{4})-([0-9]{2})-([0-9]{2})#\w(.+?)-.+?$', '$1$2$3$4') |
End of Life of Notice | 60 Seconds |
子Workflowの定義
つぎに、呼び出される側の2つの子Workflowを作成します。
※今回はサンプルとして、簡単な処理で済ませます。
Practice - Controller - Workflows より、新規Workflow SyncExec_sub_A
を作成します。
適当なJobと、最後にPostNoticesを追加してください。
Jobの項目 | 値 |
---|---|
Name/Label | job |
Agent | primaryAgent |
JobClass | Shell |
Script | sleep 10 |
PostNoticesの項目 | 値 |
---|---|
Notice Board Names | notice_sub_A |
設定できましたら、Deployしてください。
同じく子WorkflowSyncExec_sub_B
を作成します。
こちらの PostNotices は、notice_sub_B
を指定します。
Jobの項目 | 値 |
---|---|
Name/Label | job |
Agent | primaryAgent |
JobClass | Shell |
Script | sleep 10 |
PostNoticesの項目 | 値 |
---|---|
Notice Board Names | notice_sub_B |
設定できましたら、Deployしてください。
NorticeBoardの動作確認
子WorkflowSyncExec_sub_A
を実行し、NorticeBoardnotice_sub_A
に通知が飛ぶことを確認します。
さらに、指定時間(60秒)経過後、自動的に通知が消えることを確認します。
※この時点では、ExpectNoticesの指定がないため、まだ連携処理はありません。
※Notice Id は、OrderId 値がそのまま入っていますが、この時点では問題ありません。
同様に、子WorkflowSyncExec_sub_B
、NorticeBoardnotice_sub_B
でも確認をしてください。
親Workflowの定義
最後に親Workflowの定義を行います。
Practice - Controller - Workflows より、新規Workflow SyncExec_main
を作成します。
まず、sub_A用の処理を定義します。
Job, AddOrder, ExpectNotices の処理を並べてください。
設定は下記のようにおこなってください。
Jobの項目 | 値 |
---|---|
Name/Label | start_sub_A |
Agent | primaryAgent |
JobClass | Shell |
Script |
echo "SyncExec_sub_A" ※処理は適当でよい |
AddOrderの項目 | 値 |
---|---|
Workflow Name | SyncExec_sub_A |
ExpectNoticesの項目 | 値 |
---|---|
Notice Board Names (ExpectNotices Expression) |
'notice_sub_A' |
sub_Aの後続に、sub_B用の処理を同じように追加します。
設定は下記のようにおこなってください。
Jobの項目 | 値 |
---|---|
Name/Label | start_sub_B |
Agent | primaryAgent |
JobClass | Shell |
Script |
echo "SyncExec_sub_B" ※処理は適当でよい |
AddOrderの項目 | 値 |
---|---|
Workflow Name | SyncExec_sub_B |
ExpectNoticesの項目 | 値 |
---|---|
Notice Board Names (ExpectNotices Expression) |
'notice_sub_B' |
終わりましたら、Deployしてください。
親Workflowの動作確認
親Workflow/Practice/SyncExec_main
を実行します。
ログの実行時間を見て、2つのWorkflowが順次実行されたことを確認してください。
時刻 | 動作 |
---|---|
14:22:51 | 親Workflow 開始 |
14:22:52 | 子Workflow SyncExec_sub_A 開始 |
14:23:02 | 子Workflow SyncExec_sub_A 終了 子Workflow SyncExec_sub_B 開始 |
14:23:12 | 子Workflow SyncExec_sub_B 終了 |
14:23:13 | 親Workflow 終了 |
NorticeBoardの動作確認
親Workflow/Practice/SyncExec_main
を実行します。
すると、まず親Workflowにて、子WorkflowSyncExec_sub_A
のOrder追加が行われます。
そのままsub_A用のExpectNotice処理が実行され、NoticeBoard notice_sub_A
では、IDの通知待ちとなります。
子Workflow SyncExec_sub_A
終了時に、PostNotice処理が実行され、NoticeBoard notice_sub_A
では、IDの通知が届きます。
また、sub_A用のExpectはIDの通知を受けたため、消えます。(後続の処理が開始されます)
さらに、後続のsub_B用のOrder追加、ExpectNotice処理が実行され、NoticeBoard notice_sub_B
では、IDの通知待ちとなります。
子Workflow SyncExec_sub_B
終了時に、PostNotice処理が実行され、NoticeBoard notice_sub_
では、IDの通知が届きます。
また、sub_B用のExpectはIDの通知を受けたため、消えます。(親Workflow終了)
End Of Life で設定した時間経過後、IDは自動的に削除されることを確認してください。
もしPost/Expect の連携がうまく動作しない場合は、このNotice Id が一致するように、NoticeBoardの設定を調整してください。
並列実行の場合
複数の子Workflowを並列実行する場合は、複数のAddOrderパーツを並べます。
非同期で処理が進むため、ほぼ同時並行で実行されます。
※AddOrderは、単一Orderしか対応できません
Workflow名: SyncExec_multi
Jobの項目 | 値 |
---|---|
Name/Label | start_sub |
Agent | primaryAgent |
JobClass | Shell |
Script |
echo "SyncExec_sub" ※処理は適当でよい |
AddOrderの項目 | 値 |
---|---|
Workflow Name | SyncExec_sub_A |
AddOrderの項目 | 値 |
---|---|
Workflow Name | SyncExec_sub_B |
ExpectNoticesの項目 | 値 |
---|---|
ExpectNotices Expression | 'notice_sub_A' && 'notice_sub_B' |
設定できましたら、Deployし、動作確認してみてください。
2つのWorkflowが同時並行で実行されたことが確認できます。
時刻 | 動作 |
---|---|
14:54:14 | 親Workflow 開始 |
14:54:15 | 子Workflow SyncExec_sub_A 開始 子Workflow SyncExec_sub_B 開始 |
14:54:25 | 子Workflow SyncExec_sub_A 終了 子Workflow SyncExec_sub_B 終了 親Workflow 終了 |
ExpectNotices では、下記のように AND, OR の条件指定が可能です。
-
'notice_sub_A' && 'notice_sub_B'
sub_A, sub_B 両方の通知を受けた場合 -
'notice_sub_A' || 'notice_sub_B'
sub_A, sub_B どちらかの通知を受けた場合 -
( 'notice_sub_A' && 'notice_sub_B' ) || 'notice_sub_C'
sub_A, sub_B の両方、または sub_C 通知を受けた場合
これにより、各Workflowがすべて終了した場合、一部のみ終了した場合などの指定が可能です。
※PostNoticeでも、複数NoticeBoardへの通知が可能
【参考】NoticeBoardでの厳密なID指定
本記事のように、AddOrder処理を行う場合、OrderIdは下記のようになります。
- 親WorkflowのOrderID :
#2022-11-09#T97195741002-root
※expectに利用 - 子WorkflowでのOrderID :
#2022-11-09#D97195781200-2022110997195741002!-root
※postに利用
※親OrderIdの日付、数値列が、子OrderIDの後ろについている。
そこで本記事では下記の設定でNotice Idを指定しています。
Notice ID for posting order : replaceAll($js7OrderId, '^#[0-9]{4}-[0-9]{2}-[0-9]{2}#.*?-(.+?)!.+?$', '$1')
Notice ID for expecting order : replaceAll($js7OrderId, '^#([0-9]{4})-([0-9]{2})-([0-9]{2})#\w(.+?)-.+?$', '$1$2$3$4')
- 親Workflowでは、ExpectNoticeで、OrderID より、
2022
,11
,09
,97195741002
を結合した2022110997195741002
を待つ - 子Workflowでは、PostNoticeで、OrderId の後部分の数値列
2022110997195741002
を通知する
この設定ならば、親WorkflowのOrderIDでつながるため、より確実かと思います。
本記事の例では、Idは適当な文字列(例えば 'id'
)で済ませ、End of Life of Notice を数秒にするのも有効です。
※PostNotice後、即ExpectNoticeで処理が動くため、短時間での同時実行がなければ、必ずしも厳密なIdである必要はありません。
なお、デフォルトで用意されている設定でNotice Idを指定することは、個人的にはお勧めしません。
-
Matching Daily Plan Dates
は、Idが日付となるため、複数のOrderでIdが重複する。1日に何度も実行する場合には不向き。 -
Matching Daily Plan Dates and Order Name
は、上記に加え、Order Name が指定しづらい。
OrderId中の日付は、手動OrderではUTC日付、Workflow中のAddOrderや、DailyPlanではJST日付(TimeZone設定による)となります。
そのため手動Orderを行う場合、実行時間帯によっては不一致となります。
【参考】親WorkflowにJobを追加する理由
親Workflowで、何故Jobstart_sub_A
, start_sub_B
を追加するのか疑問をもたれた方もいるかと思います。
1つ目の理由として、WorkflowにJobを1つも追加しないままでは、Deployがうまくできない問題があります。
そのため、適当なJobを1つ以上追加する必要があります。
2つ目の理由として、万が一途中の子Workflowがエラーとなり、途中から再実行したい場合、分かりやすいJob名があれば、多くの処理の中から、開始位置を容易に選ぶことができます。(運用上の利便性)
これらの理由で、子Workflow開始前に、それを示すJobを配置することをお勧めします。
【参考】Slack通知の簡略化
本記事の方法で複数のWorkflowを実行した場合、各子Workflowおよび親Workflowが終了するたび通知が飛びます。
この場合、例えば下記の設定で、正常終了時は、親Workflowの完了通知だけで済ませることができます。
子Workflowでは、Order変数で、is_notify (Boolean) = true
を定義
親Workflowでは、AddOrder時に、is_notify = false
を指定
Notification 中の 該当CommandFragment設定にて、最初に下記のような条件指定を追加
# Order変数 is_notify = false かつ正常終了時は通知しない
[ "$(echo '${MON_O_START_VARIABLES}' |jq -r .is_notify)" == "false" ] && [ ${MON_N_STATUS} == 0 ] && exit
Monitor変数${MON_O_START_VARIABLES}
では、Order追加時に変数を指定した場合に、json形式で変数値を取得できます。
これを用いて、Order変数 is_notify=false
かつ${MON_N_STATUS}=0
(正常終了)の場合は処理を抜けるように設定します。
※JOCにて、jqコマンドが使えることを確認してください。docker版では別途追加が必要です。
最後に
本記事の方法を用いることで、各Workflowを部品化でき、設定をすっきりさせることができます。
また、スケジュールによる各Workflowの順次実行と、状況に応じた個別実行の、両方に対応できます。
Workflowの利用が増えた際の整理手段として、参考にしていただければ幸いです。
参考
JS7 - AddOrder Instruction
JS7 - Notice Boards
JS7 - How to make a job wait for completion of a workflow