Schedulable Apex の完全ガイド:Salesforce 開発者のための自動化テクニック

背景と応用シナリオ

Salesforce 開発者として、私たちは常に業務プロセスの自動化と効率化を求められています。手動で行われている定型的なタスクを自動化することで、ヒューマンエラーを削減し、より価値の高い戦略的な業務にリソースを集中させることができます。この自動化を実現するための強力なツールの一つが Schedulable Apex (スケジュール可能なApex) です。

Schedulable Apex は、指定した日時に Apex クラスを自動的に実行するためのフレームワークです。これにより、「毎日深夜に実行」「毎週日曜日の朝に実行」「毎月1日に実行」といった、時間ベースのトリガーでビジネスロジックを起動させることが可能になります。

具体的な応用シナリオは多岐にわたります。

定期的なデータクレンジング

例えば、長期間更新されていない商談レコードのフェーズを「失注」に自動更新したり、古くなったカスタムログオブジェクトのデータを毎週末に削除したりするタスクです。これにより、データの鮮度と正確性を保ち、ストレージ使用量を最適化します。

レポートデータ生成と通知

週次や月次の売上データを集計し、関連するマネージャーにメールでサマリーを送信するプロセスを自動化できます。複雑な集計ロジックが必要な場合でも、Apex を使って柔軟に対応し、定期的にレポートを配信することが可能です。

外部システムとの夜間バッチ同期

オンプレミスの ERP システムや外部のデータウェアハウスと、日次でデータを同期するシナリオです。ユーザの活動が少ない深夜帯に Schedulable Apex を実行し、大量のデータを一括で送受信することで、日中のシステムパフォーマンスへの影響を最小限に抑えます。

リマインダーや期限切れ通知

契約終了日が近づいている取引先に対して、担当営業に自動で ToDo を作成したり、期限切れが近い ToDo をエスカレーションしたりする処理も、Schedulable Apex の得意分野です。

このように、Schedulable Apex は定期的なバッチ処理やメンテナンス作業を自動化するための基盤となる技術であり、Salesforce プラットフォームの運用において不可欠な存在です。


原理説明

Schedulable Apex を実装するには、Apex クラスに Salesforce が提供する Schedulable インターフェースを実装する必要があります。このインターフェースには、実装が必須となるメソッドが一つだけ定義されています。

void execute(SchedulableContext sc)

この execute メソッドが、スケジュールされた日時に Salesforce によって呼び出される処理の本体です。引数の SchedulableContext オブジェクトには、実行中のジョブの ID (getTriggerId()) や、スケジュールされた日時 (getScheduledTime()) などのコンテキスト情報が含まれており、必要に応じて処理内で利用できます。

クラスが Schedulable インターフェースを実装したら、次はそのジョブをいつ実行するかを Salesforce に登録(スケジュール)する必要があります。スケジュールする方法は主に2つあります。

1. Salesforce UI を使用する方法

[設定] -> [Apex クラス] に移動し、「Apex をスケジュール」ボタンをクリックします。ここで、ジョブ名、実行したい Apex クラス、実行頻度(毎週、毎月)、開始日、終了日、希望の時間を指定することで、コーディングなしでジョブをスケジュールできます。これは、管理者でも簡単に設定できる方法です。

2. System.schedule メソッドを使用する方法

開発者がより動的かつ柔軟にスケジュールを設定したい場合は、Apex コード内から System.schedule メソッドを呼び出します。この方法は、例えば特定のビジネスプロセスが完了したタイミングで、その1週間後に行うフォローアップタスクを自動でスケジュールする、といったシナリオで非常に有効です。

System.schedule メソッドは、ジョブ名、CRON 式 (CRON expression)、そして実行する Schedulable クラスのインスタンスを引数に取ります。

CRON 式は、ジョブの実行タイミングを詳細に定義するための文字列です。Salesforce の CRON 式は以下の7つのセクションで構成されます。

秒 分 時 日 月 曜日 [年(オプション)]

  • 秒 (Seconds): 0-59
  • 分 (Minutes): 0-59
  • 時 (Hours): 0-23 (24時間表記)
  • 日 (Day of month): 1-31 または '?' (曜日指定の場合)
  • 月 (Month): 1-12 または3文字の月名 (JAN, FEB など)
  • 曜日 (Day of week): 1-7 (1=日曜) または3文字の曜日名 (SUN, MON など)、'?' (日付指定の場合)
  • 年 (Optional year): 空白または 1970-2099

