jueves, 1 de marzo de 2012

Setting up a One-Time Batch Job via X++


Here's the premise.  We are inserting some kind of transaction records and need to generate an alert if a collection of entries arrive that are of interest to a group of users.  Adding code to the XTrans.insert() method is our first step but spamming the users with multiple alerts is not satisfactory and so instead we generate a one time batch job to create just one message containing a list of these transaction.  The job will have a delay of 30 seconds before firing (say) but as new transactions arrive we will reset the launch time to another 30 seconds if we find that there is an existing job still pending execution.  In a high usage Ax installation the delay from 30 seconds would be reduced or quite simply this whole approach would be inappropriate.

I've taken code from the Setting up Batch jobs using X++ blog post and modified it so that the job launches only once.  I've also added code examples of terminating the job via the other two alternative methods (no end date or end by date).  Use of the SysRecurrence class is recommended to update the associated periodic data but note that the recurrence data is a container and therefore the setter functions in this class will return a new container.

A word of warning: debugging the server side batch job is well documented and well tricky!

static server void Tutorial_RunBaseTest(Args _args)
{
    BatchHeader         header;
    SysRecurrenceData   sysRecurrenceData;
    Batch               batch;
    BatchJob            batchJob;
    BatchInfo           processBatchInfo;
    BatchRetries        noOfRetriesOnFailure = 4;
    Tutorial_RunbaseBatch accAlerts_rbb;
    #define.timeInSecondsDelay(30)
    ;

    // Create the Tutorial_RunbaseBatch job, only if one does not exist
    select forupdate batch
        join batchJob
        where batchJob.RecId == batch.BatchJobId
                && batch.ClassNumber == classnum(Tutorial_RunbaseBatch)
                && batchJob.Status == BatchStatus::Waiting
                && batch.Company == curext();
    if (!batch)
    {
        // Setup the RunBaseBatch Job
        header = BatchHeader::construct();
        accAlerts_rbb = Tutorial_RunbaseBatch::construct();
        processBatchInfo = accAlerts_rbb.batchInfo();
        processBatchInfo.parmRetriesOnFailure(noOfRetriesOnFailure);
        header.addTask(accAlerts_rbb);
        // batchJob.OrigStartDateTime = DateTimeUtil::addSeconds(DateTimeUtil::utcNow(), #timeInSecondsDelay);

        // Set the recurrence data
        sysRecurrenceData = SysRecurrence::defaultRecurrence();
        // Start time {now + 30 seconds}
        sysRecurrenceData = SysRecurrence::setRecurrenceStartDateTime(sysRecurrenceData, DateTimeUtil::addSeconds(DateTimeUtil::utcNow(), #timeInSecondsDelay));
        // No end date
        //sysRecurrenceData = SysRecurrence::setRecurrenceNoEnd(sysRecurrenceData);
        // Finish after X times
        sysRecurrenceData = SysRecurrence::setRecurrenceEndAfter(sysRecurrenceData, 1);
        // End by {now + 1 day}
        //sysRecurrenceData = SysRecurrence::setRecurrenceEndAfterDate(sysRecurrenceData, DateTimeUtil::date( DateTimeUtil::addDays(DateTimeUtil::utcNow(), 1) ));

        header.parmRecurrenceData(sysRecurrenceData);
        // Set the batch alert configurations
        header.parmAlerts(NoYes::No, NoYes::Yes, NoYes::No, NoYes::Yes, NoYes::No);
        header.save();
    }
    else
    {
        // Push the exec time back 30 seconds - Avoid spamming the users with multiple warnings.
        ttsbegin;
        select forupdate batchJob
            join batch
            where batchJob.RecId == batch.BatchJobId
                && batch.ClassNumber == classnum(Tutorial_RunbaseBatch)
                && batch.Company == curext();

        sysRecurrenceData = batchJob.RecurrenceData;
        // Start time {now + 30 seconds}
        sysRecurrenceData = SysRecurrence::setRecurrenceStartDateTime(sysRecurrenceData, DateTimeUtil::addSeconds(DateTimeUtil::utcNow(), #timeInSecondsDelay));
        batchJob.RecurrenceData = sysRecurrenceData;
        batchJob.OrigStartDateTime = DateTimeUtil::addSeconds(DateTimeUtil::utcNow(), #timeInSecondsDelay);
        batchJob.update();
        ttscommit;
    }

    // Eliminate previous Cancelled and Finished jobs
    ttsbegin;
    while select forupdate batchJob
        join batch
            where batchJob.RecId == batch.BatchJobId
                && batch.ClassNumber == classnum(Tutorial_RunbaseBatch)
                && (batchJob.Status == BatchStatus::Finished
                    || batchJob.Status == BatchStatus::Canceled)
                && batch.Company == curext()
    {
        batchJob.delete();
    }
    ttscommit;
}

No hay comentarios:

Publicar un comentario