Any company who has used an IBM i, or any other server, find that over time the amount of free disk space becomes an issue. Most of the reduction in free disk space can probably be explained by the amount of data being added to files. This makes a need for regular data purges a necessity. But how many also consider purging unused objects from their servers?
As part of regular maintenance of the applications I am responsible for I purge objects that have not been used in the past two years. I do this for two reasons:
- If I need to make a mass change I only have to be concerned with objects that are being used.
- By removing objects that are not used the size of the library is kept to a minimum. Therefore, the time to save the library as part of the daily save is kept to a minimum.
Once I identify an object that has not been used in that time I move it from the "live" library to an "archive" library, if there is a source member for the object I move it to the a source file in the "archive" library too.
When the purging has been completed the "archive" library can be backed up to tape, and then the library can be deleted from the IBM i. The tape of the save of the "archive" library is stored in a fire proof safe, or equivalent, on site. If any of the purged objects are needed they can be restored from the tape.
So how do you identify when objects were last used?
The 'Display Object Description', DSPOBJD, command contains all the information I need and much more. When I run the command to output to an output file, then I can run reports over the file for the information I want.
Display Object Description (DSPOBJD) Type choices, press Enter. Object . . . . . . . . . . . . . > *all Library . . . . . . . . . . . > lib1 Object type . . . . . . . . . . > *all + for more values > Detail . . . . . . . . . . . . . > *BASIC ASP device: Device . . . . . . . . . . . . > * Search type . . . . . . . . . > Output . . . . . . . . . . . . . > *OUTFILE File to receive output . . . . . @dspobjd Library . . . . . . . . . . . qtemp Output member options: Member to receive output . . . *FIRST Replace or add records . . . . *REPLACE
I am only interested in a few of the fields from the output file, these being:
|ODCRTU||Object created by|
|ODCDAT||Object creation date|
|ODUDAT||Last used date|
Note: I am in the USA and the two date fields are in MMDDYY format. I do not know if format of those fields is hard coded or mirrors the format from the system value QDATFMT. If you are not in the USA check the format.
My current responsibilities include a good number of libraries. I leave any library provided by our ERP vendor alone. I only scrutinize all of the home grown software libraries. As there are so many libraries I could create a CL program that look like below:
01 PGM 02 DCLF FILE(QTEMP/TESTPF) 03 CHGVAR VAR(&LIB) VALUE('LIB_1') 04 CALLSUBR SUBR(SUBR01) 05 CHGVAR VAR(&LIB) VALUE('LIB_2') 06 CALLSUBR SUBR(SUBR01) . . /*=========================================*/ 90 SUBR SUBR(SUBR01) 91 DSPOBJD OBJ(&LIB/*ALL) OBJTYPE(*ALL) + OUTPUT(*OUTFILE) + OUTFILE(QTEMP/@DSPOBJD) + OUTMBR(*FIRST *ADD) 92 ENDSUBR /*=========================================*/ 99 ENDPGM
Lines 3 and 4 are repeated for each library, changing the library name in the CHGVAR command.
You can learn more about executing subroutines by reading Subroutines in CL.
I recommend that this program run in batch, as it could take some time to complete depending upon the number of libraries and the number of objects in each one.
As the date is not in an useful format and there is a lot of additional fields in the file that I do not need for this task I use an RPGLE program to write the fields I want, and format the date fields, into another file. Below is the source for that phyiscal file:
A R LASTUSEDR * A LIB 10A TEXT('Library') COLHDG('Library') A OBJ 10A TEXT('Object') COLHDG('Object') A OBJTYPE 8A TEXT('Object type') COLHDG('Object' 'type') A OBJATTR 10A TEXT('Object attribute') COLHDG('Object' 'attribute') A CRTDTE L TEXT('Creation date') COLHDG('Creation' 'date') A CRTUSER 10A TEXT('Created by user') COLHDG('Created' 'by user') A LASTDTE L TEXT('Last used date') COLHDG('Last used' 'date') A OBJTXT 50A TEXT('Text description') COLHDG('Text description')
The RPGLE program is simple:
01 F@DSPOBJD IF E DISK 02 FLASTUSED O E DISK /free 03 dow (1 = 1) ; 04 read @DSPOBJD ; 05 if (%eof) ; 06 leave ; 07 endif ; 08 LIB = ODLBNM ; 09 OBJ = ODOBNM ; 10 OBJTYPE = ODOBTP ; 11 OBJATTR = ODOBAT ; 12 CRTUSER = ODCRTU ; 13 OBJTXT = ODOBTX ; 14 CRTDTE = %date(ODCDAT:*mdy0) ; 15 test(de) *mdy0 ODUDAT ; 16 if (%error) ; 17 clear LASTDTE ; 18 else ; 19 LASTDTE = %date(ODUDAT:*mdy0) ; 20 endif ; 21 write LASTUSEDR ; 22 enddo ; 23 *inlr = *on ;
Note: If the format of the date in ODCDAT and ODUDAT is not MMDDYY, see previous Note, then lines 14, 15, and 19 will need to be changed to be appropriate date format code.
If the object has never been used the field ODUDAT is blank, which is not a valid date. Therefore, I perform a TEST(DE) operation to validate ODUDAT, line 15, and if the date is invalid I CLEAR the date field LASTDTE, line 17. When a date field is CLEARed it is initialized with the date 0001-01-01, therefore, if LASTDTE = 0001-01-01 then I know that the object has never been used.
I can now take the file LASTUSED and use it to search for objects that have not been used in two years.
Here's a gotcha. When a library is moved from one IBM i to another all of the 'Last Used date' fields are set to blank. If you are moving from an older IBM i to a newer one run the DSPOBJD on the old IBM i the day everything is moved to the new server. That way you have a last snapshot of when the objects were used.
You can learn about the DSPOBJD command from the IBM website here.
This article was written for IBM i 7.1, and it should work with earlier releases too.