2

I recently came across a salesforce package that uses the ghost user to schedule a process during the install/upgrade. One of the things it does is it creates records.

I've been trying to replicate this behaviour, but have so far had no luck. I can get it to schedule the class, and it seems to run fine (no errors anywhere) but it never creates any records. Tried making everything global, using without sharing, tried standard salesforce objects (Accounts and Opportunities) and tried creating records for an object within it's own package, but I get nothing.

To confirm i've not missed anything obvious, I even have it creating a single record when it initially runs the post install handler.

global without sharing class PostInstallHandler implements InstallHandler {
    global void onInstall(InstallContext cx) {
    //Everything in this method works
        System.schedule('Ghost User Job 1', '0 15 * * * ?', new T());
        System.schedule('Ghost User Job 2', '0 30 * * * ?', new T());
        System.schedule('Ghost User Job 3', '0 45 * * * ?', new T());
        System.schedule('Ghost User Job 4', '0 0 * * * ?', new T());                      
        System.schedule('Ghost User Job 5', '0 0 * * * ?', new B());                      
        System.schedule('Ghost User Job 6', '0 15 * * * ?', new B());                      
        System.schedule('Ghost User Job 7', '0 30 * * * ?', new B());                      
        System.schedule('Ghost User Job 8', '0 45 * * * ?', new B());      
        insert new Log__c(Description__c = 'Installed');         
    }
    
    global without sharing class T implements Schedulable{
        global void execute(SchedulableContext sc) {
        try {
            //Did this incase there was some issue with the ghost user being the owner of the opp record
            if([Select Id from User where Id ='0050N000007AVORQA4'].size() > 0) {
                Insert new Opportunity(Name='Async Opp', StageName='Prospect', Amount=500, CloseDate=Date.Today(), OwnerId='0050N000007AVORQA4');
            } else {
                Insert new Opportunity(Name='Async Opp', StageName='Prospect', Amount=500, CloseDate=Date.Today());            
            }
            }catch(Exception e) {
            //I never see this
                insert new Log__c(Description__c = e.getMessage());
            }


        }
    }
    
    global without sharing class B implements Schedulable{
        global void execute(SchedulableContext sc) {
        //This should always create a new record, but never does.
                insert new Log__c(Description__c = 'Successful');
        }
    }
}

This code is so basic, it wouldn't look out of place in a "How too dummies" book, but it just doesn't work. Am I missing something?

To clarify

  • There is nothing stopping the records being created in the target org. I confirm this by running the code in dev console
  • I'm full sys admin
  • It's a managed package that i'm creating. I then use an enterprise sandbox to test it in.
  • Logs are non existent. Can't add trace flags to ghost user, as it doesn't really exist. No error emails (even with "Notify on apex error" set in the package)
5
  • 2
    In our experience (we are an ISV and have an AppExchange managed package) we find that (at least when the package is installed for all users) scheduled jobs are actually owned by the admin user who performed the installation rather than some "ghost user". The issues we have seen are when that user account is later deactivated or downgraded from being an admin to a standard user - the scheduled jobs stop working in the former case and are run with the wrong permissions in the latter case.
    – Phil W
    Commented Aug 11, 2020 at 13:56
  • Does this answer your question: salesforce.stackexchange.com/questions/32607/… (note that it mentions the "cascading" of the install user context for batches and scheduled jobs in the answer).
    – Phil W
    Commented Aug 11, 2020 at 14:00
  • Unfortunately not. The scheduled process is definitely running under the ghost user and not as myself (The name of the ghost user in the scheduled jobs list is the same name as the package, and if you try drill into the user record, it tells you Insufficient Privileges"). I understand that user context will be cascading, but if the PostInstallHandler can create the records during it's onInstall method, then by that logic, it should still have the same permission to create the records when running the scheduled process.
    – Xander
    Commented Aug 11, 2020 at 14:04
  • I'm hoping to have a call with the dev team that created the package where I saw this being done to see if we can replicate it. They have managed to do this, but have a different set of issues further down the line.
    – Xander
    Commented Aug 11, 2020 at 14:05
  • Good luck and remember to post an update.
    – Phil W
    Commented Aug 11, 2020 at 14:08

1 Answer 1

0

So I managed to get this working. For the ghost user to be able to do dml on a scheduled basis, it needs to start a queueable job from the scheduled job. Doing DML in the scheduled job causes no errors, but never succeeds. Works fine if the scheduled job starts a queueable job to do everything.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .