本記事は Salesforce 開発者 の視点から、Salesforce の強力な自動化ツールの一つである Schedulable Apex について、その基本からベストプラクティスまでを網羅的に解説します。
背景と適用シナリオ
Salesforce は単なる CRM (Customer Relationship Management - 顧客関係管理) ツールではありません。ビジネスプロセスを自動化し、業務効率を飛躍的に向上させるための強力なプラットフォームです。その自動化機能の中核をなすのが Apex コードであり、特に時間ベースのタスクを自動実行する上で Schedulable Apex は不可欠な存在です。
日々の業務においては、特定の時間に決まった処理を実行したいという要求が数多く存在します。例えば、以下のようなシナリオが考えられます。
- 定期的なデータクレンジング: 毎日深夜に、1年以上更新のない古いリード(Lead)のステータスを「アーカイブ」に変更する。
- レポートの自動生成と通知: 毎週金曜日の夕方に、その週にクローズした商談(Opportunity)のサマリーレポートを作成し、営業マネージャーにメールで送信する。
- 外部システムとの夜間バッチ連携: 毎日午前3時に、ERP システムからエクスポートされた最新の製品価格データを Salesforce の価格表(Price Book)に同期する。
- 契約期限のリマインダー: 毎月1日に、翌月末に期限切れとなる契約(Contract)を抽出し、担当者へのリマインダータスク(Task)を自動で作成する。
これらのタスクを手動で行うのは非効率的であり、ヒューマンエラーのリスクも伴います。Flow やプロセスビルダーでも時間ベースのアクションは可能ですが、大量のレコード処理や複雑なビジネスロジック、外部システムとの連携など、より高度な要件に対応するためには Schedulable Apex が最適なソリューションとなります。
原理説明
Schedulable Apex の仕組みは非常にシンプルです。特定の Apex クラスに Salesforce が提供する Schedulable interface(インターフェース)を実装することで、そのクラスをスケジュール実行可能なジョブとして登録できるようになります。
Schedulable Interface
Schedulable インターフェースを実装するには、クラスの定義に implements Schedulable を追加します。このインターフェースには、実装が必須なメソッドが一つだけ定義されています。
execute(SchedulableContext sc) method:
このメソッドが、スケジュールされた時間に Salesforce によって呼び出される処理の本体です。引数の SchedulableContext オブジェクトからは、現在実行中のジョブの ID などを取得できますが、通常はあまり使用されません。この execute メソッド内に、実行したいビジネスロジックを記述します。
ジョブのスケジューリング
Schedulable Apex クラスを作成しただけでは、自動実行はされません。実行を予約するためには、System.schedule メソッドを呼び出す必要があります。このメソッドは通常、開発者コンソールの匿名実行(Anonymous Apex)ウィンドウや、別の Apex クラスから実行されます。
System.schedule メソッドは3つの引数を取ります。
- Job Name (String): スケジュールジョブの一意の名前です。[設定] > [Apex ジョブ] の画面でジョブを識別するために使用されます。
- CRON Expression (String): ジョブをいつ実行するかを定義する文字列です。CRON 式は、特定の時間、日、月、曜日にタスクをスケジュールするための標準的なフォーマットです。
- Schedulable Class Instance (Object): スケジュールしたい Schedulable Apex クラスのインスタンスを
newキーワードで生成して渡します。
CRON Expression
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 など
- Day_of_week: 1-7 (1=Sunday) または SUN, MON など
- Optional_year: 省略可能。年を指定する場合。
特殊文字も使用可能です。
*(アスタリスク): 全ての値を意味します。(例:分の位置で*は毎分)?(クエスチョンマーク): 「指定なし」を意味します。Day_of_month と Day_of_week のどちらか一方に指定する必要があります。
CRON 式の例:
'0 0 2 * * ?': 毎日午前2時に実行'0 30 17 ? * FRI': 毎週金曜日の午後5時30分に実行'0 0 1 1 * ?': 毎月1日の午前1時に実行
示例代码
ここでは、Salesforce の公式ドキュメントで紹介されている最も一般的なパターン、つまり Schedulable Apex から Batch Apex (Database.Batchable - 大量データを非同期で処理するための仕組み) を呼び出す例を見ていきましょう。このアプローチは、一度に処理するレコード数が多く、Governor Limit(ガバナ制限)に抵触する可能性がある場合に非常に有効です。
1. バッチ処理を行う Batch Apex クラス
まず、実際にデータ処理を行うバッチクラスを定義します。この例では、全ての取引先(Account)レコードを更新する簡単なバッチ処理です。
// Batch Apexクラスの定義
// 全てのAccountレコードをクエリし、Descriptionフィールドを更新します。
global class MyBatchableClass implements Database.Batchable<sObject> {
// startメソッド: 処理対象のレコードを決定するSOQLクエリを返します。
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator('SELECT Id, Name FROM Account');
}
// executeメソッド: startメソッドで取得したレコードのチャンク(塊)ごとに処理を実行します。
// この例では、各AccountのDescriptionを更新しています。
global void execute(Database.BatchableContext bc, List<Account> scope){
for (Account a : scope) {
a.Description = 'Batch Processed ' + System.now();
}
update scope;
}
// finishメソッド: 全てのバッチ処理が完了した後に実行されます。
// 例えば、処理結果をメールで通知するなどの後処理を記述します。
global void finish(Database.BatchableContext bc){
// 処理完了後のロジックをここに記述
System.debug('Batch job finished.');
}
}
2. バッチ処理を呼び出す Schedulable Apex クラス
次に、上記のバッチクラスを特定の時間に実行するためのスケジューラクラスを定義します。
// Schedulableインターフェースを実装したスケジューラクラス
global class MySchedulableClass implements Schedulable {
// executeメソッド: スケジュールされた時間にこのメソッドが実行されます。
global void execute(SchedulableContext sc) {
// 実行したいバッチクラスのインスタンスを作成します。
MyBatchableClass b = new MyBatchableClass();
// Database.executeBatchを呼び出してバッチジョブを開始します。
// 第2引数はバッチのチャンクサイズ(一度に処理するレコード数)です。
// 指定しない場合はデフォルトの200になります。
Database.executeBatch(b, 200);
}
}
3. ジョブのスケジュール実行
最後に、開発者コンソールの匿名実行ウィンドウなどから以下のコードを実行し、ジョブをスケジュールします。この例では、毎週土曜日の午前1時にジョブが実行されるように予約しています。
// 毎週土曜日の午前1時に実行するようスケジュールします。 MySchedulableClass sched = new MySchedulableClass(); String cronExp = '0 0 1 ? * SAT'; String jobName = 'Weekly Account Update'; System.schedule(jobName, cronExp, sched);
実行後、[設定] > [スケジュール済みジョブ] に `Weekly Account Update` という名前のジョブが登録されていることを確認できます。
注意事項
Schedulable Apex を実装・運用する際には、いくつかの重要な点に注意する必要があります。
権限 (Permissions)
System.schedule メソッドを実行してジョブをスケジュールするユーザーには、「Apex を作成および実行」の権限が必要です。また、スケジュールされたジョブは、そのジョブをスケジュールしたユーザーのコンテキストで実行されます。これは、レコードの共有設定や項目レベルセキュリティが、そのユーザーの権限に基づいて適用されることを意味します。そのため、必要なデータにアクセスできる権限を持ったユーザー(例えば、システム管理者やインテグレーション専用ユーザー)でスケジュールすることが推奨されます。
API 制限 (API and Governor Limits)
- スケジュールジョブ数の上限: 一度に有効にできるスケジュール済み Apex ジョブの数には上限があります(通常は100件)。この上限に達すると、新たなジョブをスケジュールすることはできません。
Limits.getLimitScheduledApexJobs()とLimits.getScheduledApexJobs()を使用して、現在の組織の上限と使用状況を確認できます。 - ガバナ制限: Schedulable Apex の
executeメソッド自体も、通常の同期 Apex と同様のガバナ制限(SOQLクエリの発行回数、DMLステートメントの実行回数、CPU時間など)に従います。前述のサンプルのように、大量のデータを扱う場合は、ガバナ制限が緩和される Batch Apex や Queueable Apex を呼び出すのが鉄則です。 System.scheduleの呼び出し:System.scheduleメソッド自体も、1つのトランザクション内で複数回呼び出すと制限に抵触する可能性があります。通常はセットアップ時やデプロイ時に一度だけ実行するものです。
エラー処理 (Error Handling)
execute メソッド内で例外が発生し、それがキャッチされない場合、ジョブは失敗し、その回の処理は中止されます。しかし、スケジュール自体は解除されず、次のスケジュールされた時間に再度実行を試みます。
堅牢なシステムを構築するためには、try-catch ブロックを使用して例外を適切に捕捉し、ログを記録したり、システム管理者にメールで通知したりする処理を実装することが不可欠です。例えば、Custom Notification(カスタム通知)や Email-to-Case を利用して、エラー発生時にすぐに対応できる仕組みを構築しましょう。
テスト (Testing)
Schedulable Apex のテストは、Test.startTest() と Test.stopTest() を使用して行います。System.schedule は Test.startTest() と Test.stopTest() のブロック内で呼び出す必要があります。Test.stopTest() が実行されると、スケジュールされたジョブが同期的に実行されるため、その結果をアサーション(検証)することができます。
@isTest
global class MySchedulableClassTest {
@isTest
static void testScheduler() {
// テストデータの準備
List<Account> accounts = new List<Account>();
for(Integer i=0; i<10; i++){
accounts.add(new Account(Name='Test Account ' + i));
}
insert accounts;
// Test.startTest() と Test.stopTest() の間でスケジュールを実行
Test.startTest();
MySchedulableClass sched = new MySchedulableClass();
String cronExp = '0 0 1 ? * SAT';
String jobName = 'Weekly Account Update Test';
System.schedule(jobName, cronExp, sched);
Test.stopTest();
// Test.stopTest() の後、スケジュールされたジョブが実行されるため、結果を検証する
List<Account> updatedAccounts = [SELECT Id, Description FROM Account WHERE Name LIKE 'Test Account %'];
for(Account acc : updatedAccounts){
System.assert(acc.Description != null, 'Description should be updated.');
System.assert(acc.Description.startsWith('Batch Processed'), 'Description should start with Batch Processed.');
}
}
}
まとめとベストプラクティス
Schedulable Apex は、Salesforce における時間ベースの自動化を実現するための強力で柔軟なツールです。定期的なデータメンテナンス、レポート作成、システム連携など、その活用範囲は多岐にわたります。この機能を最大限に活用し、安定的でスケーラブルなシステムを構築するために、以下のベストプラクティスを遵守することをお勧めします。
- スケジューラはシンプルに保つ: Schedulable クラスの役割は、処理を「開始する」ことに限定します。実際の複雑なビジネスロジックは、Batch Apex や Queueable Apex、あるいは再利用可能なサービスクラスに実装し、Schedulable クラスから呼び出すだけにしましょう。これにより、関心の分離が促進され、コードの再利用性やテストの容易性が向上します。
- 大量データには Batch Apex を利用する: 100件以上のレコードを処理する可能性がある場合は、ほぼ常に Batch Apex を使用すべきです。これにより、ガバナ制限を回避し、大規模なデータセットに対しても安定した処理が可能になります。
- 設定の外部化: CRON 式や、処理対象を絞り込むための SOQL の WHERE 句などをコード内にハードコーディングするのは避けるべきです。代わりに、Custom Metadata Types(カスタムメタデータ型)や Custom Settings(カスタム設定)を使用して、これらの設定を外部から変更できるようにしましょう。これにより、コードをデプロイし直すことなく、スケジュールの変更やロジックの調整が可能になります。
- 堅牢なエラーハンドリングと監視: 本番環境では予期せぬエラーが発生する可能性があります。
try-catchによる例外処理を徹底し、失敗時にはシステム管理者へ通知する仕組みを必ず実装してください。また、定期的に [設定] > [Apex ジョブ] を確認し、ジョブが正常に実行されているかを監視する運用も重要です。 - べき等性 (Idempotency) を考慮する: べき等性とは、ある操作を1回行っても複数回行っても結果が同じになる性質のことです。何らかの理由でジョブが再実行された場合に、データが重複して作成されたり、意図しない状態になったりしないよう、処理を設計することが望ましいです。
これらの原則に従うことで、あなたは Schedulable Apex を使いこなし、ビジネスの要求に応える効率的で信頼性の高い自動化ソリューションを構築できる Salesforce 開発者となることができるでしょう。
コメント
コメントを投稿