Skip to content

バックグラウンドサーバースクリプトの仕組み ​

第2版作成 最終更新 (日本時間)
確認バージョン1.5.8.1

バックグラウンドサーバースクリプト(テナント管理から登録し、スケジュールで動かすサーバースクリプト)は、プリザンターに組み込まれた Quartz.NET のスケジューラの上で動いています。スケジュールの画面設定は内部で Quartz の cron 式に変換され、発火すると HTTP リクエストの無い専用の Context でサーバースクリプトが実行されます。

使い方の例は 拡張サーバースクリプトとバックグラウンドサーバースクリプト を参照してください。

全体像 ​

図を読み込み中…

  • アプリケーションの起動時、ウォームアップの完了を待ってから、全テナントのスクリプトをスケジューラに登録し、スケジューラを開始します(CustomQuartzHostedService.cs#L33-L68、BackgroundServerScriptUtilities.cs#L31-L51)。
  • テナント管理で設定を更新すると、そのテナントのジョブ(グループ BGServerScript_TenantId_{テナントID})を全部消してから登録し直します(TenantModel.cs#L2259-L2264、BackgroundServerScriptUtilities.cs#L53-L85)。
  • 登録されるのは「無効」でなく、「共有」でなく、スケジュールが 1 つ以上あるスクリプトです。スクリプト 1 つが 1 ジョブ、スケジュール 1 つが 1 トリガーになります。
  • 次回の発火時刻が無いトリガー(過去の日時を指定した「一回のみ」など)は登録されません(BackgroundServerScriptUtilities.cs#L82)。エラーにもならないので、登録したのに動かないときは日時を確認してください。

有効にする条件 ​

スケジュールの登録とジョブの実行のたびに、次の 3 つがすべて満たされているかを確認します(BackgroundService.cs#L57-L67)。

条件パラメータ既定値
サーバースクリプトが有効Script.json の ServerScripttrue
バックグラウンドサーバースクリプトが有効Script.json の BackgroundServerScriptfalse
実行環境が一致するBackgroundService.json の EnvironmentVariables と Service.json の DeploymentEnvironmentEnvironmentVariables が null(制限なし)

EnvironmentVariables を使うと、複数台構成のうち特定の環境だけでバックグラウンド処理を動かせます。

スケジューラの構成 ​

Quartz.json の Clustering.Enabled で、スケジューラの作り方が変わります(CustomQuartzHostedService.cs#L20-L31、#L101-L152)。

項目クラスタリングなし(既定)クラスタリングあり
スケジューラStdSchedulerFactory.GetDefaultScheduler()設定値から作成
ジョブストアQuartz 既定(メモリ上)AdoJobStore(プリザンターの DB に QRTZ_ 始まりのテーブル)
同時実行数Quartz 既定MaxConcurrency(既定 10)
チェックイン間隔なしCheckinInterval(既定 15000 ミリ秒)
ミスファイア閾値なしMaxMisfireThreshold(既定 60000 ミリ秒)

ジョブの基底クラス ClusterExecutionTimerBase には [DisallowConcurrentExecution] が付いているため、同じジョブ(同じスクリプト)が前回の実行中に重なって動くことはありません(ClusterExecutionTimerBase.cs#L5)。1.5.8.1 は Quartz.AspNetCore 3.19.1 を参照しています(Implem.Pleasanter.csproj#L80)。

スケジュールと cron 式 ​

スケジュールの画面設定(BackgroundSchedule)は、GetTrigger() で Quartz の 7 フィールドの cron 式(秒 分 時 日 月 曜日 年)に変換されます(BackgroundServerScriptUtilities.cs#L112-L179)。

種別(ScheduleType)画面の入力生成される cron 式例
hourly(毎時)分(00〜55 の 5 分刻みのドロップダウン)0 {分} * * * ? *毎時 30 分 → 0 30 * * * ? *
daily(毎日)時刻(HH:mm)0 {分} {時} * * ? *9:00 → 0 0 9 * * ? *
weekly(毎週)曜日(複数選択)と時刻0 {分} {時} ? * {曜日} *月水金 9:00 → 0 0 9 ? * 2,4,6 *
monthly(毎月)月(複数)・日(複数)と時刻0 {分} {時} {日} {月} ? *毎月 1 日と 15 日 9:00 → 0 0 9 1,15 * ? *
onlyonce(一回のみ)日時0 {分} {時} {日} {月} ? {年}2026/3/15 9:00 → 0 0 9 15 3 ? 2026
  • 曜日の値は Quartz と同じく 1 が日曜、7 が土曜です(TenantUtilities.cs#L2598-L2610)。
  • 日の選択肢の「月末」は値が 32 で、cron 式では L になります(TenantUtilities.cs#L2672、BackgroundServerScriptUtilities.cs#L196-L202)。
  • タイムゾーンは、スケジュールのタイムゾーン → Service.json の TimeZoneDefault → UTC の順に決まります。
  • 保存時のサーバー側の検証は、毎日・毎週・毎月の時刻が HH:mm 形式かどうかだけです。曜日・月・日の値は検証されません(BackgroundScheduleValidators.cs#L22-L39)。値が解釈できないスケジュールはトリガーが作られず、黙って登録されません。

このため、画面からは「15 分ごと」「平日の 9〜17 時だけ」「第 2 火曜日」のような指定はできません。毎時の分も 5 分刻みで、複数の時刻に動かすにはスケジュールを複数登録します。cron 式を直接入力できるようにする改修案は バックグラウンドサーバースクリプトに cron 式を直接指定する にまとめています。

実行の流れ ​

図を読み込み中…

(BackgroundServerScriptJob.cs#L20-L91)

  • 実行するスクリプトは、同じテナントの「共有」のバックグラウンドサーバースクリプト全部と、ジョブの対象スクリプト 1 つです。共有スクリプトはスケジュールでは動かず、他のスクリプトに結合される部品として使われます。
  • スケジュール実行ではスクリプトを毎回 DB(Tenants.TenantSettings)から読み直します。
  • ログは SysLogs に Exec BGServerScript TenantId=…,ScriptId=…,ScheduleId=…,UserId=…(実行時)または Skip BGServerScript …(対象が無い・実行ユーザーが使えないとき)として記録され、処理の終わりに Finish されます。例外も SysLogs に記録されます。
  • タイムアウトは通常のサーバースクリプトと同じく、スクリプトの TimeOut と Script.json の ServerScriptTimeOut から決まります(タイムアウト)。

即時実行 ​

スクリプトの編集ダイアログから即時実行すると、保存前のダイアログの内容をジョブのデータとして渡し、StartNow() のトリガーで 1 回だけ実行します(TenantModel.cs#L1941-L1964、BackgroundServerScriptUtilities.cs#L87-L110)。実行ユーザーが未入力のとき、「共有」にチェックがあるときは実行されません。スクリプトの「デバッグ」指定が効くのはこの即時実行のときだけで、スケジュール実行では常にデバッグなしで動きます(BackgroundServerScriptJob.cs#L77)。

実行時の Context ​

HTTP リクエストが無いため、テナント ID・実行ユーザーの ID・そのユーザーの部署 ID から Context を作ります(request: false、setAuthenticated: true)。context.BackgroundServerScript が true になり、AbsoluteUri には Service.json の AbsoluteUri が入り、タイムゾーンは実行ユーザーの設定になります(BackgroundServerScriptJob.cs#L110-L133)。

スクリプトの中で items.Get などを呼ぶときの内部の Context も、API リクエストの本文をマージせず、同じユーザー・部署・テナントから作り直し、タイムゾーンを引き継いで権限を設定し直します(ServerScriptUtilities.cs#L1401-L1435)。つまり、スクリプトは実行ユーザーの権限で動きます。

安全のための制御 ​

制御内容
登録できる人テナント管理の画面から登録する
実行ユーザーの確認実行ユーザーがそのテナントに存在し、無効化されていないこと。保存時と実行時の両方で確認する(BackgroundServerScriptValidators.cs#L29-L47)
機能ごと無効化Script.json の BackgroundServerScript(既定 false)
httpClient の無効化Script.json の DisableServerScriptHttpClient が true なら httpClient 自体を渡さない(ServerScriptUtilities.cs#L1240-L1243)
$ps.file の無効化Script.json の DisableServerScriptFile(既定 true)
多重実行の防止[DisallowConcurrentExecution]

httpClient には接続先の制限(localhost や社内ネットワーク宛てを拒否するなど)はありません(ServerScriptModelHttpClient.cs#L84)。バックグラウンドサーバースクリプトから社内のサービスにも到達できるので、必要ならネットワーク側で制限してください。

C# のプログラムを定期実行したいとき ​

バックグラウンドサーバースクリプトで動かせるのは JavaScript だけです。C# で書いた処理を定期実行したい場合、本体を改修しない方法は、処理を別プロセスの Web API として立て、バックグラウンドサーバースクリプトの httpClient から呼ぶ構成です。本体に DLL を読み込ませる改修案との比較は バックグラウンドで C# の DLL を実行する にまとめています。

関連ページ ​

変更履歴

第2版画面項目の連携・送信と非同期処理の解説を拡充し検索向け情報を整備
第1版バックグラウンドサーバースクリプトの仕組みと、サーバースクリプトの他言語対応・DLL 実行・cron スケジュールの改修・設計メモを追加