0

I have created Queueable class to process data and making the callouts this Queueable class executes upon after update and after insert of Opportunity object.

Now in order to track the status of Queueable class I am inserting the status of job into another object i.e. LogInfo object. The purpose of this object is to keep track of jobs, status and error if any.

When the queueable gets executed i do insert the status of it using following code

Execute method of Queueable class

 public void execute(QueueableContext context) {


     SomeHandler.insertStatus(context.getJobId());

 }

 class SomeHandler {

    public Id insertStatus(Id jobId){


     AsyncApexJob apexJob = [SELECT Id ,Status,CompletedDate,ExtendedStatus, 
     NumberOfErrors,JobItemsProcessed,TotalJobItems,CreatedBy.Email 
                                FROM AsyncApexJob 
                                WHERE Id = :jobId];

     LogInfo__c logInfoObj = new LogInfo__c();
     // add job id 
     logInfoObj.JobId__c = jobId;
     // update status
     logInfoObj.JobStatus__c = apexJob.Status;



    }

 }

SomeHandler.insertStatus is being used by multiple Queueable classes , now the issue is so far I am able to record only Processing status as all calls to insertStatus are being made within the exectue method and what I really want to record is the Completed status. However, Queueable class does not have the finish method we do not have any space or hook where in we can record the Queueable class Completed status. In the end we waned to display the real-time dashboard of how many jobs are in the queue and how many are being processed , failed, and completed.

Solutions did not work so far

  1. Can not create a trigger on AsyncApexJob object
  2. Can not use Transaction Finalizers as it is a pilot and available for specially-configured scratch orgs
5
  • why not a near-real time dashboard using a scheduled job that runs every minute and that queries AsyncApexJob? – cropredy May 4 '20 at 1:25
  • Yes , but I have to maintain the history of jobs as well because AsyncApexJob only store record of last 24 hours. Moreover, scheduled job minimum run Is of 1 hour and to set it run for every 1 min I have to run 60 jobs – Hunt May 4 '20 at 3:16
  • The scheduled job can chain itself with a 1 minute displacement; you can also save the query results in a custom object – cropredy May 4 '20 at 3:20
  • How is that and won't this approach create more jobs in Salesforce org – Hunt May 4 '20 at 3:25
  • 1
    Use a chained queueable then. SFDC will auto back off the chain to a one minute interval after the fourth item – cropredy May 4 '20 at 5:08
1

It looks like you are trying to create a apex jobs view which is available out of the box with with new apex jobs interface.

If you want to do this manually there are two options:

  1. Put you queue able code in try/catch and update the job status to complete at the end of the queuable as last line in try block or as a failure in the catch block . It may not work if the errors are non catch-able like limit exceptions.
  2. Write a batch which runs every 15 minutes, queries the job status for records in log info and updates them.
5
  • Apex job Interface are you referring to? – Hunt May 3 '20 at 6:57
  • In setup > Apex jobs – manjit5190 May 3 '20 at 6:57
  • Even if the code is in finally it is still in execute method hence technically job is still running so option one won't help and for option two minimum schedule apex duration is of 1 hour so to schedule job after every 15 mins , I have to schedule 4 jobs , which isn't a good solution – Hunt May 3 '20 at 7:24
  • You could use Transaction finalizers but they are a pilot feature. – manjit5190 May 3 '20 at 7:28
  • Yes hence I can't use it – Hunt May 3 '20 at 7:34
0

In spring 20 new feature is introduced by salesforce System.finalizer interface. You can use that interface that is an awesome feature. Refer to This Code for reference:

Release Note: https://releasenotes.docs.salesforce.com/en-us/spring20/release-notes/rn_apex_Transaction_Finalizers.htm

AccountUpdateLoggingFinalizer.Cls

    public class AccountUpdateLoggingFinalizer implements Finalizer {
    // Used to maintain progress
    List<String> acctNames;

    public AccountUpdateLoggingFinalizer() {
        acctNames = new List<String>();
    }

    public void execute(FinalizerContext ctx) {        
        Id parentQueueableJobId = ctx.getAsyncApexJobId();
        System.Debug('Executing Finalizer that was attached to Queueable Job ID: ' + parentQueueableJobId);
        if (ctx.getAsyncApexJobResult() == FinalizerParentJobResult.SUCCESS) {
            // Queueable executed successfully
            System.Debug('Parent Queueable (Job ID: ' + parentQueueableJobId + '): completed successfully!');
        } else {
            // Queueable failed
            // Log some additional information.
            System.Debug('Parent Queueable (Job ID: ' + parentQueueableJobId + '): FAILED!');
            System.Debug('Parent Queueable Exception: ' + ctx.getAsyncApexJobException().getMessage());

            // Show the accounts that were processed before Queueable Job encountered the exception            
            System.Debug('Parent Queueable processed following accounts:');
            for (String acctName : acctNames) {
                System.Debug(acctName);
            }
        }        
    }

    public void reportProgress(Account acct) {
        acctNames.add(acct.Name);
    }
}

AccountUpdateQueueable.cls --- Example to use interface

public class AccountUpdateQueueable implements Queueable {

    public void execute(QueueableContext ctx) {

        // Create a transaction finalizer
        AccountUpdateLoggingFinalizer finalizer = new AccountUpdateLoggingFinalizer();

        // Attach the transaction finalizer to this queueable
        System.attachFinalizer(finalizer);

        // Do some (partial) work
        Account acct = new Account();
        acct.Name = '1st Account';
        insert(acct);

        // Send some status update to the finalizer
        finalizer.reportProgress(acct);

        // do some work that results in an unforeseen, uncatchable exception
        someWork();

        // Attempt to do some more work
        Account acct2 = new Account();
        acct2.Name = '2nd Account';
        insert(acct2);

        // Report more progress
        finalizer.reportProgress(acct2);
    }

    private void someWork() {
        // regular implementation that could result in an un-catchable
        // exception e.g. System.LimitException due to CPU usage over limits

        // for demonstration, try to enqueue 2 jobs so this method results in
        // System.LimitException because more than one job cannot be enqueued
        // from a Queueable
        System.enqueueJob(new FollowupActionQueueable());
        System.enqueueJob(new FollowupActionQueueable());
    }
}
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.