Friday, July 10, 2026

Coordinating Db2 and MQ Updates Using RRS in a COBOL Batch Program

When a batch COBOL program performs INSERT, UPDATE, or DELETE operations on a Db2 table and also executes MQPUT operations to an IBM MQ queue, the commit/rollback processing becomes more complex.

In such a scenario, issuing a Db2 COMMIT commits only the Db2 changes; it does not commit the MQ messages. Similarly, issuing MQCMIT commits only the MQ operations and has no effect on pending Db2 updates. Each resource manager commits its own work independently but does not coordinate with other resource managers.

This is where Resource Recovery Services (RRS) becomes essential.

RRS acts as a central transaction coordinator for resource managers such as Db2 and IBM MQ. When an application requests a commit or rollback, RRS coordinates all participating resource managers using the standard two-phase commit protocol, ensuring that updates across all resources are committed or rolled back as a single unit of work.

The following sample COBOL program updates both Db2 and MQ resources and uses the RRS services to commit the changes atomically.

Unlike a traditional batch Db2 COBOL program, an application that uses RRS must explicitly establish and terminate the Db2 connection and thread within the program itself.

When we compile the program, we need to add "ATTACH(RRSAF)" option in the DB2 precompiler step. This instructs Db2 to use the RRS Attachment Facility (RRSAF) for transaction coordination.

After doing all the DB2, MQ updates, COBOL program need to invoke RSS API "SRRCMIT" to initiate the two phase COMMIT process.     

The following stub modules must be link-edited to the COBOL module:

CSQBRSTB - available in MQ SCSQLOAD library
ATRSCSS  - available in SYS1.CSSLIB library
DSNRLI   - available in Db2 SDSNLOAD library

Since the COBOL program explicitly manages the Db2 connection through RRSAF, it can be executed like a regular Batch COBOL program.

Below is the sample run JCL.

//STEP01 EXEC PGM=DB2MQPGM,                              
//STEPLIB  DD DSN=xxxx.DB2.SDSNEXIT,DISP=SHR          
//         DD DSN=xxxx.DB2.SDSNLOAD,DISP=SHR          
//         DD DSN=xxxx.MQ.SCSQANLE,DISP=SHR            
//         DD DSN=xxxx.MQ.SCSQAUTH,DISP=SHR            
//         DD DSN=USER.LOADLIB,DISP=SHR                  
//*                                                      
//SYSOUT   DD SYSOUT=*                                    

