Your current setup lacks several critical components for production reliability. Let me address all three key aspects you need:
Apex Scheduler Reliability Enhancement:
First, implement defensive scheduling with a monitoring wrapper. Create a custom metadata type to store execution history:
Loyalty_Job_Run__mdt.getInstance('Last_Run').Execution_Time__c
In your schedulable class, check the last run timestamp and implement a self-healing mechanism that reschedules if the gap exceeds expected intervals.
Job Execution Monitoring:
Build a comprehensive monitoring solution:
public class LoyaltyJobMonitor {
public static void logExecution(String status, Integer recordsProcessed) {
Loyalty_Execution_Log__c log = new Loyalty_Execution_Log__c(
Execution_Date__c = System.now(),
Status__c = status,
Records_Processed__c = recordsProcessed
);
insert log;
}
}
Schedule a separate monitoring job that runs every 6 hours to query execution logs and alert if gaps exist. Query CronTrigger to verify your job is still scheduled: `SELECT Id, State, NextFireTime FROM CronTrigger WHERE CronJobDetail.Name = ‘Daily Loyalty Points’
Catch-up Batch Processing:
Implement a date-range aware batch class:
public class LoyaltyPointsCatchupBatch implements Database.Batchable<sObject>, Database.Stateful {
private Date startDate;
private Date endDate;
private Integer totalProcessed = 0;
public LoyaltyPointsCatchupBatch(Date start, Date end) {
this.startDate = start;
this.endDate = end;
}
public Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([
SELECT Id, Total_Points__c, Last_Activity_Date__c
FROM Account
WHERE IsActive__c = true
AND Last_Points_Calculation__c < :endDate
]);
}
public void execute(Database.BatchableContext bc, List<Account> scope) {
// Calculate points for date range
for(Account acc : scope) {
acc.Total_Points__c += calculatePointsForPeriod(acc.Id, startDate, endDate);
acc.Last_Points_Calculation__c = endDate;
}
update scope;
totalProcessed += scope.size();
}
public void finish(Database.BatchableContext bc) {
LoyaltyJobMonitor.logExecution('Catchup Complete', totalProcessed);
// Send notification email
}
}
For your 50K accounts, use batch size of 200 to stay within governor limits. Create a scheduled job that runs every 12 hours to check for gaps and automatically trigger catch-up batches when needed.
Implement idempotency in your point calculation logic - use a junction object to track which date ranges have been processed for each account. This prevents double-counting if catch-up runs overlap.
Finally, add this to your deployment checklist: after any production deployment, verify scheduled jobs are still active by querying CronTrigger. We’ve seen cases where deployments inadvertently remove scheduled jobs if they’re not included in the deployment package.
This comprehensive approach ensures reliability through redundancy, monitoring, and automatic recovery mechanisms.
This draft is based on general Salesforce knowledge. It has not been verified against your specific version and environment. Practitioners: verify the steps and share your experience below.