Restarting Job Queue when it Hits an Error on Dynamics NAV 2013

Job Queue
In Dynamics NAV 2013, you can setup job queue to automate processing of tasks. Popular tasks that are automated are things such as Adjust Cost – Item Entries, EDI processing, Reporting, etc.

Setting up job queue in NAV2013 couldn’t be much easier can a step by step instruction can be found here.

The Problem
When the job queue runs into an error, it will never get picked up again. This means that while the Job Queue is an automated process, the IT manager will need to monitor this every day to make sure every process is running.

I know what you’re thinking, “This does not make sense!”. I fully agree.

There are some processes where the error is fatal. This is the reason why you would not want to have it run again. However, there are some situations where the error occurs when the table is locked by another process, in this case, you absolutely need to have it restart again.

This is especially true when you process EDI orders. You have to send back acknowledgment and/or confirmation within a certain timeframe or else you’ll get charge backs. Having the job queue error out because of table locks does not make too much sense.

The Solution
The problem lies in the Job Queue Dispatcher codeunit (448). If you go down and find the local function GetNextRequest, you’ll see that for some odd reason, the process is only looking at any statuses that are Ready.JobQueueReadySo we will need to modify the code to scan for the error entries as well.

JobQueueReady2

Depending on what you use the Job Queue for, I would include job queues that are In Process. The reason is if it’s running and someone stops the job queue, it’ll stay stuck in the In Process status.

By setting this, it’s important that you set the Max No. of Attempts. You don’t want the job queue to keep running if there really is a critical error.

A special thanks to Rafael Rezende for helping me with this!

