Case Study 1: End-of-Day Batch Job Stream

Background

Continental Savings Bank operates a mainframe-based core banking system that processes all account activity through a nightly batch cycle. Every business day at 6:00 PM Eastern, the end-of-day (EOD) job stream is submitted automatically by the bank's job scheduling system (CA-7). The job stream performs five critical steps in sequence:

  1. Backup the current master file to a Generation Data Group (GDG) for daily versioning.
  2. Sort the day's transactions by account number for sequential processing.
  3. Update the account master file with the sorted transactions.
  4. Generate a daily activity report and exception report.
  5. Archive the processed transaction file to tape.

Operations analyst Derek Washington designed the JCL for this job stream in 2023 when the bank migrated from a flat-file-based system to a more robust GDG-based architecture. The JCL demonstrates several key techniques: GDG management, COND/IF-THEN-ELSE conditional execution, cataloged procedures with symbolic parameters, multi-step job flow, and production-quality DD statement coding.

This case study presents the complete JCL, explains each statement and parameter, and walks through several operational scenarios including normal execution, step failures, and restart procedures.

The GDG Base Definition

Before the job stream can use GDG datasets, the GDG base must be defined. This is a one-time setup performed by the storage administrator:

//DEFGDG   JOB (ACCT001),'DEFINE GDG BASES',CLASS=A,
//             MSGCLASS=X,NOTIFY=&SYSUID
//*================================================================*
//* Define GDG bases for daily master file backups and               *
//* transaction archives. LIMIT(30) keeps 30 generations.           *
//*================================================================*
//STEP01   EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DEFINE GDG -
    (NAME(CONT.BANK.MASTER.BACKUP) -
     LIMIT(30) -
     NOEMPTY -
     SCRATCH)
  DEFINE GDG -
    (NAME(CONT.BANK.TRANS.ARCHIVE) -
     LIMIT(60) -
     NOEMPTY -
     SCRATCH)
