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:
- Backup the current master file to a Generation Data Group (GDG) for daily versioning.
- Sort the day's transactions by account number for sequential processing.
- Update the account master file with the sorted transactions.
- Generate a daily activity report and exception report.
- 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
-
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.
-
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?
-
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)?
-
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? -
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? -
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?