Salesforce Schedulable Apex のマスター:自動化バッチジョブ開発者ガイド

背景と応用シナリオ

Salesforce プラットフォームでは、ビジネスプロセスの自動化が重要な要素です。フローやプロセスビルダーは、レコードの作成や更新といったイベント駆動型の自動化に優れていますが、特定の時間にタスクを実行する必要がある場合もあります。例えば、「毎晩深夜に外部システムとデータを同期する」、「毎週金曜日の夕方にサマリーレポートを生成して経営陣にメールで送信する」、「毎月1日に古いToDoレコードをアーカイブする」といった要件です。

このような時間基準の自動化要件を実現するために Salesforce が提供しているのが Schedulable Apex (スケジュール可能な Apex) です。これは、開発者が Apex コードを特定の日時に、あるいは定期的なスケジュールで実行できるようにするための強力な機能です。手動での繰り返し作業をなくし、プロセスの信頼性を高め、システム管理者の負担を大幅に軽減することができます。

応用シナリオは多岐にわたります:

データクリーンアップとメンテナンス

長期間更新されていないレコードや、特定の条件を満たす古いレコード(例:完了してから1年以上経過したToDo)を定期的に削除またはアーカイブします。これにより、データ品質を維持し、ストレージ使用量を最適化します。

定期的なレポート生成と通知

週次や月次の売上データを集計し、関係者にメールでレポートを送信します。あるいは、契約終了日が近づいている顧客リストを抽出し、営業担当者に通知することも可能です。

外部システムとのデータ同期

多くの企業では、ERP や会計システムなど、Salesforce 以外のシステムも利用しています。Schedulable Apex を使用して、深夜などシステムの負荷が低い時間帯に、これらの外部システムとの間でデータを一括同期するバッチ処理を実行できます。

複雑なビジネスロジックの実行

日次で全顧客のクレジットスコアを再計算したり、階層的なデータを集計して親レコードに反映させたりするなど、リアルタイムでの実行が難しい、あるいはリソースを大量に消費する処理をオフピーク時に実行します。

このように、Schedulable Apex は、Salesforce の自動化ツールキットの中で、時間という要素をトリガーとするユニークで不可欠な役割を担っています。


原理説明

Schedulable Apex の中核となるのは、`Schedulable` という名前の Apex の interface (インターフェース) です。インターフェースとは、特定のメソッドを必ず実装することをクラスに強制する契約のようなものです。クラスが `Schedulable` インターフェースを実装 (implements) すると、そのクラスは Salesforce のスケジューラから呼び出せるようになります。

Schedulable インターフェース

このインターフェースには、実装必須のメソッドが一つだけ定義されています。 `void execute(SchedulableContext sc)`

この `execute` メソッド内に、スケジュールされた時間に実行したい Apex コードを記述します。引数の `SchedulableContext` オブジェクトは、現在実行中のジョブの ID などを取得するために使用できますが、多くの場合、この引数は直接使用されません。

ジョブのスケジューリング

`Schedulable` インターフェースを実装したクラスを作成しただけでは、コードは自動実行されません。そのクラスのインスタンスを作成し、`System.schedule` メソッドを使って実行スケジュールを Salesforce に登録する必要があります。

`System.schedule` メソッドは主に以下の3つの引数を取ります。

System.schedule(jobName, cronExpression, schedulableClassInstance);
  • jobName (String): ジョブの名前です。Salesforce の設定画面「スケジュール済みジョブ」で表示されるため、一意で分かりやすい名前を付けます。
  • cronExpression (String): ジョブをいつ実行するかを定義する CRON (クロン) 式です。これは、時間、日、月、曜日などを指定するための標準的なフォーマットです。
  • schedulableClassInstance (Object): `Schedulable` インターフェースを実装したクラスのインスタンスです。

CRON 式の理解

CRON 式は Schedulable Apex を使いこなす上で非常に重要です。その形式は以下の通りです。
`Seconds Minutes Hours Day_of_month Month Day_of_week [Optional_year]`

例えば、

  • `0 0 22 * * ?` : 毎日午後10時に実行
  • `0 0 8 ? * MON-FRI` : 月曜日から金曜日の毎朝8時に実行
  • `0 0 12 1 * ?` : 毎月1日の正午に実行