/*

The LIMIT parameter specifies how many generations to retain. NOEMPTY means that when the limit is reached, only the oldest generation is removed (not all of them). SCRATCH means the dataset is also uncataloged when it rolls off the GDG.

The Complete EOD Job Stream

//EODBATCH JOB (ACCT001),'EOD BATCH CYCLE',CLASS=A,
//             MSGCLASS=X,MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID,
//             REGION=0M,
//             TYPRUN=SCAN
//*================================================================*
//* END-OF-DAY BATCH JOB STREAM                                     *
//* Continental Savings Bank                                         *
//* Submitted daily at 18:00 EST by CA-7 scheduler                  *
//*                                                                  *
//* STEP010 - Backup current master to GDG                           *
//* STEP020 - Sort daily transactions by account number              *
//* STEP030 - Update master file with sorted transactions            *
//* STEP040 - Generate daily reports                                 *
//* STEP050 - Archive processed transactions to GDG                  *
//*================================================================*
//*
//JOBLIB   DD DSN=CONT.BANK.LOADLIB,DISP=SHR
//*
//*================================================================*
//* STEP010: BACKUP CURRENT MASTER FILE TO GDG (+1)                  *
//* Creates a new generation of the master file backup.              *
//* Uses IEBGENER for a simple sequential copy.                      *
//*================================================================*
//STEP010  EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1   DD DSN=CONT.BANK.ACCT.MASTER,DISP=SHR
//SYSUT2   DD DSN=CONT.BANK.MASTER.BACKUP(+1),
//            DISP=(NEW,CATLG,DELETE),
//            DCB=(RECFM=FB,LRECL=500,BLKSIZE=27500),
//            SPACE=(CYL,(50,10),RLSE),
//            UNIT=SYSDA
//SYSIN    DD DUMMY
//*
//*================================================================*
//* STEP020: SORT DAILY TRANSACTIONS BY ACCOUNT NUMBER               *
//* Input: unsorted daily transaction file                           *
//* Output: sorted transaction file for master update                *
//* Sort key: Account number (positions 1-12), then transaction      *
//*           sequence number (positions 13-20) ascending.           *
//*================================================================*
//STEP020  EXEC PGM=SORT,
//         COND=(0,NE,STEP010)
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=CONT.BANK.DAILY.TRANS,DISP=SHR
//SORTOUT  DD DSN=&&SORTED.TRANS,
//            DISP=(NEW,PASS),
//            DCB=(RECFM=FB,LRECL=200,BLKSIZE=27400),
//            SPACE=(CYL,(30,10),RLSE)
//SORTWK01 DD SPACE=(CYL,(20,5)),UNIT=SYSDA
//SORTWK02 DD SPACE=(CYL,(20,5)),UNIT=SYSDA
//SORTWK03 DD SPACE=(CYL,(20,5)),UNIT=SYSDA
//SYSIN    DD *
  SORT FIELDS=(1,12,CH,A,13,8,CH,A)
  SUM FIELDS=NONE
/*
//*
//*================================================================*
//* STEP030: UPDATE ACCOUNT MASTER WITH SORTED TRANSACTIONS          *
//* Executes the COBOL update program ACCTUPDT.                      *
//* Reads sorted transactions and applies to master file.            *
//* Produces updated master and an unmatched transaction file.        *
//*================================================================*
//STEP030  EXEC PGM=ACCTUPDT,
//         COND=(0,NE,STEP020)
//STEPLIB  DD DSN=CONT.BANK.LOADLIB,DISP=SHR
//MASTER   DD DSN=CONT.BANK.ACCT.MASTER,DISP=OLD
//TRANSIN  DD DSN=&&SORTED.TRANS,DISP=(OLD,DELETE)
//NEWMAST  DD DSN=CONT.BANK.ACCT.MASTER.NEW,
//            DISP=(NEW,CATLG,DELETE),
//            DCB=(RECFM=FB,LRECL=500,BLKSIZE=27500),
//            SPACE=(CYL,(50,10),RLSE),
//            UNIT=SYSDA
//UNMATCHED DD DSN=CONT.BANK.UNMATCHED.TRANS,
//            DISP=(NEW,CATLG,DELETE),
//            DCB=(RECFM=FB,LRECL=200,BLKSIZE=27400),
//            SPACE=(CYL,(1,1),RLSE)
//UPDTRPT  DD SYSOUT=*,
//            DCB=(RECFM=FBA,LRECL=133,BLKSIZE=0)
//SYSOUT   DD SYSOUT=*
//*
//*================================================================*
//* STEP035: RENAME DATASETS - Replace old master with new master    *
//* Only executes if STEP030 completed successfully.                 *
//*================================================================*
//STEP035  EXEC PGM=IDCAMS,
//         COND=(0,NE,STEP030)
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DELETE CONT.BANK.ACCT.MASTER PURGE
  ALTER  CONT.BANK.ACCT.MASTER.NEW -
         NEWNAME(CONT.BANK.ACCT.MASTER)
/*
//*
//*================================================================*
//* STEP040: GENERATE DAILY REPORTS                                  *
//*          Uses a cataloged procedure for report generation.       *
//*          Symbolic parameters allow environment flexibility.      *
//*================================================================*
//STEP040  EXEC RPTPROC,
//         ENV='PROD',
//         RPTDATE='2025-01-15',
//         OUTCLASS='A'
//*
//*================================================================*
//* STEP050: ARCHIVE PROCESSED TRANSACTIONS TO GDG                   *
//*          Archives regardless of report step outcome (reports     *
//*          are not critical for data integrity).                    *
//*================================================================*
//STEP050  EXEC PGM=IEBGENER,
//         COND=(0,NE,STEP030)
//SYSPRINT DD SYSOUT=*
//SYSUT1   DD DSN=CONT.BANK.DAILY.TRANS,DISP=SHR
//SYSUT2   DD DSN=CONT.BANK.TRANS.ARCHIVE(+1),
//            DISP=(NEW,CATLG,DELETE),
//            DCB=(RECFM=FB,LRECL=200,BLKSIZE=27400),
//            SPACE=(CYL,(30,5),RLSE),
//            UNIT=SYSDA
//SYSIN    DD DUMMY
//*
//*================================================================*
//* CONDITIONAL CLEANUP: Delete temporary and intermediate files     *
//* Uses IF/THEN/ELSE for clarity.                                   *
//*================================================================*
//         IF (STEP030.RC = 0) THEN
//CLEANUP  EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DELETE CONT.BANK.DAILY.TRANS PURGE
  SET MAXCC = 0
/*
//         ELSE
//NOTIFY   EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  SEND 'EOD BATCH: STEP030 FAILED - REVIEW JOB LOG' +
       USER(OPER01)
/*
//         ENDIF
//*

The Cataloged Procedure

The report generation step (STEP040) invokes a cataloged procedure. This procedure is stored as a member of a procedure library and can be reused across multiple jobs with different parameters:

//*================================================================*
//* RPTPROC - Cataloged procedure for daily report generation       *
//* Stored in: CONT.BANK.PROCLIB(RPTPROC)                          *
//*                                                                  *
//* Symbolic parameters:                                             *
//*   &ENV     - Environment (PROD, TEST, QA)   Default: PROD       *
//*   &RPTDATE - Report date (YYYY-MM-DD)       Default: &LYYMMDD   *
//*   &OUTCLASS - SYSOUT class for reports      Default: A           *
//*================================================================*
//RPTPROC  PROC ENV='PROD',
//             RPTDATE='&LYYMMDD',
//             OUTCLASS='A'
//*
//*---------- STEP 1: Generate Daily Activity Report ---------------*
//RPTSTEP1 EXEC PGM=DAILYRPT
//STEPLIB  DD DSN=CONT.BANK.&ENV..LOADLIB,DISP=SHR
//MASTER   DD DSN=CONT.BANK.ACCT.MASTER,DISP=SHR
//RPTOUT   DD SYSOUT=&OUTCLASS,
//            DCB=(RECFM=FBA,LRECL=133,BLKSIZE=0)
//CSVOUT   DD DSN=CONT.BANK.RPT.DAILY.CSV,
//            DISP=(NEW,CATLG,DELETE),
//            DCB=(RECFM=VB,LRECL=500,BLKSIZE=0),
//            SPACE=(CYL,(5,2),RLSE)
//CTLCARD  DD *
REPORT-DATE=&RPTDATE
REPORT-TYPE=DAILY-ACTIVITY
INCLUDE-ZERO-BALANCE=N
MINIMUM-ACTIVITY=0.01
/*
//SYSOUT   DD SYSOUT=*
//*
//*---------- STEP 2: Generate Exception Report --------------------*
//RPTSTEP2 EXEC PGM=EXCPRPT,
//         COND=(4,LT,RPTSTEP1)
//STEPLIB  DD DSN=CONT.BANK.&ENV..LOADLIB,DISP=SHR
//MASTER   DD DSN=CONT.BANK.ACCT.MASTER,DISP=SHR
//UNMATCHED DD DSN=CONT.BANK.UNMATCHED.TRANS,DISP=SHR
//RPTOUT   DD SYSOUT=&OUTCLASS,
//            DCB=(RECFM=FBA,LRECL=133,BLKSIZE=0)
//CTLCARD  DD *
REPORT-DATE=&RPTDATE
REPORT-TYPE=EXCEPTION
THRESHOLD-AMOUNT=10000.00
FLAG-DORMANT-DAYS=90
/*
//SYSOUT   DD SYSOUT=*
//*
//         PEND

Solution Walkthrough

Job Statement Parameters

The JOB statement controls job-level attributes:

//EODBATCH JOB (ACCT001),'EOD BATCH CYCLE',CLASS=A,
//             MSGCLASS=X,MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID,
//             REGION=0M,
//             TYPRUN=SCAN
  • CLASS=A: Assigns the job to input class A. The system administrator defines which initiators service each class. Class A is typically the production batch class.
  • MSGCLASS=X: Directs the job log output to held output class X, where operations staff can review it.
  • MSGLEVEL=(1,1): The first 1 requests that all JCL statements (including those from procedures) be printed in the job log. The second 1 requests that all allocation messages be printed. This is essential for debugging.
  • NOTIFY=&SYSUID: Sends a completion message to the submitting user's TSO session. The system symbol &SYSUID resolves to the user ID.
  • REGION=0M: Requests the maximum available region size. In production, this might be set to a specific value like REGION=256M.
  • TYPRUN=SCAN: This is included for illustration -- it causes JES to syntax-check the JCL without executing it. In production, this parameter would be removed.

JOBLIB vs STEPLIB

The job uses both JOBLIB and STEPLIB:

//JOBLIB   DD DSN=CONT.BANK.LOADLIB,DISP=SHR

JOBLIB applies to all steps in the job. When a step specifies its own STEPLIB, the STEPLIB overrides JOBLIB for that step only. Derek uses JOBLIB for the default load library and STEPLIB in the procedure steps where the library name includes the environment symbolic parameter (&ENV).

If both JOBLIB and STEPLIB are specified for the same step, STEPLIB takes precedence and JOBLIB is ignored for that step. The system searches STEPLIB first, then the link list libraries, but does not search JOBLIB.

The COND Parameter

The COND parameter controls whether a step executes based on return codes from previous steps:

//STEP020  EXEC PGM=SORT,
//         COND=(0,NE,STEP010)

COND=(0,NE,STEP010) means: "Skip this step if 0 is NOT EQUAL to the return code from STEP010." In other words, skip if STEP010's return code is not zero -- execute only if STEP010 returned RC=0.

The COND parameter is evaluated as: skip the step if the condition is TRUE. This inverted logic is a notorious source of confusion. The test reads as: "If (code) (operator) (return code from step), then BYPASS this step."

COND Parameter Meaning
COND=(0,NE,STEP010) Skip if STEP010 RC is not 0
COND=(4,LT,STEP010) Skip if 4 < STEP010 RC (i.e., RC > 4)
COND=(0,EQ,STEP010) Skip if STEP010 RC = 0 (run only on failure)
COND=EVEN Run even if a previous step abended
COND=ONLY Run only if a previous step abended

IF/THEN/ELSE Construct

The cleanup section demonstrates the IF/THEN/ELSE construct, which is clearer than COND:

//         IF (STEP030.RC = 0) THEN
//CLEANUP  EXEC PGM=IDCAMS
...
//         ELSE
//NOTIFY   EXEC PGM=IKJEFT01
...
//         ENDIF

This reads naturally: "If STEP030 returned 0, run the cleanup; otherwise, send a notification." The IF construct supports relational operators (=, !=, <, >, <=, >=) and logical operators (AND, OR, NOT).

GDG References

GDG datasets are referenced using relative generation numbers:

  • (+1): The next new generation. Used with DISP=(NEW,CATLG) to create a new version.
  • (0): The current (most recent) generation. Used with DISP=SHR to read the latest backup.
  • (-1): The previous generation. Used to access the backup from yesterday.
  • (-2): Two generations back. Used for the backup from two days ago.

During job execution, the relative numbers are resolved to absolute generation numbers (e.g., G0045V00). If two steps in the same job reference (+1) for the same GDG base, they create two different generations, resolved in step order.

Temporary Datasets

The sorted transaction file uses a temporary dataset:

//SORTOUT  DD DSN=&&SORTED.TRANS,
//            DISP=(NEW,PASS)

The && prefix marks this as a temporary dataset. DISP=(NEW,PASS) creates it and passes it to subsequent steps. The consuming step references it with DISP=(OLD,DELETE), which reads it and then deletes it. Temporary datasets are automatically deleted at job completion even if not explicitly deleted.

Symbolic Parameters in Procedures

The cataloged procedure uses symbolic parameters for environment flexibility:

//RPTPROC  PROC ENV='PROD',
//             RPTDATE='&LYYMMDD',
//             OUTCLASS='A'

When invoked, the caller can override any parameter:

//STEP040  EXEC RPTPROC,
//         ENV='PROD',
//         RPTDATE='2025-01-15',
//         OUTCLASS='A'

For the QA environment, the same procedure can be used with:

//STEP040  EXEC RPTPROC,ENV='QA',OUTCLASS='T'

This changes the STEPLIB to CONT.BANK.QA.LOADLIB and routes reports to output class T. The COBOL programs, the JCL structure, and the data flow are identical -- only the environment-specific parameters change.

Operational Scenarios

Scenario 1: Normal Execution

All steps complete with RC=0. The job flow proceeds sequentially: 1. STEP010 backs up the master (RC=0). 2. STEP020 sorts transactions (RC=0, COND satisfied). 3. STEP030 updates the master (RC=0, COND satisfied). 4. STEP035 renames datasets (RC=0, COND satisfied). 5. STEP040 generates reports (RC=0). 6. STEP050 archives transactions (RC=0, COND satisfied). 7. CLEANUP deletes the daily transaction file (IF condition met).

Scenario 2: Sort Step Failure

STEP020 (SORT) abends with S0C7 (data exception): 1. STEP010 completes normally (RC=0). 2. STEP020 abends (RC=S0C7). 3. STEP030 is skipped (COND=(0,NE,STEP020) -- true because STEP020 abended). 4. STEP035 is skipped (depends on STEP030). 5. STEP040 executes (no COND dependency on STEP020). 6. STEP050 is skipped (depends on STEP030). 7. NOTIFY step executes (IF condition: STEP030 did not return 0).

Scenario 3: Restart After STEP030 Abend

If STEP030 abends, the master file was opened with DISP=OLD, which means it might be in an inconsistent state. Restart procedure: 1. Restore the master from the GDG: CONT.BANK.MASTER.BACKUP(0) is the backup created in STEP010. 2. Delete the partially created new master: DELETE CONT.BANK.ACCT.MASTER.NEW. 3. Resubmit the job starting from STEP030, using the RESTART=STEP030 parameter on the JOB statement.

The DCB Parameter

The DCB (Data Control Block) parameter specifies the dataset characteristics:

//         DCB=(RECFM=FB,LRECL=500,BLKSIZE=27500)
  • RECFM=FB: Fixed Block format. Each record is exactly LRECL bytes, and records are packed into blocks of BLKSIZE bytes.
  • RECFM=FBA: Fixed Block with ASA carriage control. The first byte of each record is an ASA print control character.
  • RECFM=VB: Variable Block. Each record has a 4-byte record descriptor word (RDW) containing its length.
  • LRECL=500: Logical record length of 500 bytes.
  • BLKSIZE=27500: Block size of 27,500 bytes (500 * 55 = 55 records per block). Choosing an efficient block size reduces I/O operations. A BLKSIZE of 0 lets the system choose the optimal value based on the device type.

Discussion Questions

  1. The COND parameter uses inverted logic ("skip if true"). The IF/THEN/ELSE construct reads more naturally. Why do many production shops still use COND instead of IF/THEN/ELSE? Consider backward compatibility, shop standards, and tool support.

  2. The job uses DISP=OLD for the master file in STEP030. What are the implications if two instances of this job were accidentally submitted simultaneously? How does DISP=OLD protect against this, and what would happen if DISP=SHR were used instead?

  3. The GDG for master backups has LIMIT(30), retaining 30 daily backups. How would you determine the appropriate LIMIT for a production banking system? What factors influence this decision (regulatory requirements, storage costs, recovery scenarios)?

  4. The cataloged procedure uses the symbolic parameter &ENV to construct dataset names like CONT.BANK.&ENV..LOADLIB. Note the double period after &ENV -- why is this necessary? What would happen with only a single period?

  5. STEP050 archives the raw daily transactions with COND=(0,NE,STEP030), meaning it only runs if the master update succeeded. Argue for or against changing this to run unconditionally. What are the risks and benefits of archiving transactions even if the update failed?

  6. The SORT step uses three SORTWK (sort work) datasets. How does the sort utility use these work areas? What happens if the data volume exceeds the allocated work space? How would you tune the SORTWK allocations for a growing transaction volume?