Регистрация  |  Вход

Используем Batch и Scheduler

Бывает полезно что бы некоторый наш  код (метод) выполнялся по определенному расписанию, для этого в SalesForce есть встроенный инструмент Scheduler.





Примечание:

Scheduler - внутренний планировщик задач в SalesForce CRM. Основная суть в следующем: мы создаем временную точку по определенным правилам и вызываем некоторый необходимый код каждый раз, когда наше время совпадает с этой временной точкой (это может быть как разовое событие, так и постоянно повторяющееся).

 

Поставим задачу:

Необходимо сделать Batch, который будет запускаться раз в сутки, просматривать все Discount и устанавливать им соответствующие статусы. Если скидка еще не начала действовать, то Not started, если действует то Active, если период действия закончился, то Ended.

 

Примечание:

Batch - это некоторый асинхронно выполняющийся код. К примеру: надо поменять статусы всех дисконтов, а их на орге очень много. Если просто так все их вытянуть из базы, то свалимся по лимитам, либо на количество записей либо на ЦПУ тайм при их обработке. А в Batch можно отправлять данные заданными порциями, например по 200 записей за раз, которые потом в методе execute будут обрабатываться. Например если у вас 1000 дисконтов, а вы задали размер пачки 200, то запустится 5 Batch-ей,  которые выполнят вашу логику асинхронно.

 

Перейдем к реализации нашей задачи:

Что бы создать наш Scheduler нам необходимо создать класс, где мы будем его реализовывать, для этого перейдем в раздел:

Setup / Build / Develop / Apex Classes

Жмем на кнопку "NEW" и в появившемся окне пишем, к примеру, следующее:
// класс должен быть глобальным или публичным, что бы был доступен в других местах системы

global class checkDiscountsStatusScheduler implements Schedulable {

// здесь мы задаем временную точку (ниже будет подробнее про это)
public static String CRON_EXP = '0 0 13 * * ?';

global void execute(SchedulableContext ctx) {
// получаем cron trigger
CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE Id = :ctx.getTriggerId()];
// можно выполнять так же необходимы проверки
System.assertEquals(CRON_EXP, ct.CronExpression);
System.assertEquals(0, ct.TimesTriggered);

// вызываем некоторый метод (см. ниже)
checkDiscountsStatus();
}

// метод, в котором вызываем Batch
public void checkDiscountsStatus() {
ChangeDiscoutsStatus myBatch = new ChangeDiscoutsStatus();
Database.executeBatch(myBatch , 100);
}
}

Таким образом мы создали наш планировщик и вызвали в нем необходимый нам Batch. Как видно из примера Batch - это тоже класс, в котором реализована некоторая логика.

 

Примечание:

временная точка. показанная в коде выше состоит из следующих параметров:

Секунды Минуты Часы День_месяца Месяц День_недели Год

Вы наверняка заметили, что я не везде указал конкретные числа а еще использовал некоторые символы. Вот вам шпаргалка, что бы было понятнее, какие символы где использовать и что они в себе несут:

ched1   ched2

 

Теперь создадим еще один класс и реализуем в нем указанный выше ChangeDiscoutsStatus класс, я добавил в него следующую логику:
global class ChangeDiscoutsStatus implements Database.Batchable<sObject>{


// Создаем необходимые переменные
String query;
String email;
Id toUserId;
Id fromUserId;

// strat: объявляем начало операции
global Database.querylocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}

// execute: выполняем основные действия
global void execute(Database.BatchableContext BC, List<sObject> scope){
List<Discount__c> discounts = new List<Discount__c>();
for(sObject s : scope){
Discount__c disc = (Discount__c)s;
if( Date.today() < disc.StartOfDiscount__c ){
disc.Status__c = 'Not Started';
discounts.add(disc);
}
else if( (disc.StartOfDiscount__c <= Date.today()) && (Date.today() < disc.EndOfDiscount__c) ){
disc.Status__c = 'Active';
discounts.add(disc);
}
else if ( Date.today() >= disc.EndOfDiscount__c){
disc.Status__c = 'Ended';
discounts.add(disc);
}
}
update discounts;
}

// finish: чем будем заканчивать действия
global void finish(Database.BatchableContext BC){
// new message
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(new String[] {email});
mail.setReplyTo('roman.bazylev.vrp@gmail.com');
mail.setSenderDisplayName('Batch Processing');
mail.setSubject('Batch Process Completed');
mail.setPlainTextBody('Batch Process has completed! All discounts received their status!');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}

Теперь у нас есть и планировщик, с временной точкой и батчер, который будет выполнять соответствующий код.  (В примере я запускаю выполнение по условию поставленному выше).

 

Запустим наш Scheduler через Developer Console, для этого перейдем в раздел:
ваше_системное_имя (см. правый верхний угол) / Developer Console /  --- жмем CTRL + E

 

В нашем окне пишем следующий код (скрипт для запуска нашего Scheduler-ра):
// Создаем экземпляр класса нашего Scheduler-класса

checkDiscountsStatusScheduler m = new checkDiscountsStatusScheduler();

// Инициализируем необходимые параметры
String seconds = '0'; //Execute at Zero Seconds
String minutes2 = String.valueOf(system.now().addMinutes(2).minute()); //Execute at every 10th minute of hour
String hours = '*'; // Execute Every Hour
String dayOfMonth = '*'; // Execute Every Day of the Month
String month = '*'; //Execute only in November(11)
String dayOfWeek = '?'; //Execute on all 7 days of the Week
String year = '*'; //Execute only for some year

// формируем временную точку
String sch = seconds + ' ' + minutes2 + ' ' + hours + ' ' + dayOfMonth + ' ' + month + ' ' + dayOfWeek + ' ' + year;

// Запускаем планировщик, на вход: название, которое дадим ему, временная точка, экземпляр класса
system.schedule('setDiscountsStatusScheduler', sch, m);


Если у вас код прошел хорошо, то можно для системы посмотреть, есть ли среди всех назначенных планировщиков созданный нами, для этого идем в этот раздел:
Setup / Monitor / Jobs / Scheduled Jobs

Здесь мы видим следующее:

ched3:

Значится наш планировщик запущен.

 

Важно:

Если вы выполнили все действия указанные выше и захотели подредактировать ваш  Scheduler, то вы получите ошибку. сообщающую о том, что ваш планировщик находится на исполнении, что бы редактировать его, вам необходимо остановить ваш планировщик, отредактировать, затем заново запустить.

 

Резюмируем:

Если вам необходимо что то выполнять по расписанию - то используем Scheduler и Batch (что бы не свалится по лимитам и выполнять операции заданными порциями).

 

Благодарность:

спасибо Рустаму за помощь.