Showing posts with label JCl. Show all posts
Showing posts with label JCl. Show all posts

Saturday, July 25, 2026

𝗔 𝗦𝗶𝗺𝗽𝗹𝗲 𝗧𝗲𝗰𝗵𝗻𝗶𝗾𝘂𝗲 𝘁𝗼 𝗘𝗻𝘀𝘂𝗿𝗲 𝗢𝗻𝗹𝘆 𝗢𝗻𝗲 𝗝𝗼𝗯 𝗥𝘂𝗻𝘀 𝗮𝘁 𝗮 𝗧𝗶𝗺𝗲 𝗶𝗻 𝗮 𝗚𝗿𝗼𝘂𝗽 𝗼𝗳 𝗣𝗮𝗿𝗮𝗹𝗹𝗲𝗹 𝗝𝗼𝗯𝘀

While reviewing our batch processing cycle, I noticed that several jobs contained the following DD statement in their final job step:

//ENQUEUE DD DSN=DUMMY.ENQUEUE.DSN,DISP=OLD

Interestingly, the dataset referenced by this DD statement was completely empty and was not accessed or processed by any program within the job. This raised the question: Why was this dataset included in multiple jobs?

After further analysis, I discovered that this was being used as a simple serialization mechanism. Although these jobs could potentially be scheduled to run in parallel, they were intentionally prevented from doing so to avoid database contention issues.
 
How It Works

The key lies in the DISP=OLD parameter. When a job is selected for execution, z/OS allocates all datasets required by a job step before the step begins execution. Because the dataset DUMMY.ENQUEUE.DSN is requested with DISP=OLD, the system reserves it for exclusive use.

As a result:

  • The first job that acquires the dataset proceeds normally.
  • Any other job that also requests the same dataset with DISP=OLD must wait until the dataset is released.
  • Since the DD statement is present in the last step of each job, the dataset remains allocated until that step completes, effectively ensuring that only one job from the group runs at a time.
This creates a simple enqueue mechanism using dataset allocation.
 
Alternative Approaches
 
Mainframe Job schedulers provide built-in facilities for managing job dependencies, resource constraints, and mutual exclusion requirements. These features are usually more flexible and easier to maintain than relying on dataset allocation techniques.
 
However, the application developers chose to implement serialization using DISP=OLD on a dummy dataset. This may have been due to historical reasons, or perhaps a lack of awareness of the scheduler's resource management capabilities.

Sunday, July 5, 2026

Understanding "Normal Disposition" and "Abnormal Disposition" in the JCL DISP Parameter

 When learning the JCL DISP parameter, beginners often get confused about the terms Normal Disposition and Abnormal Disposition.
 
Every job step that is executed generates "return code". Famous return codes are 0,4,8,12,16..
 
If the job step does NOT abend, "normal dispostion" comes into effect. If the job step generates return code such as 8/12/16(any return code for that matter) still that is considered "normal disposition"
 
IF the job step abends with Sxxx (such as S0C4, SOC7, SB37, SE37, S322 etc) , Uxxx(user abend), "abnormal disposition" comes into effect.
 
 
This can be illustrated with following example
 
//STEP1   EXEC PGM=IDCAMS                                       
//SYSPRINT DD SYSOUT=*                                          
//DD1      DD DSN=USERID.STEP1.DSN1,                           
//            DISP=(NEW,CATLG,DELETE),                          
//            LRECL=80,RECFM=FB,                                
//            SPACE=(TRK,(1,1),RLSE)                            
//DD2      DD DSN=USERID.STEP1.DSN2,                           
//            DISP=(NEW,CATLG,DELETE),                          
//            LRECL=80,RECFM=FB,                                
//            SPACE=(TRK,(1,1),RLSE)                            
//SYSIN    DD *                                                 
 SET MAXCC=16                                                   
