背景と適用シナリオ
今日のビジネス環境では、プロセスの自動化は効率化と生産性向上の鍵となります。Salesforce プラットフォームは、ワークフロールール、プロセスビルダー、フローなどの強力な宣言的自動化ツールを提供していますが、特定の時間に、または定期的なスケジュールでビジネスロジックを実行する必要がある場合も少なくありません。このような要件に応えるのが、Schedulable Apex です。
Schedulable Apex は、Apex クラスを特定の日時に実行するようにスケジュールできる、Salesforce の非同期処理フレームワークの一部です。これにより、開発者は時間ベースの自動化をプログラムで実装できます。手動での介入を必要とせずに、バックグラウンドでタスクを定期的に実行する必要がある場合に非常に役立ちます。
主な適用シナリオ:
- 夜間のデータクリーンアップ: 毎日深夜に、重複したリードや古いケースなどの不要なデータをクリーンアップまたはアーカイブする。
- 週次のレポート生成: 毎週金曜日の終業時に、週間の営業活動サマリーレポートを生成し、マネージャー陣にメールで送信する。
- 月次の請求処理: 毎月1日に、契約中の顧客に対して請求書レコードを自動作成する。
- 定期的なデータ同期: 1時間ごとに外部の ERP システムと通信し、在庫情報や注文ステータスを同期する。
- リマインダー通知: 契約終了日が近づいている顧客の営業担当者に対し、事前にタスクを自動作成して通知する。
これらのシナリオが示すように、Schedulable Apex は、反復的で予測可能なタスクを自動化し、ユーザーがより戦略的な業務に集中できるようにするための強力なソリューションです。
原理説明
Schedulable Apex の仕組みを理解するには、主に2つの重要な要素、`Schedulable` インターフェースと `System.schedule` メソッドを把握する必要があります。
Schedulable インターフェース
Apex クラスをスケジュール可能にするには、`Schedulable` インターフェース(日本語:スケジューラブルインターフェース)を実装(implement)する必要があります。このインターフェースには、実装必須のメソッドが1つだけ定義されています。
`void execute(SchedulableContext sc)`
Salesforce スケジューラがジョブを実行する時間になると、この `execute` メソッドが呼び出されます。すべてのビジネスロジック、例えばレコードのクエリ、更新、外部システムへのコールアウトなどは、このメソッド内に記述します。引数として渡される `SchedulableContext` オブジェクトには、現在実行中のジョブの ID などのコンテキスト情報が含まれており、`sc.getTriggerId()` を使用して取得できます。
System.schedule メソッド
`Schedulable` インターフェースを実装したクラスを作成した後、そのクラスをいつ実行するかを Salesforce に指示する必要があります。これは `System.schedule` メソッドを使用して行います。このメソッドは、開発者コンソールの匿名実行ウィンドウや、他の Apex クラス(トリガーなど)から呼び出すことができます。`System.schedule` メソッドには3つの引数が必要です。
`System.schedule(jobName, cronExpression, schedulableClassInstance)`
- `jobName` (String): ジョブの一意な名前です。[設定] > [ジョブ] > [スケジュール済みジョブ] のリストに表示され、監視や管理に役立ちます。
- `cronExpression` (String): CRON 式(クロン式)と呼ばれる、ジョブの実行スケジュールを定義する文字列です。特定の時間、日、月、曜日などを指定できます。
- `schedulableClassInstance` (Object): スケジュールしたい `Schedulable` インターフェースを実装したクラスのインスタンスです。(例: `new MySchedulableClass()`)
Salesforce の CRON 式は7つの要素で構成されます(最後の「年」は任意)。
`Seconds Minutes Hours Day_of_month Month Day_of_week [optional_year]`
- Seconds: 0-59
- Minutes: 0-59
- Hours: 0-23 (24時間表記)
- Day_of_month: 1-31
- Month: 1-12 または JAN, FEB, MAR など
- Day_of_week: 1-7 または SUN, MON, TUE など (1=日曜日)
- [optional_year]: 任意。指定しない場合は毎年実行されます。
例:
- `'0 0 13 * * ?'` : 毎日午後1時に実行
- `'0 0 22 ? * MON-FRI'` : 月曜日から金曜日の毎日午後10時に実行
- `'0 0 8 1 * ?'` : 毎月1日の午前8時に実行
注意: `Day_of_month` と `Day_of_week` の両方を指定することはできません。どちらか一方には `?` を使用して「指定なし」を意味させる必要があります。
サンプルコード
ここでは、Salesforce の公式ドキュメントで紹介されている例を基に、毎週特定の曜日に実行され、条件に合致する商談のフォローアップタスクを作成する Schedulable Apex クラスを作成します。
Schedulable Apex クラス
以下のクラスは、毎週実行されるように設計されています。金額が大きく、かつ完了予定日が近いにもかかわらず、まだ完了していない商談を検索し、所有者のためにフォローアップタスクを作成します。
global class WeeklyOppReminder implements Schedulable { // Schedulable インターフェースで必須の execute メソッド global void execute(SchedulableContext sc) { // 完了予定日が次の7日間で、まだ進行中の商談をSOQLで検索 // 金額が 500,000 を超えるものに絞り込む List<Opportunity> opportunities = [ SELECT Id, Name, OwnerId, CloseDate, Amount FROM Opportunity WHERE CloseDate = NEXT_N_DAYS:7 AND StageName != 'Closed Won' AND StageName != 'Closed Lost' AND Amount > 500000 ]; // フォローアップ用のタスクを格納するリストを初期化 List<Task> tasksToCreate = new List<Task>(); // 検索された商談をループ処理 for(Opportunity opp : opportunities) { // 新しいタスクオブジェクトを作成 Task followUpTask = new Task(); // 件名を設定。商談名を含めることで分かりやすくする followUpTask.Subject = 'Follow up on large opportunity: ' + opp.Name; // 所有者を商談の所有者と同じに設定 followUpTask.OwnerId = opp.OwnerId; // WhatId に商談のIDを設定し、タスクを商談に関連付ける followUpTask.WhatId = opp.Id; // 優先度を 'High' に設定 followUpTask.Priority = 'High'; // 状況を 'Not Started'(未着手)に設定 followUpTask.Status = 'Not Started'; // 期日を商談の完了予定日の2日前に設定 followUpTask.ActivityDate = opp.CloseDate.addDays(-2); // 作成するタスクのリストに追加 tasksToCreate.add(followUpTask); } // 作成するタスクが1件以上ある場合のみ、DML操作を実行 if(!tasksToCreate.isEmpty()) { // try-catch ブロックで DML エラーを捕捉 try { insert tasksToCreate; } catch (DmlException e) { // エラーが発生した場合、デバッグログにエラーメッセージを出力 // 実際のプロジェクトでは、カスタムオブジェクトへのログ記録やメール通知などを検討 System.debug('An error occurred during task insertion: ' + e.getMessage()); } } } }
ジョブのスケジューリング
上記のクラスを Apex として保存した後、開発者コンソールの [Debug] > [Open Execute Anonymous Window] を開き、以下のコードを実行してジョブをスケジュールします。この例では、毎週金曜日の午後5時に実行するように設定しています。
// スケジュールするクラスのインスタンスを作成 WeeklyOppReminder reminderJob = new WeeklyOppReminder(); // CRON式で毎週金曜日の17:00 (午後5時) を指定 // 形式: Seconds Minutes Hours Day_of_month Month Day_of_week // '0 0 17 ? * FRI' String cronExpression = '0 0 17 ? * FRI'; // ジョブ名、CRON式、クラスインスタンスを指定してジョブをスケジュール String jobId = System.schedule('Weekly Opportunity Follow-up Task Creator', cronExpression, reminderJob); // スケジュールされたジョブのIDをデバッグログに出力 System.debug('Scheduled job ID: ' + jobId);
このコードを実行すると、ジョブが Salesforce のキューに追加されます。[設定] > [スケジュール済みジョブ] に移動すると、スケジュールしたジョブ("Weekly Opportunity Follow-up Task Creator")がリストに表示され、次回の実行時間などを確認できます。
注意事項
Schedulable Apex を実装・運用する際には、Salesforce のプラットフォームの制約と特性を理解しておくことが不可欠です。
ガバナ制限 (Governor Limits)
- スケジュール済みジョブの最大数: 1つの Salesforce 組織で有効にできるスケジュール済み Apex ジョブの数には上限があります。通常は100件です。`Limits.getLimitScheduledJobs()` メソッドで組織の上限を確認できます。上限に達すると、新たな `System.schedule` の呼び出しは失敗します。
- System.schedule の呼び出し制限: 1つのトランザクション内で `System.schedule` を呼び出せる回数にも制限があります(通常50回)。
- 非同期処理の制限: Schedulable Apex は非同期で実行されるため、CPU 時間やヒープサイズなど、非同期処理に適用されるガバナ制限に従います。これは同期的処理よりも緩和されていますが、無限ではありません。
権限と実行コンテキスト
- スケジューリング権限: ジョブをスケジュールするユーザーには、「Author Apex」権限が必要です。
- 実行ユーザー: スケジュールされたジョブは、ジョブをスケジュールしたユーザーの権限で実行されます。つまり、そのユーザーがアクセスできないオブジェクトや項目には、ジョブもアクセスできません。この点を考慮して、適切な権限を持つユーザー(インテグレーションユーザーなど)でジョブをスケジュールすることが重要です。
エラー処理
`execute` メソッド内でハンドルされない例外が発生すると、ジョブは失敗し、その回の実行は中止されます。しかし、その失敗が自動的にシステム管理者に通知されるわけではありません。そのため、`execute` メソッドのロジック全体を `try-catch` ブロックで囲み、エラーを捕捉することが強く推奨されます。
捕捉したエラーは、カスタムオブジェクトにログとして記録したり、`Messaging.SingleEmailMessage` を使って関係者にメールで通知したりするべきです。これにより、ジョブの失敗を迅速に検知し、対応することが可能になります。
テスト
Schedulable Apex も通常の Apex と同様に、コードカバレッジを満たすテストクラスが必要です。非同期処理であるため、テストメソッド内で `Test.startTest()` と `Test.stopTest()` を使用します。`System.schedule` を `Test.startTest()` の後で呼び出し、`Test.stopTest()` を実行すると、その間にスケジュールされた非同期処理が同期的に実行され、結果をアサーション(`System.assertEquals` など)で検証できます。
@isTest private class WeeklyOppReminderTest { @isTest static void testScheduledJob() { // テストデータの準備 // (商談レコードなどを作成) // CRON式を定義 (テストでは実際の日時と一致させる必要はない) String cronExpression = '0 0 17 ? * FRI'; String jobName = 'Test Weekly Opp Reminder'; Test.startTest(); // ジョブをスケジュール System.schedule(jobName, cronExpression, new WeeklyOppReminder()); Test.stopTest(); // Test.stopTest() の後、ジョブが実行された状態になる // 結果を検証 (タスクが作成されたかなどをクエリして確認) List<Task> createdTasks = [SELECT Id FROM Task]; System.assertEquals(1, createdTasks.size(), 'A task should have been created.'); } }
まとめとベストプラクティス
Schedulable Apex は、Salesforce における時間ベースの自動化を実現するための強力で柔軟なツールです。定期的なバッチ処理、データメンテナンス、通知など、幅広い用途に活用できます。しかし、その力を最大限に引き出し、安定的かつスケーラブルなソリューションを構築するためには、以下のベストプラクティスを遵守することが重要です。
- ロジックの分離 (Single Responsibility): Schedulable Apex の `execute` メソッドは、あくまで処理の「トリガー」として機能させるべきです。大量のレコードを処理するような重いロジックは、専用の Batch Apex(バッチApex)クラスに実装し、`execute` メソッドからはそのバッチジョブを呼び出す(`Database.executeBatch(new MyBatchClass());`)ように設計します。これにより、ガバナ制限に抵触するリスクを大幅に低減できます。
- 設定の外部化: CRON 式や処理の閾値(例: 金額 > 500,000)などをコード内にハードコーディングするのではなく、カスタムメタデータ型 (Custom Metadata Types) やカスタム設定 (Custom Settings) に保存します。これにより、管理者はコードを変更することなく、GUI 上でスケジュールの変更やビジネスルールの調整が可能になり、メンテナンス性が向上します。
- 堅牢なエラーハンドリングと監視: すべての `execute` メソッドに `try-catch` ブロックを実装し、エラー発生時には詳細な情報をカスタムログオブジェクトに記録します。さらに、重大なエラーが発生した場合には Platform Events を発行したり、システム管理者にメール通知を送る仕組みを構築し、プロアクティブな監視体制を整えましょう。
- 冪等性 (Idempotency) の確保: ジョブが何らかの理由で複数回実行されても、システムに悪影響(例: 重複レコードの作成)を与えないように設計すること(冪等性)が理想です。例えば、処理対象のレコードに「処理済み」フラグを立て、ジョブの冒頭で処理済みでないレコードのみを対象とするなどの工夫が考えられます。
- ジョブ管理の一元化: 組織内で多数のスケジュールジョブが必要になる場合、ジョブが乱立して100件の上限に達しやすくなります。これを避けるため、1つの「マスター」スケジューラを作成し、そのジョブが毎時、または毎日実行され、実行すべき他のビジネスロジック(ヘルパークラスなど)を条件に応じて呼び出す、という設計パターンも有効です。
これらの原則に従うことで、Schedulable Apex を活用して、信頼性が高く、管理しやすく、拡張性のある自動化ソリューションを Salesforce 上に構築することができるでしょう。
コメント
コメントを投稿