Showing posts with label GDG. Show all posts
Showing posts with label GDG. Show all posts

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.

  

Thursday, April 23, 2026

𝗛𝗼𝘄 𝘁𝗼 𝗱𝗲𝗳𝗶𝗻𝗲 𝗚𝗗𝗚𝘀 𝘄𝗶𝘁𝗵 𝗺𝗼𝗿𝗲 𝘁𝗵𝗮𝗻 𝟮𝟱𝟱 𝗴𝗲𝗻𝗲𝗿𝗮𝘁𝗶𝗼𝗻𝘀 𝗮𝗻𝗱 𝗘𝘅𝗽𝗮𝗻𝗱𝗶𝗻𝗴 𝗘𝘅𝗶𝘀𝘁𝗶𝗻𝗴 𝗚𝗗𝗚𝘀 𝗕𝗲𝘆𝗼𝗻𝗱 𝗧𝗵𝗲𝗶𝗿 𝗖𝘂𝗿𝗿𝗲𝗻𝘁 𝗚𝗲𝗻𝗲𝗿𝗮𝘁𝗶𝗼𝗻 𝗟𝗶𝗺𝗶𝘁𝘀

Generation Data Groups (GDGs) can be defined with more than the traditional 255 generations by using the LIMIT parameter in conjunction with the EXTENDED attribute. These parameters control the maximum number of Generation Data Sets (GDSs) that can be associated with a GDG.

Source : https://www.ibm.com/docs/en/zos/3.1.0?topic=dgp-required-parameters
LIMIT(limit)
Specifies the maximum number, from 1 to 255, of GDSs that can be associated with the GDG being defined. If EXTENDED is specified, 1 to 999 GDSs can be associated with the GDG being defined.

Source: https://www.ibm.com/docs/en/zos/3.1.0?topic=dgp-optional-parameters
EXTENDED|NOEXTENDED
Specifies whether the GDG is to be an extended GDGr or not.
     EXTENDED
        allow up to 999 generation data sets (GDSs) to be associated with the GDG.
        Abbreviation: EXT
 
     NOEXTENDED
        allow up to 255 generation data sets (GDSs) to be associated with the GDG. This is the default value.
        Abbreviation: NEXT
 
Sample JCL to define a GDG with 999 Generations
 
//STEP1    EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DEFINE GDG(NAME(USERID.GDG.LIMIT999) -
             EXTENDED                  -
                   LIMIT(999)                -
                   NOEMPTY                   -
                   SCRATCH)
/*
 
Converting a NOEXTENDED GDG to a EXTENDED GDG
 
Source:  https://www.ibm.com/docs/en/zos/3.1.0?topic=generationdatagroup-converting-noextended-extended
 
You can use the following procedure to convert a generation data group defined as NOEXTENDED to one that is EXTENDED. IBM advises that the original user catalog should be backed up before starting the procedure.

  1. Define a temporary user catalog.
  2. REPRO MERGECAT the GDG from the original user catalog to the temporary user catalog. This moves the GDG and all GDSes to the temporary user catalog and removes them from the original user catalog.
  3. DEFINE a new GDG with the same name as the original GDG specifying the EXTENDED parameter and a new LIMIT parameter as desired.
  4. REPRO MERGECAT only the GDSes using wild card notation from the GDG in the temporary user catalog into the new GDG now in the original user catalog.
  5. Use DELETE RECOVERY to delete the temporary user catalog.

The process is complete. You can use LISTCAT to prove that the new GDG has the EXTENDED attribute and that a new LIMIT has been set.

Example JCL and IDCAMS statements:
 
//* GDG NOEXTENDED TO GDG EXTENDED CONVERSION
//*
//CONVERT EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
/* 1. DEFINE A TEMPORARY CATALOG */
 
DEFINE -
UCAT -
(NAME (TEMP.UCAT) STORCLAS(SMSSTORC) -
CYL (1 1))
 
/* 2. REPRO GDG AND GDSES TO THE TEMPORARY CATALOG */
 
REPRO -
INDATASET(ORIG.CAT) -
OUTDATASET(TEMP.UCAT) -
ENTRIES(SMSA.GDG) -
MERGECAT
 
