バッチジョブを実行する

DeviceServer で一連のタスクをスクリプトで実行するときに、ひとかたまりの機能をスクリプトに分けて、それらを順に実行させることができます。

例えば、I/O のデータを取得した後に、そのデータをデータベースに格納して、その後データベースの内容を集計した後、メールで送信するスクリプトを実現するときを想定してみます。全てのタスクを一つのスクリプトにすることもできますが、メンテナンスや作り易さ、再利用性を考慮して、このような場合には複数のスクリプトに分けて作成する方が好ましいです。この例の場合には、I/O データ取得のスクリプト、データベース格納のスクリプト、集計のスクリプトとメール送信のスクリプトに分けることができます。

このような複数に分けて作成したスクリプトを、普通に連続して実行する場合には script_exec() ライブラリ関数(スクリプト中から別スクリプトを呼び出してその終了を待つ)を順に呼び出すだけで簡単に実現できます。でも、例えば I/O装置のデータ取得部分で、データ取得対象のデバイスが多数の場合には、データ取得のスクリプトを並行して複数実行することで処理速度を向上できる場合があります。このような場合には DeviceServer 側で用意されている event_set2(), event_wait2() ライブラリ関数を利用できます。

たとえば、BATCH_JOB1 から BATCH_JOB2,BATCH_JOB3,BATCH_JOB4 の4つのジョブがある場合に、BATCH_JOB2 と BATCH_JOB3 が並行して処理できる場合には、下記の様なスクリプトで実行できます。

file_id = "BATCH_JOB"

--[[

バッチジョブ実行試験

                               +---> BATCH_JOB2 ----+
                               |                    |
                               |                    V
  BATCH_JOB ---> BATCH_JOB1 ---+                    +--> BATCH_JOB4 ---> BATCH_JOB (END)
                               |                    ^
                               |                    |
                               +---> BATCH_JOB3 ----+

]]

log_msg("start..",file_id)

-- 全ジョブを別スレッドで起動する
-- 各々のジョブスクリプトの先頭で、先行するジョブの完了を待つためにイベント待ちを行う
-- また、各々のジョブスクリプトの終了部分で、そのジョブが完了した事をイベントをセットすることで
-- イベント待ちのジョブスクリプトに伝える
if not script_fork_exec("BATCH_JOB1","MasterTaskID",g_taskid) then error() end
if not script_fork_exec("BATCH_JOB2","MasterTaskID",g_taskid) then error() end
if not script_fork_exec("BATCH_JOB3","MasterTaskID",g_taskid) then error() end
if not script_fork_exec("BATCH_JOB4","MasterTaskID",g_taskid) then error() end

-- 全てのジョブが終了するのを待つ
if not event_wait2(g_taskid .. "JOB1",g_taskid .. "JOB2",g_taskid .. "JOB3",g_taskid .. "JOB4",30000) then
	log_msg("event_wait2() failed",file_id)
	return
end

log_msg("all job completed",file_id)

上記のスクリプトは、バッチジョブ全体を管理するためのスクリプトで BATCH_JOBの名前で作成しておきます。各バッチジョブの処理の全体のフローはコメント部分を参照して下さい。最初に、BATCH_JOB1 が実行された後、BATCH_JOB2 と BATCH_JOB3 が並行して実行されます。その後、BATCH_JOB2 と BATCH_JOB3 の両方の処理が完了した後、BATCH_JOB4 が実行されます。全てのジョブが完了するとメインの BATCH_JOB の処理が終了します。

script_fork_exec() ライブラリ関数を使用して、全てのバッチジョブを別スレッドで起動します。ただし、各バッチジョブのスクリプトでは先行するジョブの終了を待つための文が記述されていますので、直ぐには各バッチジョブのメイン処理は開始されません。

上記のスクリプトで、各バッチジョブを起動する script_fork_exec() ライブラリ関数のスクリプトパラメータに g_taskid が渡されていますが、これはバッチジョブ全体処理を複数並行して実行できるように、ジョブの待ち合わせを行うためのイベント名に、メインのバッチジョブスクリプト(BATCH_JOB) にアサインされたタスク番号を付加して各々のイベントを区別できるようにするためです。

BATCH_JOB1 の内容は下記の様になっています。

file_id = "BATCH_JOB1"

log_msg("start..",file_id)

-- 時間がかかるダミータスク
wait_time(2000)

-- ジョブが終了した
if not event_set2(g_params["MasterTaskID"] .. "JOB1") then error() end

log_msg("completed",file_id)

このジョブでは先行するジョブがないので、直ぐにメイン処理を模した wait_time() を実行します。その後、自身のジョブが完了したことを知らせるために event_set2() ライブラリ関数を使用してイベントをセットします。

BATCH_JOB2 の内容は下記の様になっています。

file_id = "BATCH_JOB2"

-- 先行するジョブ(JOB1) の完了を待つ
if not event_wait2(g_params["MasterTaskID"] .. "JOB1",10000) then
	log_msg("event_wait2() failed",file_id)
	return
end

log_msg("start..",file_id)

-- 時間がかかるダミータスク
wait_time(2000)

-- ジョブが終了した
if not event_set2(g_params["MasterTaskID"] .. "JOB2") then error() end

log_msg("completed",file_id)

最初の部分で、先行する BATCH_JOB1 の終了を待っています。BATCH_JOB1 の完了を event_wait2() で受信したら、メイン処理を模した wait_time() を実行します。その後、自身のジョブが完了したことを知らせるために event_set2() ライブラリ関数を使用してイベントをセットします。

BATCH_JOB3 の内容は下記の様になっています。

file_id = "BATCH_JOB3"

-- 先行するジョブ(JOB1) の完了を待つ
if not event_wait2(g_params["MasterTaskID"] .. "JOB1",10000) then
	log_msg("event_wait2() failed",file_id)
	return
end

log_msg("start..",file_id)

-- 時間がかかるダミータスク
wait_time(2500)

-- ジョブが終了した
if not event_set2(g_params["MasterTaskID"] .. "JOB3") then error() end

log_msg("completed",file_id)

このスクリプトの内容は BATCH_JOB2 とほぼ同じです。ただし、wait_time() で待つ時間をすこしずらしてあります。また、最後にセットするイベント名も BATCH_JOB3 用のものを使用します。

最後に、BATCH_JOB4 の内容は下記の様になっています。

file_id = "BATCH_JOB4"

-- 先行するジョブ(JOB1と JOB2) の完了を待つ
if not event_wait2(g_params["MasterTaskID"] .. "JOB2",g_params["MasterTaskID"] .. "JOB3",10000) then
	log_msg("event_wait2() failed",file_id)
	return
end

log_msg("start..",file_id)

-- 時間がかかるダミータスク
wait_time(1000)

-- ジョブが終了した
if not event_set2(g_params["MasterTaskID"] .. "JOB4") then error() end

log_msg("completed",file_id)

先行するバッチジョブが2つありますので、全てのイベントがセットされるのを event_wait2() ライブラリ関数で待ち合わせします。最後に、自身のジョブが完了したことを知らせるために event_set2() ライブラリ関数を使用してイベントをセットします。DeviceServer にはこの他にも複数のイベントの何れかがセットされるまで待ち合わせるためのライブラリ関数 event_wait_either2() も用意されています。

メインのジョブスクリプト (BATCH_JOB) を実行させると、各々のバッチジョブが順番に実行されて、ログには下記の様なメッセージが記録されます。

各バッチジョブが順番に実行されているのを確認することができます。

それではまた。