//*                                                             
//STEP2   EXEC PGM=SORT                                         
//SORTIN   DD DUMMY,LRECL=80,RECFM=FB                           
//SORTOUT  DD DUMMY,LRECL=80,RECFM=FB                           
//SYSOUT   DD SYSOUT=*                                          
//DD1      DD DSN=USERID.STEP1.DSN2,                           
//          DISP=(OLD,CATLG,DELETE)                             
//SYSIN    DD *                                                 
 INTENTIONALLY FORCING ABEND BY NOT GIVING PROPER CONTROL CARD  
//*                                                             
 
The below JOB Log messages indicates that even thoug STEP1 genarated return code 16, still USERID.STEP1.DSN1, USERID.STEP1.DSN2 datasets are cataloged.
 
IEF142I USERIDS STEP1 - STEP WAS EXECUTED - COND CODE 0016                  
IEF285I   USERID.USERIDS.JOB04476.D0000103.?         SYSOUT                
IGD104I USERID.STEP1.DSN1                           RETAINED,  DDNAME=DD1   
IGD104I USERID.STEP1.DSN2                           RETAINED,  DDNAME=DD2   
 
The below JOB Log messages indicates that since STEP2 abended with abend code U016, USERID.STEP1.DSN2 was deleted as coded in the DISP parameter.
 
IEF472I USERIDS STEP2 - COMPLETION CODE - SYSTEM=000 USER=0016 REASON=00000000    
IEF285I   USERID.USERIDS.JOB04476.D0000104.?         SYSOUT                      
IGD105I USERID.STEP1.DSN2                           DELETED,   DDNAME=DD1   

What happens when you use DUMMY parameter for a dataset

When the DUMMY parameter is specified for a dataset, no disk or tape resources are allocated to that dataset, and no I/O operations are performed against it.

During batch job testing, I frequently use the DUMMY parameter for output datasets that would otherwise contain millions of records and are not required for validation. By eliminating the creation of these unnecessary output files, the job avoids the associated I/O overhead, resulting in shorter execution times of the job

The following example illustrates the behaviour of DUMMY parameter.

Sample job with the DUMMY parameter specified for the output file

//STEP1 EXEC PGM=SMF30ASM    
//STEPLIB  DD DSN=USERID.LOAD,DISP=SHR        
//SYSPRINT DD SYSOUT=*                                          
//SMF30IN DD DISP=SHR,DSN=USERID.WEEKLY.SMF30 
//OUT     DD DUMMY,                   
//        DISP=(NEW,CATLG,DELETE),                          
//        SPACE=(CYL,(50,50,)),
//        DCB=(LRECL=140,RECFM=FB)  
                               
The "EXCP Statistics" section of the JESYSMSG in the job log does not contain an entry for DDNAME OUT. This indicates that no I/O operations were performed against DDNAME OUT.

EXCP Statistics
DDNAME   CC# Unit EXCP Count
STEPLIB      914F         15
SMF30IN   +2 9087      48001
SMF30IN      9186      48001
SMF30IN   +3 9284      23996
SMF30IN   +1 9080      26856
SMF30IN   +1 9187      48001

The above job with the output file defined without the DUMMY parameter
//STEP1 EXEC PGM=SMF30ASM                                
//STEPLIB  DD DSN=USERID.LOAD,DISP=SHR       
//SYSPRINT DD SYSOUT=*                                          
//SMF30IN DD DISP=SHR,DSN=USERID.WEEKLY.SMF30 
//OUT     DD DSN=USERID.SMF30.ASM.OUT,           
//        DISP=(NEW,CATLG,DELETE),                          
//        SPACE=(CYL,(50,50,))              
//        DCB=(LRECL=140,RECFM=FB)  
                           
The "EXCP Statistics" section of the JESYSMSG in the job log shows the number of I/O operations performed against DDNAME OUT.    

EXCP Statistics
DDNAME   CC# Unit EXCP Count
STEPLIB      914F         15
SMF30IN   +1 9187      48001
SMF30IN   +2 9087      48001
SMF30IN      9186      48001
SMF30IN   +3 9284      23996
SMF30IN   +1 9080      26856
OUT          9187      12898
 

