Wednesday 26 January 2022

“Self-Monitoring” Batch-Job

Introduction

ABAPers

have you ever encountered the situation where it might be helpful to monitor running batch jobs via ABAP-Reports (e.g. to get noticed automatically via email when a job fails)?

I want to share some basic ideas/snippets on this topic with the following blog post.

Snippets

It is quite “simple” to retrieve the status of a batch-job via function-module. FM

CALL FUNCTION 'BP_JOB_STATUS_GET'

  EXPORTING

    jobcount                   = p_jbcnt        " Kennummer eines Jobs

    jobname                    = p_jbnam        " Name eines Hintergrundjobs

  IMPORTING

    status                     = job_status     " Zustand eines Batchjobs

...

takes the corresponding job name + job count (ID) as input-parameter and returns the status of the job.

The problem is that the job-ID is unknown in advance. So it is not possible to predefine a report variant which passes the job name/job count to this function module.

But with the help of a Template-Job, which is making use of selection-variables (tvarvc), this lack of information can be overcome in a simple way (at least in my opinion :)).

Template of Job-Monitor

This example of a template for the job monitor is quite simple.

It consists of a single step (Z-Report) which only reads two selection-screen parameters and checks the status of the corresponding batch job in a while loop (until the job is finished or fails).

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Selection-Screen of Job-Monitor

  WHILE job_status <> zcl_job_base=>btc_finished AND job_status <> zcl_job_base=>btc_aborted.
    CALL FUNCTION 'BP_JOB_STATUS_GET'
      EXPORTING
        jobcount                   = p_jbcnt        " Kennummer eines Jobs
        jobname                    = p_jbnam        " Name eines Hintergrundjobs
      IMPORTING
        status                     = job_status     " Zustand eines Batchjobs
      EXCEPTIONS
        job_doesnt_exist           = 1
        unknown_error              = 2
        parent_child_inconsistency = 3
        OTHERS                     = 4.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
    IF job_status = zcl_job_base=>btc_finished.
      "or write an email...
      WRITE:/ |job: { p_jbnam } with Job-ID { p_jbcnt } finished| COLOR COL_POSITIVE.
    ELSEIF job_status = zcl_job_base=>btc_aborted.
      "or write an email...
      WRITE:/ |job: { p_jbnam } with Job-ID { p_jbcnt } aborted| COLOR COL_NEGATIVE.
    ELSE.
      WAIT UP TO p_wait SECONDS.
    ENDIF.
  ENDWHILE.

The corresponding report variant would look like this (It is important to reference Selection-Variables)

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Variant with Selection-Variables

Finally the step list:

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs

Job with “Automatic” Monitoring


A second Z-Report is needed to make use of this Monitor-Template. The task of this report is to retrieve the current batch runtime info (job name/job count) and transfer this information to the previously defined selection-variables. The template job is then copied and released immediately. The result is an independent running monitoring job (of course, the monitoring job could fail as well (seems to be unlikely), but it is not affected by the result of the job which is being monitored). This report needs to run as the first step in order to implement “real-time” monitoring (before any other step is executed). The selection screen of this report looks like this:

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs

First two parameters must match the name of the selection variables defined in the variant of the monitoring report. The third and fourth parameter are used to find and copy our template.

SELECTION-SCREEN BEGIN OF BLOCK bl_sel_par WITH FRAME TITLE TEXT-001.
PARAMETERS: p_jcnt TYPE tvarvc-name DEFAULT 'ZMONITOR_JOBCOUNT' OBLIGATORY,
            p_jnam TYPE tvarvc-name DEFAULT 'ZMONITOR_JOBNAME' OBLIGATORY.
SELECTION-SCREEN END OF BLOCK bl_sel_par.

SELECTION-SCREEN BEGIN OF BLOCK bl_sel_job WITH FRAME TITLE TEXT-002.
PARAMETERS: p_jmsrc TYPE btcjob DEFAULT 'ZJOB_MONITOR' OBLIGATORY MATCHCODE OBJECT z_sh_planned_jobs.
PARAMETERS: p_jmtgt TYPE btcjob DEFAULT 'ZJOB_MONITOR_RUN'.
SELECTION-SCREEN END OF BLOCK bl_sel_job.

AT SELECTION-SCREEN ON p_jmsrc.
**********************************************************************
*** Check Copy Sources for Monitoring Job
  SELECT FROM tbtco AS job_def
       FIELDS COUNT( * ) AS cnt_cpy_srces
        WHERE job_def~jobname EQ @p_jmsrc
          AND job_def~status EQ @zcl_job_base=>btc_scheduled
         INTO @DATA(l_cnt).
  CASE l_cnt.
    WHEN 0.
      MESSAGE |Monitor-Job Copy Source not found!| TYPE 'E'.
    WHEN 1.
    WHEN OTHERS.
      MESSAGE |There are more than 1 Copy Sources| TYPE 'W'.
  ENDCASE.