/* 3. DEFINE GDG EXTENDED IN THE ORIGINAL CATALOG */
 
DEFINE GDG (NAME(SMSA.GDG) -
EXTENDED LIMIT(999) SCRATCH PURGE)
 
/* 4. REPRO GDSES BACK TO THE ORIGINAL CATALOG */
 
REPRO -
INDATASET(TEMP.UCAT) -
OUTDATASET(ORIG.CAT) -
ENTRIES(SMSA.GDG.*) -
MERGECAT
 
/* 5. DELETE TEMPORARY CATALOG */
 
DELETE TEMP.UCAT USERCATALOG RECOVERY
/* LISTCAT TO SHOW EVERYTHING BUILT CORRECTLY */
 
LISTCAT ENTRY(SMSA.GDG) ALL
/*
 
 

Wednesday, December 27, 2023

Changing the order of GDG concatenation

GDGORDER keyword specifies the order in which the individual generation data sets (GDSs) will be concatenated.

 

The values of Parameter are USECATLG, LIFO and FIFO.

 

USECATLG - The GDS concatenation is ordered as specified in the GDG data set catalog entry. When the GDGORDER keyword is not specified, the concatenation order from the catalog definition of the GDG data set is used. However, the default setting in the catalog entry for a GDG base data set definition is LIFO, to match the setting for releases prior to z/OS V2.1.

 

LIFO - The GDS concatenation is ordered with the newest GDS defined first and the oldest GDS last.

 

FIFO - The GDS concatenation is ordered with the oldest GDS defined first and the newest GDS last.

 

Let us explore this with an example. Suppose USERID.GDG has three generations as shown below.

 

 BROWSE    USERID.GDG.G0001V00                      Line 0000000000 Col 001 080

 Command ===>                                                  Scroll ===> CSR 

********************************* Top of Data **********************************

GDG1                                                                          

******************************** Bottom of Data ********************************

 

 BROWSE    USERID.GDG.G0002V00                      Line 0000000000 Col 001 080

 Command ===>                                                  Scroll ===> CSR 

********************************* Top of Data **********************************

GDG2                                                                          

******************************** Bottom of Data ********************************

 

 BROWSE    USERID.GDG.G0003V00                      Line 0000000000 Col 001 080

 Command ===>                                                  Scroll ===> CSR 

********************************* Top of Data **********************************

GDG3                                                                          

******************************** Bottom of Data ********************************

 

 

Case 1: When the GDGORDER keyword is not specified

 

The below step copies all the generations of GDG to SORTOUT.

 

//STEP1    EXEC PGM=SORT                             

//SORTIN   DD DISP=SHR,DSN=USERID.GDG                 

//SORTOUT  DD SYSOUT=*                               

//SYSOUT   DD SYSOUT=*                                 

//SYSIN    DD *                                         

 OPTION COPY                                         

//*                

 

Below is the output of the above step. The effect is LIFO

 

 BROWSE - SORTOUT           STEP1    - Page  1      Line  1        Cols 1-80   

 COMMAND ===>                                                SCROLL ===> CURSOR

******************************** Top of Data ***********************************

GDG3                                                                           

GDG2                                                                           

GDG1                                                                           

 ******************************* Bottom of Data ********************************

            

Case 2: When GDGORDER=FIFO

 

The below step copies all the generations of GDG to SORTOUT.

            

//STEP2    EXEC PGM=SORT                             

//SORTIN   DD DISP=SHR,DSN=USERID.GDG,GDGORDER=FIFO   

//SORTOUT  DD SYSOUT=*                               

//SYSOUT   DD SYSOUT=*                                 

//SYSIN    DD *                                         

 OPTION COPY                                         

//*                                                  

 

Below is the output of the above step.

 

 BROWSE - SORTOUT           STEP2    - Page  1      Line  1        Cols 1-80   

 COMMAND ===>                                                SCROLL ===> CURSOR

******************************** Top of Data ***********************************

GDG1                                                                           

GDG2                                                                           

GDG3                                                                           

 ******************************* Bottom of Data ********************************