Apex scheduled job for loyalty points accrual missed execution windows

We’re experiencing critical issues with our scheduled Apex job that calculates monthly loyalty points accrual for our customer rewards program. The job is scheduled to run daily at 2 AM using System.schedule(), but we’ve noticed it’s been missing execution windows - sometimes skipping 2-3 days in a row. This is causing major customer dissatisfaction as points aren’t being credited properly.

Here’s our current scheduler setup:

LoyaltyPointsScheduler scheduler = new LoyaltyPointsScheduler();
String cronExp = '0 0 2 * * ?';
System.schedule('Daily Loyalty Points', cronExp, scheduler);

We need reliable job execution monitoring and a strategy for catch-up batch processing when executions are missed. The job processes around 50K loyalty accounts per run. Has anyone dealt with Apex scheduler reliability issues in production environments? What’s the best approach for ensuring no customer gets missed in the points calculation?

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.

I’ve seen this pattern before. The Apex Scheduler can miss executions during maintenance windows or when org limits are hit. You should implement a monitoring mechanism to track execution history and detect gaps.

Add a custom object to log each execution with timestamp and records processed. Query CronTrigger and CronJobDetail objects to monitor job status. For missed runs, you’ll need a catch-up mechanism that identifies the gap period and processes those dates specifically. Consider using Database.Stateful in your batch to track progress across chunks. Also check if you’re hitting governor limits - 50K accounts might be pushing boundaries depending on your point calculation complexity.

We had similar issues. Are you checking the Setup Audit Trail for any org maintenance or deployment activities during those missed windows? Salesforce maintenance can pause scheduled jobs. Also verify your job isn’t being aborted due to timeout - check the Apex Jobs page for any failed executions.

For catch-up processing, I recommend maintaining a Last_Processed_Date__c field on your loyalty configuration object. Each successful run updates this field. Then create a separate on-demand batch job that can process date ranges between Last_Processed_Date__c and today. This gives you a manual recovery option when automated runs fail. We also send email notifications when the gap exceeds 24 hours so the admin team can trigger catch-up immediately.

Consider using Platform Events to trigger your loyalty calculations instead of relying solely on scheduled jobs. You can publish events from a more reliable external scheduler (like Heroku Scheduler or AWS Lambda with cron) that calls into Salesforce via REST API to publish the event. This gives you better control and monitoring outside of Salesforce’s scheduled job infrastructure.