`?` (疑問符) は、`Day_of_month` (日) と `Day_of_week` (曜日) のどちらか一方に指定する必要がある場合に利用されるワイルドカードで、「指定なし」を意味します。両方を同時に指定することはできません。

ジョブの監視

スケジュールされたジョブは、Salesforce の [設定] > [環境] > [ジョブ] > [スケジュール済みジョブ] から一覧で確認できます。ここでは、次の実行予定時刻や、ジョブの停止・削除が可能です。ジョブの実行履歴は、[Apex ジョブ] ページで確認できます。


示例代码

ここでは、ベストプラクティスとしてよく用いられる、Schedulable Apex から Batch Apex (バッチ Apex) を呼び出す例を示します。Schedulable Apex 自体は同期処理のガバナ制限を受けるため、大量のデータを処理する場合は、非同期でガバナ制限が緩和された Batch Apex に処理を委譲するのが一般的です。

この例では、毎週特定の時間に、取引先責任者 (Contact) レコードの説明 (Description) 項目を更新するバッチジョブをスケジュールします。

1. Batch Apex クラス

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

// developer.salesforce.com の公式ドキュメントに基づく Batch Apex のサンプル
global class UpdateContactDescriptions implements Database.Batchable<sObject> {

    // start メソッド: 処理対象のレコードを決定する SOQL クエリを返す
    // Database.QueryLocator を返すことで、最大5,000万レコードまで処理可能
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT Id, Description FROM Contact WHERE Description = NULL LIMIT 200'
        );
    }

    // execute メソッド: start メソッドで取得したレコードのチャンク(塊)ごとに処理を実行する
    // この例では、各 Contact の Description 項目を更新する
    global void execute(Database.BatchableContext bc, List<Contact> scope) {
        for (Contact contact : scope) {
            contact.Description = 'Batch processed on ' + System.now();
        }
        update scope;
    }

    // finish メソッド: すべてのバッチ処理が完了した後に実行される
    // エラー通知や後処理などを記述する
    global void finish(Database.BatchableContext bc) {
        System.debug('UpdateContactDescriptions batch job finished.');
        // 例えば、ジョブ完了をシステム管理者にメールで通知する処理をここに追加できる
    }
}

2. Schedulable Apex クラス

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

// developer.salesforce.com の公式ドキュメントに基づく Schedulable Apex のサンプル
global class ScheduledBatchable implements Schedulable {

    // Schedulable インターフェースで必須の execute メソッド
    // スケジュールされた時間にこのメソッドが呼び出される
    global void execute(SchedulableContext sc) {
        // 処理を実行したいバッチクラスのインスタンスを作成する
        UpdateContactDescriptions batchJob = new UpdateContactDescriptions();
        
        // Database.executeBatch を呼び出してバッチジョブを開始する
        // 第2引数でバッチのチャンクサイズ(デフォルトは200)を指定できる
        Database.executeBatch(batchJob, 100);
    }
}

3. ジョブのスケジュール登録

最後に、開発者コンソールの「匿名実行」ウィンドウなどから以下のコードを実行して、ジョブをスケジュールに登録します。

// 毎週土曜日の午前3時にジョブを実行するCRON式
String cronExpression = '0 0 3 ? * SAT';

// スケジュールするクラスのインスタンスを作成
ScheduledBatchable schedulerInstance = new ScheduledBatchable();

// System.schedule メソッドでジョブを登録
// ジョブ名は組織内で一意である必要がある
String jobName = 'Weekly Contact Description Update';
System.schedule(jobName, cronExpression, schedulerInstance);

このコードを実行すると、`Weekly Contact Description Update` という名前のジョブが登録され、毎週土曜日の午前3時に `ScheduledBatchable` クラスの `execute` メソッドが実行されます。その結果、`UpdateContactDescriptions` バッチジョブが起動し、取引先責任者のデータが更新されます。


注意事項

Schedulable Apex を利用する際には、いくつかの制約と注意点を理解しておく必要があります。

ガバナ制限 (Governor Limits)

`execute` メソッドの実行自体は、通常の同期 Apex トランザクションと同じガバナ制限(SOQLクエリ発行回数100回、合計CPU時間10,000ミリ秒など)に従います。そのため、大量のデータを処理するロジックを `execute` メソッドに直接記述するべきではありません。前述のサンプルのように、処理を Batch Apex や Queueable Apex に委譲することが強く推奨されます。