例えば、「毎週月曜日から金曜日の午後5時に実行」というスケジュールは、'0 0 17 ? * MON-FRI' という CRON 式で表現できます。この柔軟性こそが、開発者が System.schedule を好む理由の一つです。


示例コード

ここでは、大量のデータを安全に処理するためのベストプラクティスとして、Schedulable Apex から Batch Apex (バッチApex) を呼び出す例を紹介します。execute メソッド内で直接重い処理を行うと、ガバナ制限に抵触する可能性があるため、処理を小さなチャンクに分割して実行できる Batch Apex に委譲するのが一般的です。

この例では、年間売上が一定額以上で、かつ格付け(Rating)が設定されていない取引先(Account)レコードを検索し、格付けを 'Hot' に更新するバッチ処理を、毎週日曜日の午前2時に実行するシナリオを考えます。

1. Batch Apex クラス

まず、実際のデータ更新処理を行う Batch Apex クラスを作成します。

// 取引先の格付けを更新するBatch Apexクラス
global class UpdateAccountRatingBatch implements Database.Batchable<sObject> {

    // バッチ処理の対象となるレコードを返す SOQL クエリを定義
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT Id, Name, AnnualRevenue FROM Account ' +
            'WHERE AnnualRevenue >= 5000000 AND Rating = null'
        );
    }

    // レコードの各バッチ(チャンク)に対して実行される処理を定義
    // scope には、start メソッドから返されたレコードのリストが含まれる
    global void execute(Database.BatchableContext bc, List<Account> scope) {
        List<Account> accountsToUpdate = new List<Account>();
        for (Account acc : scope) {
            acc.Rating = 'Hot'; // 格付けを 'Hot' に更新
            accountsToUpdate.add(acc);
        }
        // 例外処理を実装することが重要
        try {
            update accountsToUpdate;
        } catch (DmlException e) {
            // エラーハンドリング: カスタムログオブジェクトへの記録やメール通知など
            System.debug('DML failed: ' + e.getMessage());
        }
    }

    // すべてのバッチ処理が完了した後に実行される処理
    global void finish(Database.BatchableContext bc) {
        // 処理完了の通知メールを送信するなどの後処理を実装
        System.debug('Batch job for updating account ratings has completed.');
    }
}

2. Schedulable Apex クラス

次に、上記の Batch Apex を定期的に呼び出す Schedulable クラスを作成します。

// UpdateAccountRatingBatch を定期的に起動するSchedulableクラス
global class ScheduledAccountRatingUpdate implements Schedulable {

    // スケジュールされた日時に実行されるメソッド
    global void execute(SchedulableContext sc) {
        // Batch Apex のインスタンスを生成
        UpdateAccountRatingBatch batchJob = new UpdateAccountRatingBatch();
        // バッチジョブを実行 (デフォルトのチャンクサイズは200)
        // 必要に応じて第二引数でチャンクサイズを指定可能: Database.executeBatch(batchJob, 100);
        Database.executeBatch(batchJob);
    }
}

3. スケジュールの設定

このジョブを開発者コンソールの「匿名実行」ウィンドウからスケジュールします。「毎週日曜日の午前2時」に実行するための CRON 式は '0 0 2 ? * SUN' です。

// ジョブをスケジュールするための匿名実行コード
// 日本時間(JST)でスケジュールする場合、タイムゾーンの考慮が必要
// Salesforce組織のタイムゾーンで実行される
String jobName = 'Weekly Account Rating Update';
String cronExpression = '0 0 2 ? * SUN'; // 毎週日曜日の午前2時0分0秒

ScheduledAccountRatingUpdate scheduler = new ScheduledAccountRatingUpdate();
System.schedule(jobName, cronExpression, scheduler);

4. テストクラス

Schedulable Apex は本番環境にデプロイする前に必ずテストが必要です。テストコードでは Test.startTest()Test.stopTest() を使用して、非同期処理を同期的にテストします。

@isTest
private class ScheduledAccountRatingUpdateTest {

    @testSetup
    static void setup() {
        // テスト用のデータを作成
        List<Account> testAccounts = new List<Account>();
        // 条件に一致するレコード (更新対象)
        testAccounts.add(new Account(Name='Test Account Hot', AnnualRevenue=6000000, Rating=null));
        // 条件に一致しないレコード (更新対象外)
        testAccounts.add(new Account(Name='Test Account Cold', AnnualRevenue=100000, Rating=null));
        insert testAccounts;
    }