Below is the sample COBOL program that invoke RRS services to commit the changes in both DB2 and MQ.

       IDENTIFICATION DIVISION.
       PROGRAM-ID.    DB2MQPGM.
       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01 RRSAF-FIELDS.
      * DB2 subsystem name for IDENTIFY
           05 SSNM          PIC X(04).
      * Correlation ID for SIGNON
           05 CORRID        PIC X(12).
      * Accounting token for SIGNON
           05 ACCTTKN       PIC X(22).
      * Accounting interval for SIGNON
           05 ACCTINT       PIC X(06).
      * DB2 plan name for CREATE THREAD
           05 PLAN          PIC X(08).
      * Collection ID for CREATE THREAD.
      * IF PLAN contains a plan name, not used.
           05 COLLID        PIC X(18).
      * Controls SIGNON after CREATE THREAD
           05 REUSE         PIC X(08).
      *  Action that application takes based
      *  on return code from RRSAF
           05 CONTROL1      PIC X(08).
      ****************** VARIABLES SET BY DB2 *******************************
      *  DB2 startup ECB
           05 STARTECB      PIC X(04).
      *  DB2 termination ECB
           05 TERMECB       PIC X(04).
      *  Address of environment info block
           05 EIBPTR        PIC X(04).
      *  Address of release info block
           05 RIBPTR        PIC X(04).
      ****************************** CONSTANTS ******************************
      * CONTROL value: Everything OK
           05 CONTINUE1     PIC X(08) VALUE 'CONTINUE'.
      * Name of RRSAF servicS
           05 IDFYFN        PIC X(18) VALUE 'IDENTIFY'.
           05 SGNONFN       PIC X(18) VALUE 'SIGNON'.
           05 CRTHRDFN      PIC X(18) VALUE 'CREATE THREAD'.
           05 TRMTHDFN      PIC X(18) VALUE 'TERMINATE THREAD'.
           05 TMIDFYFN      PIC X(18) VALUE 'TERMINATE IDENTIFY'.
           05 XLATFN        PIC X(18) VALUE 'TRANSLATE'.
           05 RETCODE       PIC X(04).
           05 REASCODE      PIC X(04).

       01  WS-WORK-AREAS.
           05  WS-ERROR-CODE            PIC S9(04)  VALUE ZEROS COMP.
           05  PROCESS-REC-COUNT        PIC S9(4) COMP.

       01 WS-SQLCODE                      PIC ----9.
           EXEC SQL
             INCLUDE SQLCA
           END-EXEC.

        01 OUT-MSG-LEN              PIC S9(9) COMP.

        01 MQ-Q-NAME              PIC X(48)      VALUE SPACES.
        01 MQ-HOBJ                PIC S9(9) COMP VALUE 0.
        01 MQ-OPEN-OPTS           PIC S9(9) COMP VALUE 0.
        01 MQ-CLOSE-OPTS          PIC S9(9) COMP VALUE 0.

        01 MQ-CONSTANTS.
            COPY CMQV.
        01 MQ-OBJ-DESC.
            COPY CMQODV.
        01 MQ-MSG-DESC.
            COPY CMQMDV.
        01 MQ-PUT-MSG-OPTS.
            COPY CMQPMOV.
        01 MQ-GET-MSG-OPTS.
            COPY CMQGMOV.

      * -------------------------------------------------------
      * Queue manager connection name and handle...
      * -------------------------------------------------------
        01 MQ-QM-NAME               PIC X(48)      VALUE SPACES.
        01 MQ-HCONN                 PIC S9(9) COMP-5.
        01 MQ-PROC                  PIC X(08).

      * -------------------------------------------------------
      * Queue manager op return codes and like things...
      * -------------------------------------------------------
        01 MQ-RETURN-STUFF.
            10 MQ-RC                PIC S9(9) COMP VALUE 0.
            10 MQ-RC-N9             PIC 9(9)       VALUE 0.
            10 MQ-RSN               PIC S9(9) COMP VALUE 0.
            10 MQ-RSN-N9            PIC 9(9)       VALUE 0.
            10 MQ-MSG               PIC X(256)     VALUE SPACES.

       PROCEDURE DIVISION.
       0000-MAINLINE.

           PERFORM 1000-INITIALIZATION
              THRU 1000-INITIALIZATION-EXIT.

           PERFORM 2000-PROCESS
              THRU 2000-EXIT
             VARYING PROCESS-REC-COUNT FROM 1 BY 1
             UNTIL PROCESS-REC-COUNT > 100

           PERFORM 3000-TERMINATION
              THRU 3000-TERMINATION-EXIT.

       0000-MAINLINE-EXIT.
           GOBACK.

       1000-INITIALIZATION.

           MOVE 'XXXX'     TO MQ-QM-NAME.
           MOVE 'TEST.MQ'  TO MQ-Q-NAME.

           CALL 'MQCONN' USING
                MQ-QM-NAME
                MQ-HCONN
                MQ-RC
                MQ-RSN

           IF MQ-RC = MQCC-FAILED
              MOVE 'MQCONN'    TO MQ-PROC
              GO TO 999-ERROR
           END-IF.

           MOVE MQOO-OUTPUT            TO MQ-OPEN-OPTS
           ADD  MQOO-FAIL-IF-QUIESCING TO MQ-OPEN-OPTS
           MOVE MQOT-Q    TO MQOD-OBJECTTYPE
           MOVE MQ-Q-NAME TO MQOD-OBJECTNAME

           CALL 'MQOPEN' USING
               MQ-HCONN
               MQ-OBJ-DESC
               MQ-OPEN-OPTS
               MQ-HOBJ
               MQ-RC
               MQ-RSN

           IF MQ-RC = MQCC-FAILED
              MOVE 'MQOPEN'    TO MQ-PROC
              GO TO 999-ERROR
           END-IF.

      ****************************** IDENTIFY **********************
           MOVE 'DB2A' TO SSNM
           CALL  'DSNRLI' USING
                  IDFYFN SSNM RIBPTR EIBPTR TERMECB STARTECB
                  RETCODE REASCODE
           IF RETCODE NOT = LOW-VALUES
              DISPLAY IDFYFN RETCODE REASCODE
              CALL  'DSNRLI' USING XLATFN SQLCA RETCODE REASCODE
              MOVE SQLCODE   TO WS-SQLCODE
              DISPLAY ' IDENTIFY ' WS-SQLCODE SQLERRM
           END-IF
      ***************************** SIGNON *************************
           CALL  'DSNRLI' USING
                  SGNONFN CORRID ACCTTKN ACCTINT
                  RETCODE REASCODE
           IF RETCODE NOT = LOW-VALUES
              DISPLAY SGNONFN RETCODE REASCODE
              CALL  'DSNRLI' USING XLATFN SQLCA RETCODE REASCODE
              MOVE SQLCODE   TO WS-SQLCODE
              DISPLAY 'SIGNON    ' WS-SQLCODE SQLERRM
           END-IF
      *************************** CREATE THREAD ********************
           MOVE 'TESTPLAN' TO PLAN
           CALL  'DSNRLI' USING
                  CRTHRDFN PLAN COLLID REUSE
                  RETCODE REASCODE.
           IF RETCODE NOT = LOW-VALUES
              DISPLAY CRTHRDFN RETCODE REASCODE
              CALL  'DSNRLI' USING XLATFN SQLCA RETCODE REASCODE
              MOVE SQLCODE   TO WS-SQLCODE
              DISPLAY 'CRE THRD  ' WS-SQLCODE SQLERRM
           END-IF.

       1000-INITIALIZATION-EXIT.
           EXIT.

       2000-PROCESS.

           EXEC SQL
               INSERT INTO TEST_TABLE
               ..............
               ..............  
           END-EXEC
 
           IF SQLCODE NOT = ZERO
              MOVE SQLCODE   TO WS-SQLCODE
              DISPLAY ' INSERT FAILED ' WS-SQLCODE
              PERFORM 9000-ABEND THRU 9000-ABEND-EXIT
           END-IF.

           COMPUTE MQPMO-OPTIONS
                 = MQPMO-FAIL-IF-QUIESCING
                 + MQPMO-SYNCPOINT

           MOVE LENGTH OF DATA-TO-BE-INSERTED
              TO OUT-MSG-LEN

           CALL 'MQPUT' USING
                MQ-HCONN
                MQ-HOBJ
                MQ-MSG-DESC
                MQ-PUT-MSG-OPTS
                OUT-MSG-LEN
                DATA-TO-BE-INSERTED
                MQ-RC
                MQ-RSN

           IF MQ-RC = MQCC-FAILED
              MOVE 'MQPUT'     TO MQ-PROC
              GO TO 999-ERROR
           END-IF.

       2000-EXIT.
           EXIT.


       3000-TERMINATION.

           CALL 'SRRCMIT' USING RETCODE
           DISPLAY 'SRRCMIT : ' RETCODE
      *    call 'SRRBACK' USING RETCODE
      *    DISPLAY 'SRRBACK : ' RETCODE

           CALL 'MQCLOSE' USING
               MQ-HCONN
               MQ-HOBJ
               MQ-CLOSE-OPTS
               MQ-RC
               MQ-RSN.

           IF MQ-RC = MQCC-FAILED
              DISPLAY 'MQCLOSE'
              DISPLAY 'MQ-RC: ' MQ-RC
           END-IF.

            CALL 'MQDISC' USING
                 MQ-HCONN
                 MQ-RC
                 MQ-RSN.

           IF MQ-RC = MQCC-FAILED
              DISPLAY 'MQDISC'
              DISPLAY 'MQ-RC: ' MQ-RC
           END-IF.

      ************************ TERMINATE THREAD ***********************
            CALL  'DSNRLI' USING TRMTHDFN
                   RETCODE REASCODE
            IF RETCODE NOT = LOW-VALUES
               DISPLAY TRMTHDFN RETCODE REASCODE
               CALL  'DSNRLI' USING XLATFN SQLCA RETCODE REASCODE
               MOVE SQLCODE   TO WS-SQLCODE
               DISPLAY 'TRM THRD  ' WS-SQLCODE SQLERRM
            END-IF.
      ************************ TERMINATE IDENTIFY *********************
            CALL  'DSNRLI' USING TMIDFYFN
                   RETCODE REASCODE.
            IF RETCODE NOT = LOW-VALUES
               DISPLAY TMIDFYFN RETCODE REASCODE
               CALL  'DSNRLI' USING XLATFN SQLCA RETCODE REASCODE
               MOVE SQLCODE   TO WS-SQLCODE
               DISPLAY 'TRM IDNTY ' WS-SQLCODE SQLERRM
            END-IF.

       3000-TERMINATION-EXIT.
           EXIT.


       9000-ABEND.

           CALL 'ILBOABN0' USING WS-ERROR-CODE.

       9000-ABEND-EXIT.
           EXIT.

       999-ERROR.
           MOVE MQ-RSN    TO MQ-RSN-N9
           DISPLAY 'MQ-PROC: ' MQ-PROC
           DISPLAY 'MQ-RSN : ' MQ-RSN-N9
           GOBACK.

