Retrieving entries from a journal is a common practice, done for numerous purposes such as auditing changes to objects, debugging applications, and replaying journal changes. At times it is beneficial to limit the journal entries that are retrieved to only those deposited for a single object or a group of objects. For the most part, specifying objects by name on the journal entry retrieval commands and APIs is a simple matter and will return exactly what you expect. But there are also times when unexpected results occur, particularly when the objects have gone through recent changes such as moves, deletes, restores, and start or end journaling operations. Understanding how the system decides which journal entries to return is key to understanding how to use journal entry retrieval commands and APIs effectively and optimally.
Different interfaces, same results
There are a number of different ways to retrieve journal entries. If you would like to view journal entries interactively, there is the Display Journal (DSPJRN) command. This command can also be used to dump journal entries into an outfile or spooled printer file. The Retrieve Journal Entries (RTVJRNE) command can be used to retrieve journal entires into CL program variables. The Retrieve Journal Entries (QjoRetrieveJournalEntries) API can be used to retrieve journal entries in a program. And the Receive Journal Entries (RCVJRNE) command can be used to continuously receive journal entries from an active journal. These interfaces may have different roles and selection criteria, but they all have the ability to limit journal entry retrieval by object. And in all cases, how the system decides which journal entries to return is the same. For the remainder of this document the DSPJRN command will be used for the descriptions and examples, but the same applies to all commands and interfaces.
To understand how DSPJRN decides which journal entries to return, a basic understanding of journal identifiers (JIDs) is required. Journal entries are often tied to objects; for example, a record update journal entry will be associated with a specific member of a database file. To remember which entry is tied to which object, something needs to be recorded in the journal entry. That something cannot be the object's name beause names can be changed. It also cannot be a pointer because journal receivers can be saved and restored onto different systems, invalidating the pointers. Instead, the journal entries record the JID of the associated object. Every object that has ever been journaled has been assigned a JID. Once an object is given a JID, it retains that JID throughout its existence (with one notable exception that will be discussed later). Even if the object is saved and restored onto another system, it retains its JID.
Along with storing JIDs in journal entries, every journal receiver also maintains a cross-reference listing of object names to JIDs. This cross reference contains the JIDs for every object that was journaled while that journal receiver was attached, along with every name and library name combination associated with every JID. That is, every time a journaled object is moved or renamed, another entry is added to the cross reference to remember the new name and library of the object as well as keeping a record of the old name and library. This history is used when displaying journal entries (look at journal entries before and after renaming an object for an example), and, in some cases, for deciding which objects to retrieve entries for.
Normally, to specify a specific object to retrieve entries for, the object name is entered on the OBJ (objects), FILE (file), or OBJPATH (object path name) parameters. Since journal entries only record JIDs, not object names, the name that was entered on the command must be translated into the appropriate JID. The system does this in a few different ways, depending on whether the journal is a local journal or remote journal and whether or not the specified object exists on the system.
Local journal and the object exists
The simplest case is when the journal is a local journal and the object exists on the system. The operating system looks at the object on the system, retrieves its JID, and then searches for journal entries with that JID.
Normally, this will simply return all the journal entries for that object, as you would expect. However, there are a few unusual cases worth mentioning. Let us say our object, called FILEA, is journaled. FILEA has a JID, let us say JIDA. Suppose the file is deleted and then a new file is created with the same name, FILEA, and starts journaling. Since all JIDs are unique, this new version is given a new JID, JIDB. If we attempt to DSPJRN specifying FILEA, we would only see entries for JIDB, the new version of the file. We would not see any entries for JIDA, even though there used to be an object with that JID, called FILEA.
Another case is the one notable exception to the rule that objects never change JIDs. This time suppose we have a file named FILEX that is journaled with JIDX. After saving the file, we rename it to FILEZ. The file remains journaled and retains its JID, JIDX. We then restore the object from the saved media onto the system. The restored FILEX also has JIDX for its JID and attempts to start journaling. But no two objects journaled to the same journal are allowed to have the same JID, and JIDX is still assigned to FILEZ. So the system assigns FILEX a new JID, JIDY. If we retrieve entries for FILEX, we will only get journal entries deposited for the file after it was restored, because only those journal entries have JIDY. If we want to see older journal entries for FILEX, we need to specify FILEZ.
Local journal and the object does not exist
If the object exists, the operating system retrieves the JID from the object to use for its searching. So what happens when the specified object does not exist? The system goes to the JID cross reference that was mentioned earlier, which contains a mapping of every JID of every journaled object to every name and library those objects had in the range of specified journal receivers. The operating system looks through all specified journaled receivers and finds every occurance of the object name and library specified, and builds a list of JIDs to use for searching. If multiple objects had that name at some time in the journal receiver range specified, then journal entries for all of those objects will be returned.
Note that this behavior is different than the "object exists" case above. Let us revisit the example of FILEA above, where it is first journaled with JIDA, then deleted, then recreated and journaled with JIDB. If we delete FILEA and then retrieve entries specifying FILEA, we will retrieve entries for both versions of the file, the original JIDA version and the new JIDB version. Next let us revisit the case of FILEX journaled with JIDX, where we saved the object, renamed it to FILEZ, then restored FILEX (and it was assigned JIDY). If we delete the new FILEX and then retrieve entries specifying FILEX, we will retrieve entries for both FILEX and FILEZ, since both objects were named FILEX at some point.
Database file members can cause additional confusion when the object does not exist. The default value for the member on the FILE and OBJ parameters is *FIRST. If the file no longer exists, there is no way for the system to know which member was the first member. Attempting to use *FIRST for a file that does not exist yields a CPF7006 -- "Member *FIRST not found." If you want to see the journal entries for the file and one or more of its members, specify a member name or *ALL for the member parameter.
It is worth noting that translating object names into JIDs in this way can be slow. Especially if there are a lot of cross reference entries that need to be searched through; for example, if there are a lot of objects journaled or a lot of journal receivers specified.
The problem with remote journals is that no objects are ever journaled to a remote journal. A remote journal is just a copy of another journal on a different system. So any objects specified on DSPJRN for a remote journal are not likely to be on that system. Even if they are, the objects on the remote system may not have the same JIDs that the objects on the source system do. So for remote journals, the operating system does not attempt to retrieve the JIDs for the specified objects from the objects directly. Rather, it treats the objects as if they do not exist. That is, specifying objects on DSPJRN for remote journals acts the same as specifying objects on DSPJRN for local journals if the specified objects have been deleted.
This means that retrieving journal entries by object name on a remote journal can be slow. The system has to find all applicable JIDs that match that object name from all specified journal receivers.
Retrieving entries by object the fast way
Regardless of journal type and whether or not the specified object exists, the system must find the associated JID (or JIDs) for that object. For a local journal this usually is not a problem. Normally all or most of the specified objects exist, and it does not take long to retrieve the JIDs from the objects. This can be an issue for remote journals, however. Every time you use DSPJRN on a remote journal and you specify one or more objects by name, the operating system needs to do a lot of extra work to turn those object names into JIDs. This can be an issue if you have an application that often retrieves entries by object from remote journals.
New in IBM System i 6.1, there is a "shortcut" parameter to avoid this extra churn. The object JID (OBJJID) parameter allows you to specify JIDs directly. This avoids the work required to find the correct JIDs. The Get Attributes (Qp0lGetAttr) API can be used to retrieve an object's JID. It is also available in the header portion of the journal entries.
Integrated file system objects
Journaled objects in the integrated file system (IFS)--directories, stream files, and symbolic links--are handled differently than other objects. Since path names can be very long, the operating system does not store path names in the cross reference area of a journal receiver. Instead, it stores file identifiers (FIDs) in the JID cross reference of a journal receiver, which are unique identifiers assigned to each object in the integrated file system.
What this means to DSPJRN is that you cannot specify objects that do not exist on the object path name (OBJPATH) parameter. If the specified object does not exist, there is no way of associating that object with a JID. The same is true for remote journals. Since remote journals treat all specified objects as if they do not exist, the OBJPATH parameter has no meaning. Attempting to use OBJPATH on a remote journal results in CPF70A9 -- "OBJPATH parameter not valid for a remote journal.".
The object file identifier (OBJFID) parameter, on the other hand, works the same as the object (OBJ) and file (FILE) parameters. Since a mapping of FIDs to JIDs is stored in the journal receiver, you can specify an FID for an object that has been deleted and the operating system will return entries for that object. You can also use the object JID (OBJJID) parameter, just as for other object types.
The commands and APIs that retrieve journal entries are powerful tools, and the ability to limit the journal entries by specific objects is also very useful. But there are some things to remember when using this functionality. The system acts differently when the specified object exists on the system than when it does not exist on the system, and when the journal is a local journal or a remote journal. Keeping the differences in mind can help explain unexpected results.
This material has not been submitted to any formal IBM test and is published AS IS. It has not been the subject of rigorous review. IBM assumes no responsibility for its accuracy or completeness. The use of this information or the implementation of any of these techniques is a client responsibility and depends upon the client's ability to evaluate and integrate them into the client's operational environment.