    @isTest
    static void testScheduledJob() {
        // テストコンテキストを開始
        Test.startTest();

        // Schedulableクラスをスケジュール
        String jobName = 'Test Weekly Account Rating Update';
        String cronExpression = '0 0 2 ? * SUN';
        System.schedule(jobName, cronExpression, new ScheduledAccountRatingUpdate());

        // テストコンテキストを停止。これにより非同期処理(スケジュールとバッチ)が実行される
        Test.stopTest();

        // 結果を検証
        // バッチ処理によって格付けが更新されたことを確認
        Account updatedAccount = [SELECT Rating FROM Account WHERE Name = 'Test Account Hot' LIMIT 1];
        System.assertEquals('Hot', updatedAccount.Rating, 'The account rating should have been updated to Hot.');

        // 更新対象外のレコードが変わっていないことを確認
        Account untouchedAccount = [SELECT Rating FROM Account WHERE Name = 'Test Account Cold' LIMIT 1];
        System.assertEquals(null, untouchedAccount.Rating, 'This account rating should have remained null.');
    }
}

注意事項

Schedulable Apex を実装・運用する際には、いくつかの重要な点に注意する必要があります。

ガバナ制限 (Governor Limits)

Schedulable Apex の execute メソッドも、他の Apex トランザクションと同様にガバナ制限の対象となります。1回のトランザクション内で発行できる SOQL クエリは100回、DML ステートメントは150回といった制限です。これが、大量データを扱う際には Batch Apex を呼び出すことが推奨される最大の理由です。また、組織全体で同時にスケジュールできる Apex ジョブの数にも上限(通常は100件)があるため、不要なジョブは削除するよう心掛けてください。

実行コンテキストと権限

スケジュールされたジョブは、そのジョブをスケジュールしたユーザの権限で実行されます。つまり、そのユーザが参照できないオブジェクトや項目には、ジョブもアクセスできません。このため、必要な権限をすべて持つ専用のインテグレーションユーザでジョブをスケジュールすることが、運用上のベストプラクティスとされています。

エラーハンドリングと監視

スケジュールされたジョブはバックグラウンドで実行されるため、エラーが発生しても即座に気づくことができません。try-catch ブロックを適切に配置し、例外が発生した際にはカスタムログオブジェクトにエラー詳細を記録したり、Messaging.SingleEmailMessage を使ってシステム管理者に通知メールを送信したりする堅牢なエラーハンドリング機構を実装することが不可欠です。[設定] -> [ジョブ] -> [Apex ジョブ] ページで、過去のジョブの実行状況(成功、失敗)を監視できますが、プロアクティブな通知メカニズムを組み込むことが推奨されます。

API 制限

System.schedule メソッドの呼び出し自体も、1トランザクションあたりのコール数に制限があります。トリガーなどから動的に大量のジョブをスケジュールしようとすると、この制限に抵触する可能性があるため、設計には注意が必要です。


まとめとベストプラクティス

Schedulable Apex は、Salesforce 上での定型的なタスクを自動化し、運用効率を劇的に向上させるための強力な機能です。その原理は Schedulable インターフェースと execute メソッドというシンプルなものですが、その応用範囲は広く、プラットフォームの価値を最大限に引き出す上で欠かせません。

最後に、開発者として Schedulable Apex を扱う上でのベストプラクティスをまとめます。

  1. execute メソッドは軽量に保つ: execute メソッドの役割は、処理の起点となることだけに留め、実際の重い処理は Batch Apex や Queueable Apex に委譲します。これにより、ガバナ制限を回避し、スケーラビリティを確保できます。
  2. ハードコーディングを避ける: クエリの条件値、メールアドレス、エンドポイント URL などをコード内に直接書き込むのではなく、カスタムメタデータ型 (Custom Metadata Types) やカスタム設定 (Custom Settings) を使用して、設定を外部化します。これにより、コードを修正・デプロイすることなく、管理者が設定値を変更できるようになります。
  3. 堅牢なエラーハンドリングを実装する: 予期せぬデータやシステムの状況変化に対応できるよう、必ず try-catch ブロックを実装し、エラー発生時の通知とロギングの仕組みを確立します。
  4. 網羅的なテストを行う: 想定される正常系シナリオだけでなく、データが存在しない場合やエラーが発生する場合など、異常系のシナリオもカバーするテストクラスを作成します。
  5. ジョブの監視と管理を怠らない: 定期的に [Apex ジョブ] ページを確認し、不要になったスケジュール済みジョブは削除します。組織のスケジュール済みジョブの上限に達しないように注意してください。

これらのプラクティスに従うことで、安定的で保守性の高い自動化プロセスを構築し、Salesforce プラットフォームをより強力なビジネスツールへと進化させることができるでしょう。

コメント