Wednesday, July 8, 2026

When a Simple Action Turned into a Major TSO Logon Problem

 One fine day, several Mainframe users found themselves unable to log in to their TSO sessions. The issue appeared in different forms:

  • For some users, the TSO logon JCL failed with JCL errors during the login process.
  • For others, the TSO logon job was being submitted with another user's LOGON PROC, causing the job to fail.

This issue did not affect all Mainframe users; only a subset of users experienced these login problems.

The TSO LOGON PROCLIB contains a dedicated LOGON PROC for each Mainframe user.

Prior to the start of the issue, a member of the Mainframe Infrastructure team compressed the TSO LOGON PROCLIB using CA PDSMAN. This action may have been taken because the PROCLIB was nearing capacity while new user entries were being added.

Following the compression activity, some users began experiencing the TSO logon issues described above.

The question is: How could compressing the TSO LOGON PROCLIB using CA PDSMAN result in these TSO logon failures ?

Further investigation revealed that the installation was using CA PMO within a Sysplex environment consisting of both PROD and DEV LPARs.

One of CA PMO's functions is to cache the directory entries of frequently accessed PDS datasets, enabling faster retrieval of PDS members when they are subsequently  accessed.  

As a result, the directory information for the TSO LOGON PROCLIB was cached in both the DEV and PROD LPARs.