また、組織全体で同時にスケジュールできる Apex ジョブの数には上限があります(通常は100件)。この上限には、実行待機中または実行中のすべてのジョブが含まれます。

実行コンテキストと権限

スケジュールされたジョブは、そのジョブをスケジュールしたユーザーの権限で実行されます。つまり、そのユーザーがアクセスできないオブジェクトや項目には、ジョブもアクセスできません。この点を考慮して、十分な権限を持つユーザー(通常はシステム管理者)がジョブをスケジュールするようにしてください。

テストクラスの実装

Schedulable Apex のテストは特殊です。テストクラス内で `System.schedule` を直接呼び出すことはできず、呼び出しても実際にはジョブはスケジュールされません。テストを行うには、`Test.startTest()` と `Test.stopTest()` の間に、スケジューラブルクラスのインスタンスを作成し、その `execute` メソッドを直接呼び出します。

@isTest
private class ScheduledBatchableTest {
    static testMethod void testScheduledJob() {
        // テストデータの準備
        
        Test.startTest();
        // スケジューラブルクラスのインスタンスを作成
        ScheduledBatchable sch = new ScheduledBatchable();
        // CRON式をダミーで作成(スケジューリングのテストではないため任意)
        String schTime = '0 0 3 ? * SAT';
        // System.schedule の代わりに、execute メソッドを直接呼び出す
        System.schedule('Test Job', schTime, sch);
        Test.stopTest();
        
        // ここで、バッチジョブが実行された結果をアサーションで検証する
        // 例: 特定のレコードが更新されたかを確認する SOQL クエリなど
    }
}

`Test.startTest()` と `Test.stopTest()` のブロック内で `System.schedule` を実行すると、ジョブは同期的に実行されるため、テストカバレッジを確保し、非同期処理の結果を検証することができます。

エラー処理

`execute` メソッド内で例外が発生し、それがキャッチされない場合、ジョブは失敗します。しかし、その失敗は自動的には通知されません。[Apex ジョブ] ページでステータスを確認しないと、失敗に気づかない可能性があります。堅牢な実装のためには、`execute` メソッド全体を `try-catch` ブロックで囲み、エラーが発生した際にはカスタムオブジェクトにエラーログを記録したり、システム管理者にメールで通知するなどの処理を組み込むべきです。


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

Schedulable Apex は、Salesforce で時間基準の自動化を実現するための強力なツールです。正しく利用することで、手作業を削減し、ビジネスプロセスの一貫性と信頼性を向上させることができます。

最後に、Schedulable Apex を実装する上でのベストプラクティスをまとめます。

  1. Schedulable クラスは軽量に保つ: `execute` メソッドの役割は、処理を開始するための「トリガー」に徹するべきです。重いデータ処理や複雑なビジネスロジックは、Batch Apex や Queueable Apex に委譲しましょう。
  2. 大量データには Batch Apex を利用する: 数百件以上のレコードを処理する可能性がある場合は、必ず Batch Apex を使用してガバナ制限を回避してください。
  3. 冪等性 (Idempotency) を意識する: ジョブが何らかの理由で複数回実行されても、システムの状態に悪影響が出ないように設計することが重要です。これを冪等性と呼びます。例えば、処理済みのレコードにはフラグを立て、次回実行時にはそのレコードをスキップするなどの工夫が考えられます。
  4. 堅牢なエラーハンドリングを実装する: `try-catch` を用いて例外を捕捉し、失敗時にはログ記録や通知を行う仕組みを必ず組み込みます。ジョブの「サイレントフェイラー(静かな失敗)」は最も避けるべき事態です。
  5. 設定のハードコーディングを避ける: CRON 式や処理の閾値などをコード内に直接書き込むのではなく、カスタムメタデータやカスタム設定に保存することで、コードの変更なしに管理者がスケジュールを調整できるようになります。
  6. ジョブの監視を怠らない: [スケジュール済みジョブ] と [Apex ジョブ] ページを定期的に確認し、ジョブが期待通りに実行されているかを監視する運用プロセスを確立しましょう。

これらの原則に従うことで、安定的で保守性の高い、スケーラブルな自動化ソリューションを構築することができます。

コメント