START-OF-SELECTION.
*** Jobname and Jobcount (->Runtime-Info)
  DATA: jobname  TYPE tbtcm-jobname,
        jobcount TYPE tbtcm-jobcount.
  DATA: selpar_job_cnt  TYPE REF TO zcl_sel_par,
        selpar_job_name TYPE REF TO zcl_sel_par.
  DATA: monitor_job TYPE REF TO zcl_job_mon.
  DATA: enqueue_errors TYPE i.
  CONSTANTS: max_enqueue_errors TYPE i VALUE 5.
**********************************************************************
  IF sy-batch IS NOT INITIAL.
    CALL FUNCTION 'GET_JOB_RUNTIME_INFO'
      IMPORTING
        jobcount        = jobcount
        jobname         = jobname
      EXCEPTIONS
        no_runtime_info = 1
        OTHERS          = 2.
    IF sy-subrc <> 0.
      MESSAGE |Could not obtain Runtime-Info| TYPE 'E'.
    ENDIF.
**********************************************************************
*** Jobcount and Jobname are transferred via Selection-Variables
    TRY.
        selpar_job_cnt = zcl_sel_var_factory=>create_selpar_with_e_lock( p_jcnt ).
        selpar_job_cnt->set_value( jobcount ).
        selpar_job_cnt->save_db( ).
        selpar_job_name = zcl_sel_var_factory=>create_selpar_with_e_lock( p_jnam ).
        selpar_job_name->set_value( jobname ).
        selpar_job_name->save_db( ).
        COMMIT WORK.
      CATCH zcx_enqueue_error INTO DATA(lo_err).   " Kein Treffer bei DB-Abfrage
        enqueue_errors = enqueue_errors + 1.
        IF enqueue_errors <= max_enqueue_errors.
          WAIT UP TO 20 SECONDS.
          RETRY.
        ELSE.
          MESSAGE |Could not perform enqueue: { lo_err->get_text( ) }| TYPE 'E'.
        ENDIF.
    ENDTRY.
*** Copy Monitor-Job and release immediatly

    TRY.
        CREATE OBJECT monitor_job
          EXPORTING
            iv_job_src_name = p_jmsrc.
        "Copy Job to Default-Name
        IF p_jmtgt IS INITIAL.
          monitor_job->copy_to_target_job_dflt_name( ).
        ELSE.
          monitor_job->copy_to_target_job( iv_target_name = p_jmtgt iv_dialog = zcl_job_base=>z_btc_no ).
        ENDIF.
        monitor_job->release_target_job_now( ).
        WRITE:/ |Job-Monitor for Job-Name: { p_jmtgt } released|.
*** dequeue after monitor job is released
        TRY.
            TYPES: range_status TYPE RANGE OF c.
            WHILE monitor_job->get_state_of_target_job( )
                       IN VALUE range_status(
                                  ( option = 'EQ' sign = 'I' low = zcl_job_base=>btc_ready )
                                  ( option = 'EQ' sign = 'I' low = zcl_job_base=>btc_released ) ).
              WAIT UP TO 15 SECONDS.
            ENDWHILE.
          CATCH zcx_job_error. " zcx_job_error
        ENDTRY.
        selpar_job_cnt->dequeue_tvarv( ).
        selpar_job_name->dequeue_tvarv( ).
      CATCH zcx_job_error.
        MESSAGE |Could not release target Job for Job-Monitor| TYPE 'E'.
      CATCH zcx_no_entry_found. " zcx_no_entry_found
        MESSAGE |Copy-Source for Job-Monitor not found| TYPE 'E'.
    ENDTRY.
  ENDIF.

The selection-variables are enqueued and released after the monitor job is active and running in order to prevent unwanted overwriting (which makes this report somehow “thread safe”).

Sample Job


Let’s put this all together. Consider the following step list of a job called ZJOB_WITH_MONITORING:

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Step list sample Job

The second step will make this job fail but let’s take a look after the first step is executed.

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Release of Job with Monitoring

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
First step…

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Template Job is copied

You can see that report ZSUBMIT_JOB_MONITOR has read the current runtime info and has transferred this data to the selection variables:

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Selection variant for zjob_monitor

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Runtime Info of ZJOB_WITH_MONITORING

The job monitor now runs as an independent job which will not be affected by the failing of ZJOB_WITH_MONITORING as you can see in the next screenshot (the monitoring job could fail as well, but I don’t see any obvious reasons for this).

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Failing job but finishing job monitor

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Exam Preparation, ABAP Career, SAP ABAP Skills, SAP ABAP Jobs
Result of monitoring

Source: sap.com

No comments:

Post a Comment