The CA PDSMAN compression job was executed on the DEV LPAR. CA PMO on the DEV system correctly detected the compression activity and refreshed its cache with the updated directory entries for the TSO LOGON PROCLIB.

CA PMO Documentation states PMO/XSYS is required to propagate PDS updates across LPARs. But PMO/XSYS was NOT ACTIVE in both DEV and PROD LPARs

Hence, the PROD LPAR was not aware that the PROCLIB had been compressed. Consequently, the CA PMO cache on the PROD LPAR continued to reference the pre-compression directory entries.

Because PDS compression reorganizes members and updates directory information, the stale CA PMO cache on the PROD LPAR no longer matched the actual state of the PROCLIB. This mismatch caused TSO logon processing to retrieve incorrect or invalid PROC directory information, resulting in symptoms such as:

  • JCL errors during TSO logon.
  • TSO logon jobs being submitted with the wrong user's LOGON PROC.

In summary, the root cause of the issue was a stale CA PMO directory cache on the PROD LPAR following the CA PDSMAN compression of the TSO LOGON PROCLIB in the DEV LPAR. To fix the issue, CA PMO cache in PROD LPAR was refreshed.

Monday, July 6, 2026

How Does the Mainframe Ensure Data Integrity When the Same sequential Dataset, VSAM File, or multiple Generations of GDG Is Accessed Concurrently Across Multiple Systems?

Mainframe systems rely on resource serialization products such as IBM GRS (Global Resource Serialization) and CA MIM (Multi-Image Manager) to enforce exclusive or shared access to resources such as datasets, DASD volumes, and tape drives across multiple mainframe systems, thereby preventing conflicting operations and maintaining data integrity.

To avoid dataset corruption, When a job starts, the initiator places locks(ENQ) on all datasets referenced by the job:
  • Exclusive lock is taken if any step references the dataset with DISP=NEW, MOD, or OLD.
  • Shared lock is taken if all references use DISP=SHR.

The lock remains in place until the last step that references the dataset completes.

For GDGs, locking is applied at the GDG base level:
  • If any step creates a new generation +1 +2, + 3 etc, the GDG base receives an exclusive lock.
  • If all referenced generations use DISP=SHR, the GDG base receives a shared lock.

