How to Retrieve Data from Journals for Killed global in CACHE

Primary tabs

Hi,

We are using CACHE 2017.2.1, I would like to retrieve data from Journal for killed global. Let say we have global name ^ EMP(123) with data and also have some child nodes and it has been killed by using cache kill command for some reason and we don't know who has executed this and when. My questions are below.

1) Can we get back the data of killed global from journal files,Is it possible or not ?

2) If above question is Yes, then how to find that specific journal file, which has the global which has been killed?. I am assuming do we have to search journal files created on that date or date range.

 

 

 

Replies

Muni, 

First of all, you have to copy all of your journals, you currently have, so, they will not be purged by schedule.

Next, it depends, on how that data was deleted. If it happened in the transaction, so, you are lucky. It will be possible to restore that data, if not, again depends on how it was killed. Just Kill ^Global,  outside of the transaction, just kills it, with no useful information for restore.

I think you can actually do what you with ^ZJRNFILT. But I prefer direct access to journals through their API, in %SYS.Journal classes.

Hi Dmitriy,

Thanks for your response. I will check with the information provided by you and let you know.

Muni,

This can be difficult and the details of what the data is and how it was killed are very important.

You may be able to accomplish what you need via ^ZJRNFILT as Dmitry mentioned.  But that requires you to find where the KILL is in the journal files, and determine how to filter out the KILL without filtering other updates that are required.  Commonly this requires replaying ALL journals from the time of the event forward, to avoid stale data replacing current data. So the longer you wait, the more journals you have to replay.

If the global contains information that has not changed in a long time, then that stable data is probably not in the journal files anymore.  You would need to restore from a backup in that case.

Erik

I've had a few times where I've needed to do a targeted restore based on a journal (e.g., restoring a week of work an intern accidentally reverted; this would work for class definition changes if you could find the right window). Just to add to what Dmitriy and Erik have said, assuming your case is eligible, here's a code sample using the %SYS.Journal classes (modified from one of the times I had to do this):

Class DC.Demo.JrnFix
{

/// Intended to be run from terminal. Find the right values to put in the variables at the top first.
/// Also, use at your own risk.
ClassMethod Run()
{
    // Path to journal file (find this based on timestamps)
    Set file = "/path/to/journal/file"
    
    // Path to database containing data that was killed
    // (assuming killed during transaction so individual nodes are journalled as ZKILL)
    Set dbJrn = "/path/to/database/directory/"
    
    // First problem offset/address (find a real value for this via management portal or further
    // %SYS.Journal scripting - e.g., output from below with full range of addresses used)
    Set addStart = 0
    
    // Last problem offset/address (find a real value for this via management portal or further
    // %SYS.Journal scripting - e.g., output from below with full range of addresses used)
    Set addEnd = 1000000000
    
    // Global that you're looking to restore - as much of the global reference as is possible
    Set global = "MyApp.DataD"
    
    Set jrn = ##class(%SYS.Journal.File).%OpenId(file)
    
    #dim rec As %SYS.Journal.SetKillRecord
    
    TSTART
    Set rec = jrn.GetRecordAt(addEnd)
    Do {
        If ((rec.%IsA("%SYS.Journal.SetKillRecord"))&&(rec.DatabaseName=dbJrn)) {
            If (rec.GlobalNode [ global) {
                w rec.Address,!
                Set @rec.GlobalNode = rec.OldValue
            } Else {
                // Keep track of other globals we see (optional)
                Set skippedList($p(rec.GlobalNode,"(")) = ""
            }
        }
        Set rec = rec.Prev
    } While (rec.Address > addStart)
    ZWrite skippedList
    Break //At this point, examine things, TCOMMIT, and quit if things look good.
    TROLLBACK
}

}