14 thoughts on “Restarting Job Queue when it Hits an Error on Dynamics NAV 2013

  1. Jens Glathe says:

    Hi Alex, hi Rafael,

    tanks for sharing this. The interesting part of this behaviour change is that GetNextRequest has always only looked at ready queue entries (just checked in mercurial). I concur that also filtering on “error” would be sensible. The HandleRequest() function of old has reset the status to ready after an unsuccesful attempt (and sufficient retries remaining). As HandleRequest() was timer-based, there was a delay of at least 2 seconds before the next retry. This is effectively the same checking for the error state, without allowing the “unwanted” errors to reoccur.
    The heart of the change appears to be in the completely changed handling of the jobs to run. They run in a concurrent session, and the retries run in the same session consecutively, without much delay, as it seems. And (worse) without logging the errors of the unsuccessful attempts – only the last unsuccessful attempt appears to be logged. Go figure.

    with best regards

    Jens

  2. Rashed says:

    I would say another approach is run schedule a separate job that checks the jobs and changes the status. This way you can control how ofter it runs and when to run and also not to reset the status if it’s the same error more than once or twice etc.

  3. Bruce says:

    Fantastic, thanks for sharing. Shows again how one line of code can make a big difference. I had modified the Job Queue Entry table so that it sends me a mail every time a queue status changes to error. Now with this change I don’t need to freak out every time I receive the email when on holiday!

  4. Dee says:

    Hello Bruce
    Could you please share your code that mails upon error on the job queue. Many thanks!

  5. Morten says:

    Hi, Here is my example where I have added 2 fields to the Jobqueue entry table and page, to allow an email to be sent when job failed.

    Here is the code for the Procedure: UpdateLogEntry

    LOCAL UpdateLogEntry(LogEntryNo : Integer;VAR JobQueueEntry : Record “Job Queue Entry”)

    IF JobQueueLogEntry.GET(LogEntryNo) THEN BEGIN
    JobQueueLogEntry.”End Date/Time” := CURRENTDATETIME;
    IF JobQueueEntry.Status = JobQueueEntry.Status::Error THEN BEGIN
    JobQueueLogEntry.Status := JobQueueLogEntry.Status::Error;
    JobQueueLogEntry.SetErrorMessage(JobQueueEntry.GetErrorMessage);
    //>>AB7.02.01
    IF JobQueueEntry.”Notify On Error” THEN BEGIN
    IF JobQueueEntry.”Notify e-mail” ” THEN
    SMTPMail.CreateMessage(USERID,’error@company.dk’,JobQueueEntry.”Notify e-mail”,STRSUBSTNO(‘JOb Queueø:%1 failedt’,JobQueueLogEntry.Description),
    STRSUBSTNO(‘JobQueue:%1,Start time:%2%3’,JobQueueLogEntry.Description,JobQueueLogEntry.”Start Date/Time”,JobQueueLogEntry.”Error Message”),TRUE);
    SMTPMail.Send;
    END;
    //<<AB7.02.01

    END ELSE
    JobQueueLogEntry.Status := JobQueueLogEntry.Status::Success;
    JobQueueLogEntry.MODIFY;
    END

  6. Pascal says:

    There is another error that this will not pickup. Example a job which runs and usually takes 10 minutes to run.
    when it runs it can sometimes then have the status of “In Progress” but actually be in error. It will have a start time to it but no end time, even several hours later.

  7. Alex Chow says:

    You can change the filter to include in progress as well. I would caution against doing this however. Typically, if a process runs into an error, it will error out on the job queue.

  8. Nik says:

    Thank you guys for the tip.
    Here is a slight modification for NAV 2016 and fix for the Morten’s mod to UpdateLogEntry.

    TAB 472 Job Queue Entry

    50000 Notify on Error Boolean
    50001 Notify E-Mail Text 250
    50002 Run After Error Boolean

    COD 448 Job Queue Dispatcher

    LOCAL GetNextRequest(VAR JobQueueEntry : Record “Job Queue Entry”) : Boolean
    JobQueueEntry.LOCKTABLE;
    JobQueueEntry.SETFILTER(“Expiration Date/Time”,’>%1|%2′,CURRENTDATETIME,CREATEDATETIME(0D,0T));
    JobQueueEntry.SETFILTER(“Earliest Start Date/Time”,'<=%1',CURRENTDATETIME);
    IF JobQueue."Job Queue Category Filter" ” THEN
    JobQueueEntry.SETFILTER(“Job Queue Category Code”,JobQueue.”Job Queue Category Filter”);

    //>>
    //JobQueueEntry.SETRANGE(Status,JobQueueEntry.Status::Ready);
    JobQueueEntry.SETFILTER(Status,’%1|%2′,JobQueueEntry.Status::Ready,JobQueueEntry.Status::Error);
    JobQueueEntry.SETCURRENTKEY(Priority);
    JobQueueEntry.LOCKTABLE;
    //Found := TryFindNextJobQueueEntry(JobQueueEntry) AND (JobQueueEntry.Status = JobQueueEntry.Status::Ready);
    Found := TryFindNextJobQueueEntry(JobQueueEntry) AND ((JobQueueEntry.Status = JobQueueEntry.Status::Ready)
    OR ((JobQueueEntry.Status = JobQueueEntry.Status::Error) AND (JobQueueEntry.”Run After Error”)));
    //<>
    IF JobQueueEntry.”Notify on Error” THEN
    IF JobQueueEntry.”Notify E-Mail” ” THEN
    IF SMTPMailSetup.GET() THEN BEGIN
    SMTPMail.CreateMessage( COMPANYNAME +’ | Job Queue – ‘+ JobQueueEntry.”Object Caption to Run”,
    SMTPMailSetup.”User ID”,
    JobQueueEntry.”Notify E-Mail”,
    STRSUBSTNO(‘Job Queue %1 Failed’,JobQueueLogEntry.Description),
    STRSUBSTNO(‘JobQueue: %1Start time: %2Failed With Error: %3’, JobQueueLogEntry.Description, JobQueueLogEntry.”Start Date/Time”, JobQueueLogEntry.”Error Message”),
    TRUE);
    SMTPMail.Send;
    END;
    //<<
    END ELSE
    JobQueueLogEntry.Status := JobQueueLogEntry.Status::Success;
    JobQueueLogEntry.MODIFY;
    END;

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.