This lock is released only after the last step referencing that GDG completes, ensuring data integrity.  

This locking (ENQ) is propagated across the entire sysplex. As a result, neither jobs running on the same system nor jobs running on different systems can update the file concurrently.

Case 1 – Shared Lock Followed by Shared Lock

TESTJOBA has already acquired a shared lock on dataset TEST.DATA.SET.
TESTJOBB requests a shared lock on the same dataset.
This request is allowed, so TESTJOBB can proceed without waiting.

Case 2 – Exclusive Lock Followed by Shared Lock

TESTJOBA has already acquired an exclusive lock on dataset TEST.DATA.SET.
TESTJOBB requests a shared lock on the same dataset.
This request is not allowed while the exclusive lock is held. Therefore, TESTJOBB must wait until TESTJOBA releases the lock.

Case 3 – Exclusive Lock Followed by Exclusive Lock

TESTJOBA has already acquired an exclusive lock on dataset TEST.DATA.SET.
TESTJOBB requests an exclusive lock on the same dataset.
This request is also not allowed. As a result, TESTJOBB must wait until TESTJOBA releases its lock before obtaining the exclusive lock.

Example

Consider TESTJOBA with 20 steps:
  • Step 5 creates GDG(+1).
  • Step 10 reads GDG(0).
Because the job creates a new generation, TESTJOBA acquires an exclusive lock on the GDG base before execution begins. The lock remains until Step 10 completes (the last step referencing that GDG).

If TESTJOBB is submitted while TESTJOBA is running and its first step tries to read GDG(0) from the same GDG, it cannot obtain a share lock on the Base GDG. As a result, TESTJOBB waits in dataset contention and starts only after TESTJOBA releases the GDG lock at the end of Step 10.

When a Job Step Dynamically Allocates a Dataset/GDG Generation with DISP=NEW, MOD, or OLD

When a dataset or GDG generation is dynamically allocated, the system first checks whether the job already holds a lock on the dataset or base GDG.

If the job already holds a lock:
  • A shared lock indicates that another step later in the same job references the dataset/GDG generation with DISP=SHR.
  • The system attempts to promote the shared lock to an exclusive lock.
  • If no other job holds a lock on the dataset/base GDG, the promotion succeeds immediately.
  • If another job holds a lock, the current job waits until that lock is released, then acquires the exclusive lock.
  • The lock is retained until the last job step that references the dataset/GDG generation, at which point it is released.

If the job does not already hold a lock:
  • This indicates that no other step in the job references the dataset/GDG generation.
  • If no other job holds a lock on the dataset/base GDG, the job acquires an exclusive lock and releases it at the end of the step.
  • If another job holds a lock, the current job waits until the lock is released, then acquires the exclusive lock and proceeds.

When a Job Step Dynamically Allocates a Dataset/GDG Generation with DISP=SHR

When a dataset or GDG generation is dynamically allocated, the system first checks whether the job already holds a lock on the dataset or base GDG.

If the job already holds a lock:
  • If the existing lock is either a shared lock or an exclusive lock, no additional lock processing is required for the current step.
  • The existing lock remains in effect until it is released according to normal lock management rules.

If the job does not already hold a lock:
  • This indicates that no other step in the job references the dataset/GDG generation.
  • If no other job holds an "exclusive lock" on the dataset/base GDG, the job acquires a "share lock" and releases it at the end of the step.
  • If another job holds a "exclusive lock", the current job step waits until the lock is released, then acquires the "share lock" and releases it at the end of the step.

How locking(ENQ) works for KSDS file

For KSDS files, in addition to the locks acquired based on the DISP parameter specified in the JCL, several other locks are obtained during file OPEN processing. These locks remain in effect for the duration of the file's usage and are released when the file is CLOSED.


A KSDS file with two alternate indexes was defined for testing, as shown below:

USERID.CUST.KSDS      
USERID.CUST.AIX1       
USERID.CUST.AIX2       
USERID.CUST.PATH1
USERID.CUST.PATH2
 
Three tests were conducted to analyze the locking (ENQ) behavior associated with KSDS files and their alternate indexes.
 

Test 1: Locking Behavior of a KSDS File Accessed Through Primary key in INPUT mode
 
The COBOL program contained the following SELECT statement, which referenced only the primary key:

