[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

How to use the recovery class (class LineDB)



This email explains how to use the LineDB class for recovery after crashes.
QE is currently using it, so for more info, have a look at the QE code
(qe_i.cpp and estimator_i.cpp).

The usage consists of a few parts:
1. creation
2. recovery
3. logging of state

the two first ones are done once
the third one is done whenever the state of your object(s) change(s)

Here I only show how to use the parts of LineDB that are necessary to do
any recovery at all. There are some other functions that may be useful to 
some people:
	virtual void replace_line(char* key, char* line);
	virtual void remove_line(char* key);
	virtual char* get_line(char* key);
	virtual char* get_key(char* line);
	virtual bool lookup(char* key);
These are documented in linedb.cpp (in CVS/common).

Good luck,
	 - Henrik

Note: if there is an error anywhere, a char* is thrown. This might not be
the best choice, but that's the way it is for now. (I want to build a good
exception hierarchy in the future (R.S.N.))

// global variable
LineDB *g_db=0;	// LineDB object to handle recovery after a QE crash

*******************
in the qe factory (estimator_i.cpp)

in main()
{
// ...
	//
	//	Set up the recovery facility.
	//
	try
	{
		if( config["Recover"] )
		{
			g_db = new LineDB( config["QERecoveryFile"], true);
			estimator->recover_state();
		}
		else 
		{
			//
			// even if we don't want to recover previous info
			// the next time we might, so we create a recovery
			// file, but we don't read the old recovery file
			//
			g_db = new LineDB( "QERecoveryFile.txt", false);
		}
	}
	catch(char const* s)
	{
		cerr << "Failed to create recovery object: " << s << endl;
		REASON;
		return -1;
	}
// ...
}

void smEstimator::recover_state()
{
	for(LineDB::const_iterator it=g_db->begin(); it!=g_db->end(); ++it)
	{
		++nToken;
		qe* pqe = new qe(0);
		pqe->recover_state((*it).second);
		m_qe_list.push_back( std::make_pair(pqe->m_token, pqe) );
	}
}

********************
in class qe (qe_i.cpp)

//
//	log_state() uses class LineDB to maintain recovery information that would
//	be used after a crash (not that it will ever be used ;-)
//
void qe::log_state()
{
	char s[4096];
	char components[4096]="";
	for(std::vector<char*>::iterator it=m_components.begin();
it!=m_components.end(); ++it)
	{
		if( it!=m_components.begin() ) strcat(components,":");
		strcat(components,*it);
	}
	sprintf(s,"%s;%d;%s;%s;%s;",m_token,m_state,m_clause,components,m_uid);

	try
	{
		g_db->add_line(strnewdup(m_token),strnewdup(s));
	}
	catch(const char* s)
	{
		cerr << s << endl;
		REASON;
	}
}

//
//	recover_state() might look slightly complicated, this is mainly because
//	we are recovering other types than just ordinary char* string,
//	such as CORBA::string, int and std::vector<char*>
//
void qe::recover_state(const char* line)
{
	if( m_clause ) { delete[] m_clause; m_clause = 0; }
	if( m_token ) { delete[] m_token; m_token = 0; }

	char* str=strnewdup(line);
	char* str_copy=str;	// make a copy of str so we can modify str, but still free
memory at the end
	char* components=0, *uid=0, *state=0;

	//
	//	get_token() returns 0 if it fails to get a token
	//	str is modify on each call to point to one character after the
	//	token it returns. This is to skip the separation character (';' in this
case)
	//	m_token, state etc. are 0 on entry, which tells get_token() to do the
	//	memory allocation (using new).
	//
	if( !get_token( str, m_token, ";") ||
		!get_token( str, state, ";") ||
		!get_token( str, m_clause, ";") ||
		!get_token( str, components, ";") ||
		!get_token( str, uid, ";") )
	{
		cerr << TOKEN << "ERROR: Failed to recover information in \"" << line << "\""
<< endl;
		delete[] str_copy;
		delete[] state;
		delete[] components;
		delete[] uid;
		return;
	}

	m_state = (SM_STATE_T)atoi(state);

	if( m_uid ) CORBA_string_free(m_uid);
	m_uid = CORBA_string_dup(uid);
	delete[] state;
	delete[] uid;

	for(std::vector< char* >::iterator it=m_components.begin();
it!=m_components.end(); ++it)
		delete[] *it;
	m_components.clear();

	char* components_copy=strnewdup(components);
	for(char*cmp=0; get_token(components,cmp,":"); ) m_components.push_back( cmp );

	if( g_verbose ) 
		cout << TOKEN << ": successfully recovered: clause= " << m_clause << 
			", components= " <<	components_copy << ", uid= " << m_uid << endl;

	delete[] str_copy;
	delete[] components_copy;
}
_________________________________________
Henrik Nordberg       <hnordberg@lbl.gov>
Scientific Data Management Research Group
Lawrence Berkeley National Laboratory