Monday, May 15, 2017

Why is my RPG program not writing to the file?

rpg output buffer

I have been asked this question a lot recently:

When I am in debug my RPG program does not write to the file. What is wrong?

I have decided to write this post so I can, in the future, refer people here.

Let me give a very simple example RPG program so I can explain what is going on.

01  **free
02  dcl-f TESTFILE usage(*output) ;

03  FIELD = 1 ;
04  write TESTFILER ;

05  *inlr = *on ;

Line 1: This example is written in totally free RPG, but the same is true in any flavor of RPG.

Line 2: My file is defined as output.

Line 3: Move a value to the key field of the file.

Line 4: Write to the file.

I debug the program and add a break point on line 5. When I call the program and it reaches the break point and I look in my output file it is empty (I am not going to show an image of an empty file). Now if I press F12 and let the program continue until it ends, then I look in the file the record is there. Why is that?

Let me start by saying that there is nothing wrong with the program. The slowest part of any program is disk I/O, therefore, many of the programming languages on IBM i use what I know as an output buffer. Records are written to the output buffer, and only when it becomes full, or a RPG program ends with *INLR on, are the records written to disk.

The one record I wrote to the file was not enough to fill the output buffer, therefore, the record was not written to the file. When the program ended with *INLR on, the contents of the output buffer were written to the file.

What happens when a program ends without *INLR being on:

01  **free
02  dcl-f TESTFILE usage(*output) ;

03  FIELD = 2 ;
04  write TESTFILER ;

05  return ;

This program is basically the same as the previous example, except for line 5. The RETURN operation means the program exits, but it is still active in memory. When I look in the file I do not see this record, that is because the program is still active and the output buffer was not full for the record to be written to the file. To write the output buffer to the file I can use the Reclaim Resource command, RCLRSC.

What happens if I absolutely have to write the record to file immediately? I would use the Force End of Data operation, FEOD. The only place I would use it is in a trigger program. This next example contains the FEOD

01  **free
02  dcl-f TESTFILE usage(*output) ;

03  FIELD = 3 ;
04  write TESTFILER ;
05  feod TESTFILE ;

06  *inlr = *on ;

I place the FEOD immediately after the write, this ensures that the output buffer is written to file immediately after the write operation. As I would expect the record does appear in the file immediately after the FEOD occurs.

Another way is to use the Record to Force a Write parameter, FRCRATIO, in the Override Database file command, OVRDBF.

01  PGM

02  OVRDBF FILE(TESTFILE) FRCRATIO(1) OVRSCOPE(*CALLLVL)

03  CALL PGM(TESTPGM2)

04  ENDPGM

Line 2: I am giving the FCRRATIO of one, this means every time there is one or more records in the output buffer they are written to the file.

Line 3: I am calling the second example program, the one with RETURN and no *INLR, to demonstrate that this override has precedence over the RPG program.

As I would expect when this program runs the record is written to the file.

Before people go off and start changing their RPG programs I want to say I am not, I repeat NOT, recommending that they change their RPG programs by inserting FEOD after every write. Leave the programs to use the output buffer, as this is the most efficient way of handling disk I/O. If they were to use FEOD or OVRDBF they will find that their programs become a lot slower and resource intensive, which is not good. The only exception I can think of is in trigger programs, as there I do want to write the trigger data immediately to the file.

If I needed to run debug and have every record written to the file when the Write operation is performed I would use the OVRDBF command. At any command line I would type in the OVRDBF shown below:

  OVRDBF FILE(TESTFILE) FRCRATIO(1) OVRSCOPE(*JOB)

By using the job override scope this override will persist for this job only and not affect any others.

 

This article was written for IBM i 7.3, and will work for earlier releases too.

9 comments:

  1. Adding (n) to FEOD(n) will save a few nanoseconds.

    Or Add Block(*No) to F-Spec. Yes, this is slower but real time.

    Ringer

    ReplyDelete
  2. Clear explanation - J

    ReplyDelete
  3. Add Block(*No) to F-Spec

    ReplyDelete
  4. If you declare the file as *update and *output will work also...

    ReplyDelete
  5. Basically, if you see compiler message "RNF7086 RPG handles blocking for the file." in the spool file, then newly inserted rows could get stuck in the output buffer. You may see this for OUTPUT only files and for files with an F-Spec that the program NEVER references (oops).

    Ringer

    ReplyDelete
  6. Very clear...as always...
    Bien claro...como siempre...

    ReplyDelete
  7. Of the four methods mentioned, I like the "block(*no)" the best. It seems more natural than the other methods.

    I believe FEOD is left over from the cycle and computer card days and it should be deprecated. As I recall, it is used to force the last card out of the card punch without ending the program.

    Also, you didn't use the N modifier "FEOD(N)" which causes the program to pause until the record(s) are physically written to disk, slowing the program down.

    ReplyDelete
    Replies
    1. Sorry Bruce but you are wrong. FEOD was updated with the addition of the (N) extender not that long ago (in RPG terms anyway!).

      The problem with Block(*No) is that all writes are forced to take the longer path and not use the buffer. FEOD on every write would have a similar effect plus it forces the block to DASD.

      FEOD(N) is a different animal though. It ensures that the record buffer is passed back to the OS where it is immediately available to other processes. But it does this without little overhead.

      So - if a series of writes are going to take place within a loop letting RPG buffer the data and adding FEOD(N) at the end of the loop is a perfectly valid and well performing option with no real connection to the cycle - if i had I wouldn't be using it!

      Delete
  8. Saved my day. Was scratching my head for hours why some records are getting skipped in outfile

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.