SELECT CUSTFILE                   
    ASSIGN TO CUST                
    ORGANIZATION IS INDEXED       
    ACCESS MODE IS RANDOM         
    RECORD KEY IS PRIMARY-KEY      
 
Before the file was opened by the COBOL program, the following lock was already held:
 
Type Status  Elapsed  Owns Waits Scope   Qname    Rname                                       
SHRD OWNS   46.31031     2       SYSTEM  SYSDSN   USERID.CUST.KSDS                     

When the KSDS file was opened in INPUT mode, additional locks with Qname=SYSVSAM were dynamically acquired. These locks remained active for the duration of the file being open and were automatically released when the file was closed.
 
Type Status  Elapsed  Owns Waits Scope   Qname    Rname                                       
SHRD OWNS   1.404245     2       SYSTEM  SYSVSAM  USERID.CUST.KSDS.INDEXICF.CATALOG.PROD4...I
SHRD OWNS   1.404669     2       SYSTEM  SYSVSAM  USERID.CUST.KSDS.DATAICF.CATALOG.PROD4...I 
SHRD OWNS   1.409698     2       SYSTEM  SYSVSAM  USERID.CUST.KSDSICF.CATALOG.PROD4...N      
SHRD OWNS   00:01:09     2       SYSTEM  SYSDSN   USERID.CUST.KSDS                           

Test 2 : Locking Behavior of a KSDS File Accessed Through Alternate Key-1 in INPUT mode
 
The COBOL program contained the following SELECT statement, which defined both the primary key and an alternate key:

SELECT CUSTFILE                   
    ASSIGN TO CUST                
    ORGANIZATION IS INDEXED       
    ACCESS MODE IS RANDOM         
    RECORD KEY IS PRIMARY-KEY      
    ALTERNATE RECORD KEY IS ALTERNATE-KEY1  

Since the program uses Alternate Key-1, both the KSDS base cluster and the corresponding PATH dataset must be provided in the JCL.
 
Before the file was opened by the COBOL program, the following locks were acquired:
 
Type Status  Elapsed  Owns Waits Scope   Qname    Rname                                       
SHRD OWNS   38.09832     1       SYSTEM  SYSDSN   USERID.CUST.PATH1                          
SHRD OWNS   38.09832     1       SYSTEM  SYSDSN   USERID.CUST.KSDS                           
 
When the file was opened in INPUT mode, additional SYSVSAM locks were dynamically acquired. These locks remained active while the file was open and were released automatically when the file was closed.
 
Type Status  Elapsed  Owns Waits Scope   Qname    Rname                                        
SHRD OWNS   00:02:01     1       SYSTEM  SYSVSAM  USERID.CUST.KSDSICF.CATALOG.PROD4...N      
SHRD OWNS   00:02:01     1       SYSTEM  SYSVSAM  USERID.CUST.KSDS.INDEXICF.CATALOG.PROD4...I
SHRD OWNS   00:02:01     1       SYSTEM  SYSVSAM  USERID.CUST.KSDS.DATAICF.CATALOG.PROD4...I 
SHRD OWNS   00:02:01     1       SYSTEM  SYSVSAM  USERID.CUST.AIX1.INDEXICF.CATALOG.PROD4...I
SHRD OWNS   00:02:01     1       SYSTEM  SYSVSAM  USERID.CUST.AIX1.DATAICF.CATALOG.PROD4...I 
SHRD OWNS   00:02:54     1       SYSTEM  SYSDSN   USERID.CUST.PATH1                          
SHRD OWNS   00:02:54     1       SYSTEM  SYSDSN   USERID.CUST.KSDS                           
 
Test 3 : Locking Behavior of a KSDS File Accessed Through Alternate Key-1 in I-O mode
 
The COBOL program contained the following SELECT statement, which defined both the primary key and Alternate Key-1:
 
SELECT CUSTFILE                   
    ASSIGN TO CUST                
    ORGANIZATION IS INDEXED       
    ACCESS MODE IS RANDOM         
    RECORD KEY IS PRIMARY-KEY      
    ALTERNATE RECORD KEY IS ALTERNATE-KEY1  
 
Since the program accesses the file using Alternate Key-1, both the KSDS base cluster and the corresponding PATH dataset must be specified in the JCL.
 