Thursday, July 2, 2026

Understanding COND=(0,NE) in JCL

 The COND parameter is often one of the most confusing JCL concepts for beginners because its logic works in a somewhat counterintuitive way.

When you code COND=(0,NE) on a job step, the condition is evaluated against the return codes of all previous steps.

  • If any previous step returns a code other than 0, the condition (0,NE) evaluates to true, and the current step is bypassed (FLUSHED).
  • If all previous steps return 0, the condition evaluates to false, and the current step executes normally.

 In simple terms, COND=(0,NE) means "Execute this step only if all preceding steps completed successfully with RC=0."

Example

//STEP1    EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=
//SYSIN    DD
  SET MAXCC=4
//*
//STEP2    EXEC PGM=IEFBR14
//*
//STEP3    EXEC PGM=IEFBR14,COND=(0,NE)
//*

Execution Results :

Step Name

Return Code

STEP1

04

STEP2

00

STEP3

FLUSH

 

Why Was STEP3 Flushed?

 

  • STEP1 ended with RC=04.
  • Since 04 is not equal to 0, the condition COND=(0,NE) becomes true.
  • As a result, STEP3 is skipped (FLUSHED).

 

Even though STEP2 completed with RC=00, JCL evaluates the condition against all preceding steps, not just the immediately prior step. Because STEP1 returned a non-zero code, STEP3 does not execute.

 

Key takeaway: COND=(0,NE) is commonly used to ensure a step runs only when all earlier steps have completed successfully with a return code of zero. 

Thursday, May 21, 2026

Running a job at a specific time every day without using a job scheduler

This post explains how to automate the repeated submission of a job using JCL, SORT, and a dataset/member where the job is stored.

Sample Job

/MYUSERRR JOB ,'REPEAT',MSGCLASS=F,COND=(4,LT),
//         CLASS=A,REGION=0M,NOTIFY=&SYSUID
//*
// SCHEDULE HOLDUNTL=('05:00','2026/141')
//*
//NEXTJOB  EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DISP=SHR,DSN=MYUSER.JCL(REPEAT)
//SORTOUT  DD SYSOUT=(,INTRDR)
//SYSIN    DD *
    OPTION COPY
    OUTFIL IFTHEN=(WHEN=(1,21,CH,EQ,C'// SCHEDULE HOLDUNTL='),
           BUILD=(1,23,C'05',26,6,DATE3(/)+1,40,41)),
           IFTHEN=(WHEN=NONE,BUILD=(1,80))
/*
//* PUT REST OF JOB AFTER THIS COMMENT

In the above job you have to change the job card to make it work at your installation. Whatever you want the job to do every hour or day or week or month or ... you will have to append in additional steps. The above job will run every day at 5am except for the first time.   

  • If the scheduled time has already passed at submission, the job starts immediately.
  • If the scheduled time is in the future, the job remains in HELD status until the specified time.

Please remember to save the JCL in MYUSER.JCL(REPEAT) before you submit it the first time.

 

So what is actually going on? SCHEDULE makes the job start at the specified time on the specified day. SORT reads the JCL from SORTIN and copies it to SORTOUT which submits it again. During the copy SORT edits the contents of the SCHEDULE card and adds one to the current date (DATE3(/)+1) which delays the execution of the job until next day at 5am. You can choose to add 7 instead if the job is supposed to run once a week or add some other value. The amazing thing is the ability to control the time of day, too. I have just chosen to let the job start at 5am. You can use some of the functionality in the BUILD function in SORT to make the job run every hour or every second hour or whatever you need.

 

Please be aware that when you make changes to the JCL in MYUSER.JCL(REPEAT) it will not take effect until after the next time the job is executed as the job waiting to be executed next time is sitting in the input queue in JES. If you want your change to have immediate effect you must cancel the job in JES and then submit your changed job with a SCHEDULE card holding the time and date for the next execution. The job will survive an IPL so you do not have to worry about that.