OutSystemsプラットフォームのタイマーと非同期プロセス
タイマーは、スケジュール設定された時刻に定期的にアプリケーションロジックを実行できるようにするOutSystemsのツールです。こうした処理は、バッチジョブとも呼ばれます。
異なるタイマーを同時に実行できますが、同じタイマーが同時に複数の実行を処理することはありません。
以下に、タイマーを使用できる一般的なシナリオをいくつか示します。
シナリオ | 例 |
---|---|
スケジュール設定されたジョブ | 同じジョブを毎日同じ時刻に実行します。たとえば、毎日午前4時にダイジェストニュースの購読者にメールを送信します。 毎日午前4時に購読者にメールを送信するジョブを実行するタイマーを作成します。 |
長時間実行アクションの実行 | 完了するまでに時間がかかることが多いアプリケーションロジックを実行します。たとえば、毎月1日の午前2時に、システムで大量のデータベースレコードをアーカイブする必要があります。所要時間は約2時間です。 レコードをアーカイブするアクションを実行するタイマーを作成し、毎月1日の午前2時に実行するように設定します。デフォルトのタイムアウトは150分です(この値はService Centerで調整できます)。 |
アーキテクチャ
以下の表は、タイマーに関連するOutSystemsの要素の一覧です。
要素 | 説明 |
---|---|
OutSystems Scheduler Service | これは、実行するタイマーがあるかどうかを確認するサービスです。異なるタイマーを同時に実行できるようにするマルチスレッドサービスでもあります。 |
ランタイムデータベース | ランタイムデータベースには、タイマーを管理するための以下のようなシステムエンティティがすべて含まれています。 - すべての既存のタイマーのレコード - タイマーを実行するスケジュール - タイマーの実行状況: 開始タイミング、タイムアウト、次回の実行 |
ログデータベース | タイマーが実行されると、ログデータベースにエントリが作成されます。 |
Configuration Tool | これは、各フロントエンドサーバーノードで同時に実行できるタイマーの最大数を構成するためのツールです。 |
OutSystems Log Service | Service Centerでは、タイマーに関する情報にアクセスできます。Log Serviceによってこうした情報がログデータベースに書き込まれます。 |
デプロイされたモジュール(アプリケーション) | デプロイされたアプリケーションには、タイマーのコードが含まれています。タイマーによって実行されるアクションに組み込まれたアプリケーションロジックに加え、タイマーの状態に関してシステムデータベースを更新するために必要なスタブコードも含まれています。 |
ランタイムデータベースとログデータベース
このセクションでは、タイマーとその機能をサポートするデータベースエンティティについて説明します。
Meta_Cyclic_Jobエンティティ
このエンティティにはモジュールで作成されたタイマーの定義が含まれ、ossys_Meta_Cyclic_Jobデータベーステーブルのレコードに保存されます。これらのレコードはOutSystemsによって作成、管理され、ユーザーが変更できるアトリビュートはEffective_Timeoutのみです。
アトリビュートの詳細な説明を以下に示します。
アトリビュート | 説明 |
---|---|
Id | 主キー。 |
Espace_Id | タイマーが定義されているモジュール。 |
Name | タイマーの名前。モジュールで定義されています。 |
Default_Schedule | タイマーのデフォルトのスケジュール設定された時刻。モジュールで定義されています。 これは、タイマーが実行される時刻と頻度を含む文字列です。Service Studioには、その値を設定するための特別なエディタがあります。 この値は、Cyclic_Job_SharedエンティティのScheduleプロパティを設定するために使用されます。 |
Priority | タイマーの優先度。モジュールで定義されています。1(最高)~4(最低)の値です。 同時に実行されるタイマーの数がフロントエンドサーバーに許可された最大数よりも多い場合、優先度の最も高いタイマーが最初に実行されます。 |
Is_Active | タイマーがアクティブかどうかを示します。 |
SS_Key | Service Studioのタイマーキー。 |
Timeout | タイマーのデフォルトのタイムアウト。モジュールで定義されています。値は分単位です。 この制限時間内にタイマーがジョブを完了しない場合、タイマーの実行はシステムによって中止されます。 |
Effective_Timeout | 値が0以外の場合、その値で上記のタイマーのデフォルトのタイムアウトが上書きされます。 新しいタイムアウトを調整するために使用します。この操作はService Centerのタイマーの詳細ページで実行します。 |
IsShared | Trueの場合はタイマーのインスタンスがCyclic_Job_Sharedエンティティで作成され、True以外の場合はCyclic_Jobエンティティで作成されます。 |
Cyclic_Jobエンティティ
このエンティティには、マルチテナントモジュールでScheduler Serviceによって実行されるすべてのタイマーに関する情報が含まれています。この情報は、Service Centerで参照できます(「タイマーの実行を監視する」をご覧ください)。
複数のフロントエンドサーバーノードがタイマーを実行し、それぞれ独自の時刻が設定されている場合があるため、タイマーを実行するための時間の基準は常にデータベースクロックです。
このエンティティのレコードは、データベースのossys_Cyclic_Jobテーブルに保存されます。これらのレコードは、OutSystemsによって作成、管理されます。
このエンティティでユーザーが変更できるアトリビュートは、ScheduleとNext_Runのみです。ただし、タイマーの実行中にこれらのアトリビュートを設定しないでください。タイマーが実行されているかどうかは、Is_Running_Sinceアトリビュートの値を調べることで確認できます。
-
設定されている場合: タイマーが実行されています。
-
設定されていない場合: タイマーが実行されていません。
アトリビュートの詳細な説明を以下に示します。
アトリビュート | 説明 |
---|---|
Meta_Cyclic_Job_Id | Meta_Cyclic_Jobエンティティへの参照。 |
Tenant_Id | タイマーが実行されているテナントへの参照。 |
Schedule | タイマーの実行が完了した後、このアトリビュートを使用して次回の実行時刻が計算されます。 例: Schedule: 「02:00 10:00 18:00」(毎日午前2時、午前10時、午後6時に実行)。タイマーが2012年10月23日の18時00分50秒に完了した場合、Next_Runは2012年10月24日の2時00分00秒に設定されます。 このプロパティは、Meta_Cyclic_JobエンティティのDefault_Scheduleプロパティで定義された値によって初期設定されますが、Service Centerで変更できます。 |
Last_Run | タイマーが最後の実行を開始した日時(データベースクロックを使用)。 |
Next_Run | 次回の実行日時(データベースクロックを使用)。 Scheduler Serviceは、Next_Runが現在の日時以降になっているタイマーを実行します。 |
Last_Duration | タイマーが最後の実行を完了するまでにかかった時間(秒単位)。 |
Is_Running_Since | Schedule Serviceがタイマーの実行を開始した日時(データベースクロックの時刻)。 このアトリビュートが設定されたタイマーはSchedule Serviceで実行されないため(エラーからの復旧アクションを除く)、複数のフロントエンドサーバーノードを同期する際にもこのアトリビュートが使用されます。 タイマーの実行が完了すると、このアトリビュートは消去されます。 |
Is_Running_By | このアトリビュートには、タイマーを実行しているフロントエンドサーバーノードの名前が含まれています。また、Is_Running_Sinceアトリビュートも設定されます。 |
Number_Of_Tries | タイマーの実行が連続して失敗した回数。 |
Cyclic_Job_Sharedエンティティ
このエンティティには、シングルテナントモジュールでScheduler Serviceによって実行されるタイマーに関する情報が含まれています。この情報は、Service Centerで参照できます(「タイマーの実行を監視する」をご覧ください)。
複数のフロントエンドサーバーノードがタイマーを実行し、それぞれ独自の時刻が設定されている場合があるため、タイマーを実行するための時間の基準は常にデータベースクロックです。
このエンティティのレコードは、データベースのossys_Cyclic_Job_Sharedテーブルに保存されます。これらのレコードは、OutSystemsによって作成、管理されます。
このエンティティでユーザーが変更できるアトリビュートは、ScheduleとNext_Runのみです。ただし、タイマーの実行中にこれらのアトリビュートを設定しないでください。タイマーが実行されているかどうかは、Is_Running_Sinceアトリビュートの値を調べることで確認できます。
-
設定されている場合: タイマーが実行されています。
-
設定されていない場合: タイマーが実行されていません。
アトリビュートの詳細な説明を以下に示します。
アトリビュート | 説明 |
---|---|
Meta_Cyclic_Job_Id | Meta_Cyclic_Jobエンティティへの参照。 |
Schedule | タイマーの実行が完了した後、このアトリビュートを使用して次回の実行時刻が計算されます。 例: Schedule: 「02:00 10:00 18:00」(毎日午前2時、午前10時、午後6時に実行)。タイマーが2012年10月23日の18時00分50秒に完了した場合、Next_Runは2012年10月24日の2時00分00秒に設定されます。 このプロパティは、Meta_Cyclic_JobエンティティのDefault_Scheduleプロパティで定義された値によって初期設定されますが、Service Centerで変更できます。 |
Last_Run | タイマーが最後の実行を開始した日時。 |
Next_Run | 次回の実行日時。 Scheduler Serviceは、Next_Runが現在の日時以降になっているタイマーを実行します。 |
Last_Duration | タイマーが最後の実行を完了するまでにかかった時間(秒単位)。 |
Is_Running_Since | Schedule Serviceがタイマーの実行を開始した日時(データベースクロックの時刻)。 このアトリビュートが設定されたタイマーはSchedule Serviceで実行されないため(エラーからの復旧アクションを除く)、複数のフロントエンドサーバーノードを同期する際にもこのアトリビュートが使用されます。 タイマーの実行が完了すると、このアトリビュートは消去されます。 |
Is_Running_By | このアトリビュートには、タイマーを実行しているフロントエンドサーバーノードの名前が含まれています。また、Is_Running_Sinceアトリビュートも設定されます。 |
Number_Of_Tries | タイマーの実行が連続して失敗した回数。 |
oslog_Cyclic_Job_< N >テーブル
これらのテーブルには、実行されたタイマーに関するすべてのログ情報が保存されます。この情報は、Service Centerで参照できます(「タイマーのログを確認する」をご覧ください)。
アトリビュートの詳細な説明を以下に示します。
アトリビュート | 説明 |
---|---|
Instant | タイマーの実行が開始された日時(Service Centerの「Time of Log」)。 これはIISクロックの日時です。 |
Duration | タイマーが実行を完了するまでにかかった時間(秒単位)。 |
Cyclic_Job_Key | Service Studioのタイマーキー。 |
Espace_Id | タイマーが作成されたモジュールへの参照。 |
Tenant_Id | タイマーが実行されたテナントへの参照。 |
Executed_By | タイマーを実行したフロントエンドサーバーノードの名前(Service Centerの「Server」)。 |
Error_Id | [oslog_]Error_< N >エンティティへの参照。 設定されている場合、タイマーのアプリケーションロジックの実行中にエラーが発生したことを意味し、Service Centerの[Timers Log]ページでタイマーの行に[Error]という赤いリンクが表示されます。 |
Should_Have_Run_At | このタイマーが実行される予定だった日時。 システムの負荷が非常に高い場合、この値はInstantアトリビュートとはまったく異なる可能性があります。このような状態になる理由は、同じモジュールに多くのテナントがあり、すべて同じスケジュールになっていること、他のタイマーの実行に時間がかかりすぎていることなどが考えられます。 これはデータベースクロックの日時です。 |
Next_Run | このタイマーを(前述のようにスケジュールに基づいて)再び開始するタイミングを表す日時です。 |
Cycle | 内部使用のサイクル番号を記録します。 |
タイマーの実行方法
すべてのタイマーは、Scheduler Serviceによって実行されます。このサービスはマルチスレッド化されているため、異なるタイマーを同時に実行できます。わかりやすくするために、以下の手順では単一のタイマーの実行について説明しています。
タイマーを実行する手順は、以下のとおりです。
-
実行するタイマーをScheduler Serviceがデータベースから周期的に取得します(Next_Run)。
-
Scheduler Serviceがスレッドを起動し、タイマーを実行します。タイマーでは、タイマーの実行が定義されたモジュール内のWebサービスが呼び出されます。
-
Webサービスが最初に、フロントエンドサーバーノードでタイマーがすでに実行されているかどうかを確認します。実行されていない場合は、Cyclic_Job_SharedエンティティのIs_Runnining_SinceアトリビュートとIs_Runnining_Byアトリビュートが更新されます。これにより、他のフロントエンドサーバーノードでタイマーが実行されなくなります。
-
モジュールがタイマーに関連付けられたアクションを実行します。
-
アクションの実行が完了すると、Is_Runnining_SinceアトリビュートとIs_Runnining_Byアトリビュートが消去され、新しい実行に備えてタイマーが解放されます。Next_Runアトリビュートは、Scheduleアトリビュートと現在の日時に基づいて再計算されます。
Next_Runアトリビュートは、アクションの実行中に変更されなかった場合にのみ更新されます。このようになる可能性があるのは、たとえば、アクション自体がこのアトリビュートを更新した場合などです。
-
タイマーの実行に関する情報を含むレコードがLog Serviceに送信され、ログデータベースに保存されます。
-
Webサービスが制御をScheduler Serviceに戻します。これにより、Scheduler Serviceが別のタイマーを実行できるようになります(手順1)。
タイマーがタイムアウトする理由
前述のように、Scheduler Serviceは、タイマーが作成されたモジュールに作成されたWebサービスを呼び出してタイマーを実行します。つまり、タイマーの実行にはWebリクエストが関連し、他のWebリクエストと同様に、サーバーで実行できる最大時間があります。そのため、タイマーにはService Studioの「Timeout in Minutes」プロパティがあります。デフォルトでは、20分に設定されていますが、個別のケースに合わせて調整できます。
予期しないエラーが発生した場合のタイムアウトの役割
予期しないエラーによってタイマーの実行が終了した場合、Scheduler Serviceはタイムアウト時間に20%を加えた時間が経過してからタイマーを復旧してデータベースを同期します。それまでは、データベース内のタイマーに関するデータに不整合が発生している可能性があります。
タイマーの実行が予期せずに終了した場合にデータの整合性を確実に維持するために、タイマーアクションにログインを実装することも重要です。
実行エラーが発生した後に再試行する
タイマーで実行エラーが発生するたびに、OutSystemsは再試行回数に従ってタイマーを再実行する場合があります。この再試行回数は、Service Centerの[Environment Configuration]画面のAdministrationフォルダで設定できます。デフォルトでは、再試行回数は3回に設定されています。
タイマーのスケジュールを動的に変更する
タイマーの定期的な実行や固定的な実行を設定できるだけではなく、任意のロジックに基づいてタイマーの実行を動的にスケジュール設定することもできます。そのためには、タイマーアクションを編集し、新しい実行日時を計算して、タイマーの実行情報を保持するエンティティでその日時を更新します。
動的な実行を設定するために必要なすべてのエンティティにアクセスするには、以下の手順を実行します。
-
タイマーアクションを含むモジュールで、以下のシステムエンティティへの参照を追加します。
- Meta_Cyclic_Job
- Cyclic_Job_Shared
-
次回の実行日時を計算します。
-
Cyclic_Job_SharedのNext_Runアトリビュートを、手順2で計算した値で更新します。
例:
定期的にデータベースのデータをクリーンアップするCleanUpタイマーがあるとします。このタイマーは、最後の実行が終了してから2時間後に実行します。
タイマーのアクションを開き、フローの最後に以下のロジックを追加します。
-
タイマーのCyclic_Job_Sharedレコードを取得するためのクエリを作成します。このクエリには、モジュール識別子(eSpaceId)、タイマー名、入力パラメータを含めます。
-
クエリパラメータに以下の値を設定します。
- eSpaceId: GetOwnerEspaceIdentifier()
- TimerName: "CleanUp"
-
次回の実行日時を今から2時間後になるように計算し、Cyclic_Job_SharedレコードのNext_Runアトリビュートに設定します。これにはAddHours(CurrDateTime(),2)を使用します。
-
データベースのCyclic_Job_Sharedレコードを更新します。
-
トランザクションをコミットして、更新されたレコードのロックを解除します。
アクションフローは以下のように終了します。
Service Centerでタイマーを管理する
Service Centerでは、タイマーを管理できるように、以下のような一連の機能が提供されています。
-
タイマーの実行監視
-
タイマー設定の編集
-
タイマー実行の強制
-
タイマーの非アクティベート/アクティベート
-
以前の実行のログの参照
タイマーの実行を監視する
Service Centerでは、[Monitoring]の[Environment Health]オプションでタイマーの実行を監視できます。このページには、[Timers]セクションがあり、タイマーの実行順序を示すリストが表示されます。
リスト内のタイマーのソート基準は、以下の項目に基づいています。
-
タイマーの優先度。
-
前回の時間に基づいたタイマーの実行所要時間。実行時間が短いタイマーが最初に実行されます。
-
タイマーの実行待機時間。タイマーは待機時間が長くなるほど、リストの上位に表示されやすくなります。
以下の図は、10分の差がある2つの瞬間の[Timers]セクションの例を示しています。
このSend Notificationsタイマーの例では、以下のことがわかります。
- 14時15分00秒に実行されるようにスケジュール設定されていた。
- 実際には14時14分10秒に実行された。
つまり、タイマーは予想よりも前に実行されました。この原因としては、アプリケーションによって明示的に実行されたか、またはService Centerで誰かがタイマーを強制的に実行したことが考えられます。
タイマーの状態を確認する
Service Centerでは、[Monitoring]の[Environment Health]オプションで、Scheduler Serviceが実行しているタイマースレッドを確認できます。
[Front-end Servers]セクションで[Scheduler]列の[detail]リンクをクリックして、サービスの詳細を含むページを表示します。ここでは、タイマー関連のスレッドに着目します。
上記の例では、Scheduler Serviceにタイマーに関連する以下のスレッドがあります。
-
Timers Fetcherは、実行するタイマージョブを取得してキューに入れて後で取り出すために、随時起動されるスレッドです。現時点で、これはスリープ状態です。
-
キューからタイマージョブを取り出して実行する3つのTimer Processorスレッドがあります。Time Processorスレッドの数はConfiguration Toolで構成されます。
また、以下のように判断することもできます。
-
すべてのタイマープロセッサスレッドのステータスが「Idle」だったため、タイマージョブは実行されていなかった。
-
キューにエントリがないということは、その時点で実行するタイマージョブがないことを示している。
タイマーを編集する
タイマーを編集するには、Service Centerの以下のいずれかの場所でタイマー名をクリックします。
-
Module: タイマーが定義されているモジュールを編集します。[Timers]タブを選択すると、タイマーのリストが表示されます。
-
Timers Log: [Timers Log]ページに移動します。このページではリストの各行にタイマー名が表示されます。
-
Environment Health: [Environment Health]ページに移動し、[Timers]セクションを確認します。このセクションではリストの各行にタイマー名が表示されます。
上記の例では、以下のことがわかります。
-
タイマーは15分間隔で実行される。
-
優先度は普通に設定されている。
-
タイムアウトは設定されていないため、Configuration Toolで設定されたデフォルト(20分)になっている。
-
最後の実行は2013年8月1日の18時15分09秒に行われた。
-
次回の実行は2013年8月1日の18時30分00秒に設定されている。
タイマーを強制的に実行する
スケジュール設定された次回の実行時刻を待たずにタイマーを強制的に実行するには、ページ下部にある[Run Now]ボタンを押します。
タイマーを非アクティベート/アクティベートする
タイマーの実行(Scheduler Serviceによる取得とリソースの利用)を停止するには、ページ下部にある[Deactivate]ボタンを押します。タイマーは、Scheduler Serviceによって取得されなくなります。
タイマーを非アクティベートすると、ボタンに「Activate」と表示されます。このボタンを押すと、タイマーが再び動作します。
タイマーのログを確認する
Service Centerでは、タイマーの実行状況を調べることができます。そのためには、[Monitoring]の[Timers]オプションを選択します。エラーが発生した場合は、[Error]リンクをクリックすると詳細を確認できます。