Before the file was opened by the COBOL program, the following locks were acquired:
 
Type Status  Elapsed  Owns Waits Scope   Qname    Rname                                       
SHRD OWNS   30.81659     1       SYSTEM  SYSDSN   USERID.CUST.PATH1                          
SHRD OWNS   30.81659     1       SYSTEM  SYSDSN   USERID.CUST.KSDS                           

When the file was opened in I-O mode, VSAM dynamically acquired the following SYSVSAM locks. These locks remained in effect until the file was closed, at which point they were automatically released.
 
Type Status  Elapsed  Owns Waits Scope   Qname    Rname                                       
SHRD OWNS   1.715577     1       SYSTEM  SYSVSAM  USERID.CUST.KSDS.INDEXICF.CATALOG.PROD4...I
SHRD OWNS   1.715260     1       SYSTEM  SYSVSAM  USERID.CUST.KSDS.INDEXICF.CATALOG.PROD4...O
SHRD OWNS   1.716168     1       SYSTEM  SYSVSAM  USERID.CUST.KSDS.DATAICF.CATALOG.PROD4...O 
SHRD OWNS   1.716495     1       SYSTEM  SYSVSAM  USERID.CUST.KSDS.DATAICF.CATALOG.PROD4...I 
EXCL OWNS   1.693842     1       SYSTEM  SYSVSAM  USERID.CUST.AIX1.INDEXICF.CATALOG.PROD4...I
EXCL OWNS   1.693445     1       SYSTEM  SYSVSAM  USERID.CUST.AIX1.INDEXICF.CATALOG.PROD4...O
EXCL OWNS   1.694132     1       SYSTEM  SYSVSAM  USERID.CUST.AIX1.DATAICF.CATALOG.PROD4...O 
EXCL OWNS   1.694345     1       SYSTEM  SYSVSAM  USERID.CUST.AIX1.DATAICF.CATALOG.PROD4...I 
EXCL OWNS   1.705077     1       SYSTEM  SYSVSAM  USERID.CUST.AIX2.DATAICF.CATALOG.PROD4...I 
EXCL OWNS   1.704877     1       SYSTEM  SYSVSAM  USERID.CUST.AIX2.DATAICF.CATALOG.PROD4...O 
EXCL OWNS   1.704285     1       SYSTEM  SYSVSAM  USERID.CUST.AIX2.INDEXICF.CATALOG.PROD4...O
EXCL OWNS   1.704550     1       SYSTEM  SYSVSAM  USERID.CUST.AIX2.INDEXICF.CATALOG.PROD4...I
SHRD OWNS   1.722668     1       SYSTEM  SYSVSAM  USERID.CUST.KSDSICF.CATALOG.PROD4...N      
SHRD OWNS   1.688906     1       SYSTEM  SYSVSAM  USERID.CUST.KSDSICF.CATALOG.PROD4...P      
SHRD OWNS   38.02660     1       SYSTEM  SYSDSN   USERID.CUST.PATH1                          
SHRD OWNS   38.02660     1       SYSTEM  SYSDSN   USERID.CUST.KSDS                           

JCL specifies DISP=SHR for both the KSDS base cluster and the PATH dataset, while the COBOL program opens the file in I-O mode.

Under these conditions, when the COBOL program attempts to open the VSAM file for update (I-O mode), the OPEN operation will fail with file status 93 (resource unavailable) if another job or user already has the base cluster or any of its alternate indexes open.

To prevent this situation and ensure exclusive update access, the JCL should specify DISP=OLD for both the KSDS base cluster and the associated PATH dataset. 

JCL specifies DISP=SHR for a sequential dataset, while the COBOL program updates the file in I-O mode.

In this scenario, the initiator acquires a shared ENQ on the dataset during job initialization. When the COBOL program subsequently opens the file in I-O mode and proceeds to update the dataset, the dataset continues to be protected only by the existing shared lock; no exclusive serialization is established.

As a result, another job or user can concurrently open the same dataset in INPUT mode while updates are being performed by the COBOL program. This concurrent access may expose readers to in-flight changes and can lead to data integrity or consistency issues.

To prevent such situations, datasets that are updated by a COBOL program should be allocated with DISP=OLD in the JCL. This ensures exclusive access to the dataset throughout the job step and prevents other jobs or users from accessing the file while update processing is in progress.