Learn how to use commitment control in your RPG programs to ensure a complete transaction and how to roll back the changes when unable to complete.
I'm usually talking about powerful IBM i resources that can do great things with minimal work. Commands such as those need to be treated with respect and tested well to ensure that you'll get the expected results. In this article, I'll be taking a step back to talk about some safety measures that you could take to protect the integrity of your data by using commitment control within your code.
Commitment control isn't something I've always used, the reason being that I didn't always use journaling on our files until we got a redundant IBM i that was synchronizing the servers using journaling. But, now that we're journaling without a choice, why not use it to its full potential? Not that I'm saying journaling is a bad thing! It's a great thing!
Journaling
In order to utilize commitment control, you need to journal your file. If you don't journal your file, you won't be able to use any other commitment than *NONE.
So how do you know if your file is being journaled? You could display the object description with the DSPOBJD command as follows:
DSPOBJD OBJ(MYFILE) OBJTYPE(*FILE) DETAIL(*FULL)
Page down a few times and you will find the journaling information that could look similar to Figure 1:
Figure 1: Find journaling information with DSPOBJD. (Click images to enlarge.)
If your file isn't journaled, you could make it be journaled by following these steps:
- Create a journal with the CRTJRN command.
- Create a journal receiver with the CRTJRNRCV command.
- Assign the journal to the file with the STRJRNPF command.
You could also create journals using the System i Navigator. The graphical interface changes frequently, but as of the V7R1 client software, I was able to open the Database Navigator by opening the Databases option, right-clicking on the database name and selecting Database Navigator. From the Map menu bar option, go to Create > Journal, as shown in Figure 2.
Figure 2: Create a journal.
Fortunately for me, journaling is handled automatically. Otherwise, it would be an article in itself. If you need more information on setting up journaling, refer to the IBM i 7.1 Information Center.
Starting and Stopping Commitment Control
To use commitment control within your program, you need to start commitment control prior to opening your file, so if you are going to start commitment control within your RPG programs, you will need to manually open them after you start commitment control. You start commitment control with the STRCMTCTL command.
When using STRCMTCTL, there are some settings you can change. For the purposes of this article, we'll be using the following settings:
- LCKLVL(*CHG) will lock each record that is opened under commitment control and changed, until the transaction is committed or rolled back.
- CMTSCOPE(*ACTGRP) specifies that the commitment control is applicable for the current activation group. Once the activation group is ended, all uncommitted records will automatically be rolled back. You could also set the scope to be by the *JOB level.
After you have completed all of your transactions, you need to end commitment control to release your files. You end commitment control with the ENDCMTCTL command.
Commit and Roll Back
Commitment control uses the journal to record every record that is changed, added, or deleted. When a file is under commitment control, the changes will not become permanent until it is committed. If a complete series of expected transactions is not as you expect them to be, you can roll back the changes (up to the last commit or rollback), and the changes will be put back to the way they were before. If you have commitment control set at a specific level that ends, either deliberately or unexpectedly, the changes will automatically be rolled back.
Suppose you had a process that moved records from one file to another during the billing process to save for historical purposes. Such a process could possibly update several files. For our example, we'll look at a simple one-to-one record move from a WORKING file to a HISTORY file.
This could be a two-step process:
- Delete from the WORKING file.
- Write to the HISTORY file.
If your program were to crash during the write to your HISTORY file, the billing record could be lost.
Another option would be to switch the order:
- Write to the HISTORY file.
- Delete from the WORKING file.
If your program were to crash during the delete and you ran the process again, you could now accidentally duplicate the record and bill your customer twice.
The best option is to ensure that both actions are always performed completely or not at all, which is the purpose of how we'll be using commitment control.
I like to start and end commitment control in a CL program that will call the RPG program as follows:
PGM
/* **************************************************************** */
/* CRTBNDCL PGM(MYLIB/MCP050CL) SRCFILE(MYLIB/QCLSRC) */
/* DFTACTGRP(*NO) ACTGRP(*NEW) */
/* **************************************************************** */
/* IF YOU DON'T START COMMITMENT CONTROL PRIOR TO OPENING THE FILES */
/* IN THE RPG PROGRAM, YOU WILL GET AN ERROR OPENING THE FILES. */
/* **************************************************************** */
STRCMTCTL LCKLVL(*CHG) CMTSCOPE(*ACTGRP)
CALL PGM(MCP050RPG)
ENDCMTCTL
/*------------------------------------------------------------------*/
END: ENDPGM
This is a nice, simple CL program with a call to an RPG program for the purpose of starting and ending commitment control. From the comments, you can see that I've created the CL program to be within a *NEW activation group every time it is called, and the commitment scope is at the *ACTGRP level, so all commits and/or rollbacks will be guaranteed to be completed when the CL program exits. Even if the program dies or the job gets ended, you can still be assured that all transactions will be committed or rolled back. There are other ways of using commitment control, but this is the case that we're discussing in this article.
Now that the CL is taking care of starting and ending commitment control, we'll write the RPG program that we'll use to read through all of the records in the WORKING file and safely push them into the HISTORY file. Typically, in a situation like this, there would be numerous files involved that could all be under commitment control to ensure every transaction is guaranteed to be complete. And it would also be typical to perform computations on the data prior to writing it into the HISTORY file that would change it from its original value in the WORKING file. But, to keep the program simple, I am merely pushing the data from one file to the other.
H DFTACTGRP(*NO) ACTGRP(*CALLER)
H INDENT('.') OPTION(*NODEBUGIO: *SRCSTMT) DATFMT(*ISO)
H Text('Commitment Control - Tom Snyder, MCPressOnline.com')
******************************************************************
* How To Compile:
* 1) Create the Program
* CRTBNDRPG MODULE(MYLIB/MCP050RPG) SRCFILE(MYLIB/QRPGLESRC)
******************************************************************
FWORKING UF E K DISK commit
FHISTORY IF A E K DISK commit
D*
/free
setll *START WORKING;
read WORKING;
dow NOT %eof(WORKING);
// Copy Fields, Delete Work, Write Hist
monitor;
HISTORNO = WORKORNO;
HISTITEM = WORKITEM;
HISTPRIC = WORKPRIC;
HISTQTY = WORKQTY;
delete WORKREC;
write HISTREC;
commit;
on-error;
rolbk;
leave;
endmon;
read WORKING;
enddo;
*inlr = *ON;
/end-free
The logic in this program is extremely simple, on purpose, to demonstrate the usefulness of commitment control. The program reads through the WORKING file, from the beginning to the end. During each read, it attempts to delete from the WORKING file and write to the HISTORY file.
If both operations are successful, the commit command will be executed after the operations and it will continue reading to the end of the file.
If a problem were to occur prior to executing the commit command, the monitor block would exit to the on-error section and the code would be rolled back.
If the job ends abnormally, or is forced to end prior to the execution of the commit, then the uncommitted transactions would automatically be rolled back, even though the rollback command was not executed.
Commit or Rollback All Records at Once
If you were to remove the commit and rollback commands from the RPG code completely in the example above, then all of the transactions would remain uncommitted until the ENDCMTCTL command was executed in the CL command. If you were to do it this way, you would be presented with options to Commit, Rollback, or Cancel.
- Cancel keeps the records uncommitted and locked.
- Rollback resets the records to before you started.
- Commit applies the updates to the files.
Embedded SQL
The above example is not using embedded SQL, but if you were to use embedded SQL, then you would specify the commitment control information during compile time.
Download the Code
You can download the code used in this article by clicking here.
References
ILE Programmer's Guide—Commitment control concepts
IBM i 7.1 Information Center—Setting up journaling
LATEST COMMENTS
MC Press Online