2010-11-17 22:31:38 -05:00
// @file dur_recover.cpp crash recovery via the journal
/**
* Copyright ( C ) 2009 10 gen Inc .
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "pch.h"
# include "dur.h"
2011-03-11 16:57:18 -05:00
# include "dur_stats.h"
2010-12-21 18:11:45 -05:00
# include "dur_recover.h"
2010-11-17 22:31:38 -05:00
# include "dur_journal.h"
2010-11-26 18:18:24 -05:00
# include "dur_journalformat.h"
# include "durop.h"
2010-11-23 14:13:46 -05:00
# include "namespace.h"
2010-11-23 10:12:31 -05:00
# include "../util/mongoutils/str.h"
2010-12-19 21:16:37 -05:00
# include "../util/bufreader.h"
2011-07-21 17:41:30 -04:00
# include "../util/concurrency/race.h"
2010-11-23 14:13:46 -05:00
# include "pdfile.h"
# include "database.h"
# include "db.h"
# include "../util/unittest.h"
2011-02-24 13:34:42 -05:00
# include "../util/checksum.h"
2010-11-26 18:18:24 -05:00
# include "cmdline.h"
2010-12-21 02:23:31 -05:00
# include "curop.h"
2011-01-04 21:02:13 -05:00
# include "mongommf.h"
2011-07-21 17:41:30 -04:00
# include "../util/compress.h"
2010-11-23 10:12:31 -05:00
2010-12-22 01:42:52 -05:00
# include <sys/stat.h>
# include <fcntl.h>
2010-11-23 10:12:31 -05:00
using namespace mongoutils ;
2010-11-17 22:31:38 -05:00
2011-01-04 00:40:41 -05:00
namespace mongo {
2010-11-17 22:31:38 -05:00
2011-01-04 00:40:41 -05:00
namespace dur {
2010-11-17 22:31:38 -05:00
2011-01-04 00:40:41 -05:00
struct ParsedJournalEntry { /*copyable*/
2010-12-20 13:29:58 -05:00
ParsedJournalEntry ( ) : e ( 0 ) { }
2010-11-26 18:18:24 -05:00
2010-12-19 10:54:45 -05:00
// relative path of database for the operation.
// might be a pointer into mmaped Journal file
2011-01-04 00:40:41 -05:00
const char * dbName ;
2010-12-17 07:59:45 -05:00
2010-12-19 10:54:45 -05:00
// thse are pointers into the memory mapped journal file
const JEntry * e ; // local db sentinel is already parsed out here into dbName
2010-12-17 07:59:45 -05:00
2010-12-19 10:54:45 -05:00
// if not one of the two simple JEntry's above, this is the operation:
shared_ptr < DurOp > op ;
2010-11-26 18:18:24 -05:00
} ;
2010-11-23 14:13:46 -05:00
2010-11-23 10:12:31 -05:00
void removeJournalFiles ( ) ;
path getJournalDir ( ) ;
/** get journal filenames, in order. throws if unexpected content found */
2011-01-04 00:40:41 -05:00
static void getFiles ( path dir , vector < path > & files ) {
2010-11-23 10:12:31 -05:00
map < unsigned , path > m ;
for ( filesystem : : directory_iterator i ( dir ) ;
2011-01-04 00:40:41 -05:00
i ! = filesystem : : directory_iterator ( ) ;
+ + i ) {
2010-11-23 10:12:31 -05:00
filesystem : : path filepath = * i ;
string fileName = filesystem : : path ( * i ) . leaf ( ) ;
if ( str : : startsWith ( fileName , " j._ " ) ) {
unsigned u = str : : toUnsigned ( str : : after ( fileName , ' _ ' ) ) ;
2011-01-04 00:40:41 -05:00
if ( m . count ( u ) ) {
2010-11-23 14:13:46 -05:00
uasserted ( 13531 , str : : stream ( ) < < " unexpected files in journal directory " < < dir . string ( ) < < " : " < < fileName ) ;
2010-11-23 10:12:31 -05:00
}
m . insert ( pair < unsigned , path > ( u , filepath ) ) ;
}
}
2011-03-01 08:05:51 -05:00
for ( map < unsigned , path > : : iterator i = m . begin ( ) ; i ! = m . end ( ) ; + + i ) {
if ( i ! = m . begin ( ) & & m . count ( i - > first - 1 ) = = 0 ) {
uasserted ( 13532 ,
str : : stream ( ) < < " unexpected file in journal directory " < < dir . string ( )
< < " : " < < filesystem : : path ( i - > second ) . leaf ( ) < < " : can't find its preceeding file " ) ;
}
2010-11-23 10:12:31 -05:00
files . push_back ( i - > second ) ;
2011-03-01 08:05:51 -05:00
}
2010-11-23 10:12:31 -05:00
}
2010-11-23 14:13:46 -05:00
/** read through the memory mapped data of a journal file (journal/j._<n> file)
2011-01-04 00:40:41 -05:00
throws
2010-11-23 14:13:46 -05:00
*/
2010-12-20 17:33:33 -05:00
class JournalSectionIterator : boost : : noncopyable {
2011-08-02 10:37:48 -04:00
auto_ptr < BufReader > _entries ;
2011-07-25 12:45:03 -04:00
const JSectHeader _h ;
2011-07-21 17:41:30 -04:00
const char * _lastDbName ; // pointer into mmaped journal file
const bool _doDurOps ;
string _uncompressed ;
2010-11-23 14:13:46 -05:00
public :
2011-07-25 12:45:03 -04:00
JournalSectionIterator ( const JSectHeader & h , const void * compressed , unsigned compressedLen , bool doDurOpsRecovering ) :
_h ( h ) ,
_lastDbName ( 0 )
2011-07-21 17:41:30 -04:00
, _doDurOps ( doDurOpsRecovering )
{
2011-07-25 12:45:03 -04:00
assert ( doDurOpsRecovering ) ;
bool ok = uncompress ( ( const char * ) compressed , compressedLen , & _uncompressed ) ;
if ( ! ok ) {
// it should always be ok (i think?) as there is a previous check to see that the JSectFooter is ok
log ( ) < < " couldn't uncompress journal section " < < endl ;
2011-08-02 11:16:49 -04:00
msgasserted ( 15874 , " couldn't uncompress journal section " ) ;
2011-07-21 17:41:30 -04:00
}
2011-07-25 12:45:03 -04:00
const char * p = _uncompressed . c_str ( ) ;
assert ( compressedLen = = _h . sectionLen ( ) - sizeof ( JSectFooter ) - sizeof ( JSectHeader ) ) ;
2011-08-02 10:37:48 -04:00
_entries = auto_ptr < BufReader > ( new BufReader ( p , _uncompressed . size ( ) ) ) ;
2011-07-21 17:41:30 -04:00
}
2010-11-23 14:13:46 -05:00
2011-07-25 12:45:03 -04:00
// we work with the uncompressed buffer when doing a WRITETODATAFILES (for speed)
JournalSectionIterator ( const JSectHeader & h , const void * p , unsigned len ) :
2011-08-02 10:37:48 -04:00
_entries ( new BufReader ( ( const char * ) p , len ) ) ,
2011-07-25 12:45:03 -04:00
_h ( h ) ,
_lastDbName ( 0 )
2011-08-02 10:37:48 -04:00
, _doDurOps ( false )
2010-11-23 14:13:46 -05:00
2011-07-25 12:45:03 -04:00
{ }
2010-11-23 14:13:46 -05:00
2011-07-25 12:45:03 -04:00
bool atEof ( ) const { return _entries - > atEof ( ) ; }
unsigned long long seqNumber ( ) const { return _h . seqNumber ; }
2010-12-20 22:49:37 -05:00
2010-11-23 14:13:46 -05:00
/** get the next entry from the log. this function parses and combines JDbContext and JEntry's.
2011-01-04 00:40:41 -05:00
* throws on premature end of section .
2010-11-23 14:13:46 -05:00
*/
2011-07-25 12:45:03 -04:00
void next ( ParsedJournalEntry & e ) {
2010-11-26 18:18:24 -05:00
unsigned lenOrOpCode ;
2011-07-25 12:45:03 -04:00
_entries - > read ( lenOrOpCode ) ;
2010-12-17 07:59:45 -05:00
2011-01-04 00:40:41 -05:00
if ( lenOrOpCode > JEntry : : OpCode_Min ) {
2010-12-21 23:22:15 -05:00
switch ( lenOrOpCode ) {
2011-01-04 00:40:41 -05:00
case JEntry : : OpCode_Footer : {
2011-07-25 12:45:03 -04:00
assert ( false ) ;
2011-01-04 00:40:41 -05:00
}
2010-11-23 14:13:46 -05:00
2010-12-21 23:22:15 -05:00
case JEntry : : OpCode_FileCreated :
2011-01-04 00:40:41 -05:00
case JEntry : : OpCode_DropDb : {
e . dbName = 0 ;
2011-07-25 12:45:03 -04:00
boost : : shared_ptr < DurOp > op = DurOp : : read ( lenOrOpCode , * _entries ) ;
2011-01-04 00:40:41 -05:00
if ( _doDurOps ) {
e . op = op ;
2010-12-21 19:35:15 -05:00
}
2011-07-25 12:45:03 -04:00
return ;
2011-01-04 00:40:41 -05:00
}
2010-11-26 18:18:24 -05:00
2011-01-04 00:40:41 -05:00
case JEntry : : OpCode_DbContext : {
2011-07-25 12:45:03 -04:00
_lastDbName = ( const char * ) _entries - > pos ( ) ;
const unsigned limit = std : : min ( ( unsigned ) Namespace : : MaxNsLen , _entries - > remaining ( ) ) ;
2011-01-04 00:40:41 -05:00
const unsigned len = strnlen ( _lastDbName , limit ) ;
massert ( 13533 , " problem processing journal file during recovery " , _lastDbName [ len ] = = ' \0 ' ) ;
2011-07-25 12:45:03 -04:00
_entries - > skip ( len + 1 ) ; // skip '\0' too
_entries - > read ( lenOrOpCode ) ; // read this for the fall through
2011-01-04 00:40:41 -05:00
}
// fall through as a basic operation always follows jdbcontext, and we don't have anything to return yet
2010-12-19 10:54:45 -05:00
2010-12-21 23:22:15 -05:00
default :
// fall through
;
}
2010-11-23 14:13:46 -05:00
}
2010-12-20 13:29:58 -05:00
// JEntry - a basic write
assert ( lenOrOpCode & & lenOrOpCode < JEntry : : OpCode_Min ) ;
2011-07-25 12:45:03 -04:00
_entries - > rewind ( 4 ) ;
e . e = ( JEntry * ) _entries - > skip ( sizeof ( JEntry ) ) ;
2010-12-20 13:29:58 -05:00
e . dbName = e . e - > isLocalDbContext ( ) ? " local " : _lastDbName ;
assert ( e . e - > len = = lenOrOpCode ) ;
2011-07-25 12:45:03 -04:00
_entries - > skip ( e . e - > len ) ;
2010-11-23 14:13:46 -05:00
}
2011-07-25 12:45:03 -04:00
2010-11-23 14:13:46 -05:00
} ;
2011-01-05 00:32:30 -05:00
static string fileName ( const char * dbName , int fileNo ) {
2011-01-04 21:02:13 -05:00
stringstream ss ;
ss < < dbName < < ' . ' ;
assert ( fileNo > = 0 ) ;
if ( fileNo = = JEntry : : DotNsSuffix )
ss < < " ns " ;
else
ss < < fileNo ;
// relative name -> full path name
path full ( dbpath ) ;
full / = ss . str ( ) ;
return full . string ( ) ;
2010-11-23 14:13:46 -05:00
}
2010-11-23 10:12:31 -05:00
2011-01-04 00:40:41 -05:00
RecoveryJob : : ~ RecoveryJob ( ) {
2011-03-01 21:08:44 -05:00
DESTRUCTOR_GUARD (
if ( ! _mmfs . empty ( ) )
close ( ) ;
)
2010-11-17 22:31:38 -05:00
}
2010-11-23 10:12:31 -05:00
2011-01-04 00:40:41 -05:00
void RecoveryJob : : close ( ) {
2010-12-21 18:55:08 -05:00
scoped_lock lk ( _mx ) ;
_close ( ) ;
}
2011-01-04 00:40:41 -05:00
void RecoveryJob : : _close ( ) {
2011-01-04 21:02:13 -05:00
MongoFile : : flushAll ( true ) ;
2011-01-05 00:32:30 -05:00
_mmfs . clear ( ) ;
2011-01-04 21:02:13 -05:00
}
2011-01-05 00:32:30 -05:00
void RecoveryJob : : write ( const ParsedJournalEntry & entry ) {
2011-04-04 14:50:11 -04:00
//TODO(mathias): look into making some of these dasserts
assert ( entry . e ) ;
assert ( entry . dbName ) ;
assert ( strnlen ( entry . dbName , MaxDatabaseNameLen ) < MaxDatabaseNameLen ) ;
2011-01-04 21:02:13 -05:00
const string fn = fileName ( entry . dbName , entry . e - > getFileNo ( ) ) ;
MongoFile * file ;
{
MongoFileFinder finder ; // must release lock before creating new MongoMMF
file = finder . findByPath ( fn ) ;
2010-12-22 01:42:52 -05:00
}
2011-01-04 21:02:13 -05:00
MongoMMF * mmf ;
2011-01-05 00:32:30 -05:00
if ( file ) {
2011-01-04 21:02:13 -05:00
assert ( file - > isMongoMMF ( ) ) ;
mmf = ( MongoMMF * ) file ;
}
else {
2011-07-20 15:28:23 -04:00
if ( ! _recovering ) {
log ( ) < < " journal error applying writes, file " < < fn < < " is not open " < < endl ;
assert ( false ) ;
}
2011-01-04 21:02:13 -05:00
boost : : shared_ptr < MongoMMF > sp ( new MongoMMF ) ;
assert ( sp - > open ( fn , false ) ) ;
_mmfs . push_back ( sp ) ;
mmf = sp . get ( ) ;
}
if ( ( entry . e - > ofs + entry . e - > len ) < = mmf - > length ( ) ) {
2011-04-04 14:50:11 -04:00
assert ( mmf - > view_write ( ) ) ;
assert ( entry . e - > srcData ( ) ) ;
2011-01-04 21:02:13 -05:00
void * dest = ( char * ) mmf - > view_write ( ) + entry . e - > ofs ;
memcpy ( dest , entry . e - > srcData ( ) , entry . e - > len ) ;
2011-03-11 16:57:18 -05:00
stats . curr - > _writeToDataFilesBytes + = entry . e - > len ;
2011-01-04 21:02:13 -05:00
}
else {
massert ( 13622 , " Trying to write past end of file in WRITETODATAFILES " , _recovering ) ;
}
2010-11-23 14:13:46 -05:00
}
2010-11-26 18:18:24 -05:00
2011-01-04 00:40:41 -05:00
void RecoveryJob : : applyEntry ( const ParsedJournalEntry & entry , bool apply , bool dump ) {
2010-12-19 10:54:45 -05:00
if ( entry . e ) {
if ( dump ) {
stringstream ss ;
ss < < " BASICWRITE " < < setw ( 20 ) < < entry . dbName < < ' . ' ;
if ( entry . e - > isNsSuffix ( ) )
ss < < " ns " ;
else
ss < < setw ( 2 ) < < entry . e - > getFileNo ( ) ;
2011-01-04 00:40:41 -05:00
ss < < ' ' < < setw ( 6 ) < < entry . e - > len < < ' ' < < /*hex << setw(8) << (size_t) fqe.srcData << dec <<*/
" " < < hexdump ( entry . e - > srcData ( ) , entry . e - > len ) ;
2010-12-19 10:54:45 -05:00
log ( ) < < ss . str ( ) < < endl ;
2011-01-04 00:40:41 -05:00
}
2010-12-19 10:54:45 -05:00
if ( apply ) {
2011-01-04 21:02:13 -05:00
write ( entry ) ;
2010-12-19 10:54:45 -05:00
}
2011-01-04 00:40:41 -05:00
}
2010-12-21 19:35:15 -05:00
else if ( entry . op ) {
2010-12-19 10:54:45 -05:00
// a DurOp subclass operation
if ( dump ) {
log ( ) < < " OP " < < entry . op - > toString ( ) < < endl ;
2011-01-04 00:40:41 -05:00
}
2010-12-19 10:54:45 -05:00
if ( apply ) {
if ( entry . op - > needFilesClosed ( ) ) {
2010-12-21 18:55:08 -05:00
_close ( ) ; // locked in processSection
2010-12-19 10:54:45 -05:00
}
entry . op - > replay ( ) ;
}
}
}
2011-01-04 00:40:41 -05:00
void RecoveryJob : : applyEntries ( const vector < ParsedJournalEntry > & entries ) {
2010-12-13 14:47:11 -05:00
bool apply = ( cmdLine . durOptions & CmdLine : : DurScanOnly ) = = 0 ;
bool dump = cmdLine . durOptions & CmdLine : : DurDumpJournal ;
2010-11-26 18:18:24 -05:00
if ( dump )
log ( ) < < " BEGIN section " < < endl ;
2011-01-04 00:40:41 -05:00
for ( vector < ParsedJournalEntry > : : const_iterator i = entries . begin ( ) ; i ! = entries . end ( ) ; + + i ) {
2010-12-19 10:54:45 -05:00
applyEntry ( * i , apply , dump ) ;
2011-01-04 00:40:41 -05:00
}
2010-11-26 18:18:24 -05:00
if ( dump )
log ( ) < < " END section " < < endl ;
2010-11-23 14:13:46 -05:00
}
2011-07-25 12:45:03 -04:00
void RecoveryJob : : processSection ( const JSectHeader * h , const void * p , unsigned len , const JSectFooter * f ) {
2010-12-21 18:55:08 -05:00
scoped_lock lk ( _mx ) ;
2011-07-21 17:41:30 -04:00
RACECHECK
2011-07-25 12:45:03 -04:00
/** todo: we should really verify the checksum to see that seqNumber is ok?
that is expensive maybe there is some sort of checksum of just the header
within the header itself
*/
if ( _recovering & & _lastDataSyncedFromLastRun > h - > seqNumber + ExtraKeepTimeMs ) {
if ( h - > seqNumber ! = _lastSeqMentionedInConsoleLog ) {
static int n ;
if ( + + n < 10 ) {
log ( ) < < " recover skipping application of section seq: " < < h - > seqNumber < < " < lsn: " < < _lastDataSyncedFromLastRun < < endl ;
}
else if ( n = = 10 ) {
log ( ) < < " recover skipping application of section more... " < < endl ;
}
_lastSeqMentionedInConsoleLog = h - > seqNumber ;
}
return ;
}
2010-12-21 18:55:08 -05:00
2011-08-02 10:37:48 -04:00
auto_ptr < JournalSectionIterator > i ;
2011-07-25 12:45:03 -04:00
if ( _recovering ) {
2011-08-02 10:37:48 -04:00
i = auto_ptr < JournalSectionIterator > ( new JournalSectionIterator ( * h , p , len , _recovering ) ) ;
2011-07-25 12:45:03 -04:00
}
else {
2011-08-02 10:37:48 -04:00
i = auto_ptr < JournalSectionIterator > ( new JournalSectionIterator ( * h , /*after header*/ p , /*w/out header*/ len ) ) ;
2011-07-25 12:45:03 -04:00
}
2010-12-20 17:33:33 -05:00
2011-07-21 17:41:30 -04:00
// we use a static so that we don't have to reallocate every time through. occasionally we
// go back to a small allocation so that if there were a spiky growth it won't stick forever.
static vector < ParsedJournalEntry > entries ;
entries . clear ( ) ;
2011-08-02 10:37:48 -04:00
/** TEMP uncomment
2011-07-21 17:41:30 -04:00
RARELY OCCASIONALLY {
if ( entries . capacity ( ) > 2048 ) {
entries . shrink_to_fit ( ) ;
entries . reserve ( 2048 ) ;
2011-02-02 01:39:13 -05:00
}
2010-12-20 22:49:37 -05:00
}
2011-08-02 10:37:48 -04:00
*/
2010-12-20 22:49:37 -05:00
2010-12-20 17:33:33 -05:00
// first read all entries to make sure this section is valid
ParsedJournalEntry e ;
2011-07-25 12:45:03 -04:00
while ( ! i - > atEof ( ) ) {
i - > next ( e ) ;
2010-12-20 17:33:33 -05:00
entries . push_back ( e ) ;
}
2011-07-25 12:45:03 -04:00
// after the entries check the footer checksum
if ( _recovering ) {
assert ( ( ( const char * ) h ) + sizeof ( JSectHeader ) = = p ) ;
if ( ! f - > checkHash ( h , len + sizeof ( JSectHeader ) ) ) {
msgasserted ( 13594 , " journal checksum doesn't match " ) ;
}
}
2011-01-04 00:40:41 -05:00
// got all the entries for one group commit. apply them:
2010-12-20 17:33:33 -05:00
applyEntries ( entries ) ;
}
2010-12-19 10:54:45 -05:00
/** apply a specific journal file, that is already mmap'd
@ param p start of the memory mapped file
2011-01-04 00:40:41 -05:00
@ return true if this is detected to be the last file ( ends abruptly )
2010-11-23 14:13:46 -05:00
*/
2010-12-20 17:33:33 -05:00
bool RecoveryJob : : processFileBuffer ( const void * p , unsigned len ) {
2010-11-23 14:13:46 -05:00
try {
2011-02-04 15:02:06 -05:00
unsigned long long fileId ;
2010-12-20 17:33:33 -05:00
BufReader br ( p , len ) ;
2011-01-04 00:40:41 -05:00
{
// read file header
2010-12-20 17:33:33 -05:00
JHeader h ;
br . read ( h ) ;
2011-09-12 22:17:51 -04:00
/* [dm] not automatically handled. we should eventually handle this automatically. i think:
( 1 ) if this is the final journal file
( 2 ) and the file size is just the file header in length ( or less ) - - this is a bit tricky to determine if prealloced
then can just assume recovery ended cleanly and not error out ( still should log ) .
*/
uassert ( 13537 ,
" journal file header invalid. This could indicate corruption in a journal file, or perhaps a crash where sectors in file header were in flight written out of order at time of crash (unlikely but possible). " ,
h . valid ( ) ) ;
2010-12-20 17:33:33 -05:00
if ( ! h . versionOk ( ) ) {
2011-09-12 22:17:51 -04:00
log ( ) < < " journal file version number mismatch got: " < < hex < < h . _version
< < " expected: " < < hex < < ( unsigned ) JHeader : : CurrentVersion
< < " . if you have just upgraded, recover with old version of mongod, terminate cleanly, then upgrade. "
< < endl ;
2010-12-20 17:33:33 -05:00
uasserted ( 13536 , str : : stream ( ) < < " journal version number mismatch " < < h . _version ) ;
2010-12-19 10:54:45 -05:00
}
2011-02-04 15:02:06 -05:00
fileId = h . fileId ;
2011-02-07 13:12:13 -05:00
if ( cmdLine . durOptions & CmdLine : : DurDumpJournal ) {
log ( ) < < " JHeader::fileId= " < < fileId < < endl ;
}
2010-12-20 17:33:33 -05:00
}
2010-11-26 18:18:24 -05:00
2010-12-20 17:33:33 -05:00
// read sections
while ( ! br . atEof ( ) ) {
JSectHeader h ;
br . peek ( h ) ;
2011-02-04 15:02:06 -05:00
if ( h . fileId ! = fileId ) {
if ( debug | | ( cmdLine . durOptions & CmdLine : : DurDumpJournal ) ) {
log ( ) < < " Ending processFileBuffer at differing fileId want: " < < fileId < < " got: " < < h . fileId < < endl ;
2011-07-21 17:41:30 -04:00
log ( ) < < " sect len: " < < h . sectionLen ( ) < < " seqnum: " < < h . seqNumber < < endl ;
2011-02-04 15:02:06 -05:00
}
return true ;
}
2011-07-25 12:45:03 -04:00
unsigned slen = h . sectionLen ( ) ;
unsigned dataLen = slen - sizeof ( JSectHeader ) - sizeof ( JSectFooter ) ;
const char * hdr = ( const char * ) br . skip ( h . sectionLenWithPadding ( ) ) ;
const char * data = hdr + sizeof ( JSectHeader ) ;
const char * footer = data + dataLen ;
processSection ( ( const JSectHeader * ) hdr , data , dataLen , ( const JSectFooter * ) footer ) ;
2010-12-21 02:23:31 -05:00
// ctrl c check
killCurrentOp . checkForInterrupt ( false ) ;
2010-11-23 14:13:46 -05:00
}
}
2011-01-04 00:40:41 -05:00
catch ( BufReader : : eof & ) {
2010-12-13 14:47:11 -05:00
if ( cmdLine . durOptions & CmdLine : : DurDumpJournal )
2010-11-26 18:18:24 -05:00
log ( ) < < " ABRUPT END " < < endl ;
2010-11-23 14:13:46 -05:00
return true ; // abrupt end
2010-11-23 10:12:31 -05:00
}
2010-11-23 14:13:46 -05:00
return false ; // non-abrupt end
2010-11-23 10:12:31 -05:00
}
2010-12-19 10:54:45 -05:00
/** apply a specific journal file */
2010-12-07 13:24:22 -05:00
bool RecoveryJob : : processFile ( path journalfile ) {
2010-11-23 14:13:46 -05:00
log ( ) < < " recover " < < journalfile . string ( ) < < endl ;
2011-07-22 13:36:24 -04:00
try {
if ( boost : : filesystem : : file_size ( journalfile . string ( ) ) = = 0 ) {
log ( ) < < " recover info " < < journalfile . string ( ) < < " has zero length " < < endl ;
return true ;
}
} catch ( . . . ) {
// if something weird like a permissions problem keep going so the massert down below can happen (presumably)
log ( ) < < " recover exception checking filesize " < < endl ;
}
2010-11-23 10:12:31 -05:00
MemoryMappedFile f ;
void * p = f . mapWithOptions ( journalfile . string ( ) . c_str ( ) , MongoFile : : READONLY | MongoFile : : SEQUENTIAL ) ;
2010-11-28 10:22:17 -05:00
massert ( 13544 , str : : stream ( ) < < " recover error couldn't open " < < journalfile . string ( ) , p ) ;
2010-12-20 17:33:33 -05:00
return processFileBuffer ( p , ( unsigned ) f . length ( ) ) ;
2010-11-23 10:12:31 -05:00
}
2010-12-19 10:54:45 -05:00
/** @param files all the j._0 style files we need to apply for recovery */
2011-01-04 00:40:41 -05:00
void RecoveryJob : : go ( vector < path > & files ) {
2010-11-23 14:13:46 -05:00
log ( ) < < " recover begin " < < endl ;
2011-01-04 20:01:22 -05:00
_recovering = true ;
2010-11-23 10:12:31 -05:00
2010-12-20 22:49:37 -05:00
// load the last sequence number synced to the datafiles on disk before the last crash
2010-12-21 01:59:33 -05:00
_lastDataSyncedFromLastRun = journalReadLSN ( ) ;
log ( ) < < " recover lsn: " < < _lastDataSyncedFromLastRun < < endl ;
2010-12-20 22:49:37 -05:00
2011-07-25 16:08:22 -07:00
// todo: we could truncate the journal file at rotation time to the right length, then this abruptEnd
// check can be turned back on. this is relevant when prealloc is being used.
2011-01-04 00:40:41 -05:00
for ( unsigned i = 0 ; i ! = files . size ( ) ; + + i ) {
2011-07-29 18:39:56 -04:00
bool abruptEnd = processFile ( files [ i ] ) ;
if ( abruptEnd & & i + 1 < files . size ( ) ) {
# if 1 // Leaving this as a warning for now. TODO: make this an error post 2.0
log ( ) < < " recover warning: abrupt end to file " < < files [ i ] . string ( ) < < " , yet it isn't the last journal file " < < endl ;
# else
2010-11-23 14:13:46 -05:00
log ( ) < < " recover error: abrupt end to file " < < files [ i ] . string ( ) < < " , yet it isn't the last journal file " < < endl ;
close ( ) ;
uasserted ( 13535 , " recover abrupt journal file end " ) ;
2011-07-29 18:39:56 -04:00
# endif
}
2010-11-23 10:12:31 -05:00
}
close ( ) ;
2010-11-26 18:18:24 -05:00
2010-12-13 14:47:11 -05:00
if ( cmdLine . durOptions & CmdLine : : DurScanOnly ) {
2010-12-17 07:59:45 -05:00
uasserted ( 13545 , str : : stream ( ) < < " --durOptions " < < ( int ) CmdLine : : DurScanOnly < < " (scan only) specified " ) ;
2010-11-26 18:18:24 -05:00
}
2010-11-23 14:13:46 -05:00
log ( ) < < " recover cleaning up " < < endl ;
2010-11-23 10:12:31 -05:00
removeJournalFiles ( ) ;
2010-11-23 14:13:46 -05:00
log ( ) < < " recover done " < < endl ;
2010-11-25 11:09:18 -05:00
okToCleanUp = true ;
2011-01-04 20:01:22 -05:00
_recovering = false ;
2010-11-17 22:31:38 -05:00
}
2010-12-21 02:23:31 -05:00
void _recover ( ) {
2010-11-28 10:13:01 -05:00
assert ( cmdLine . dur ) ;
2010-11-23 10:12:31 -05:00
filesystem : : path p = getJournalDir ( ) ;
2011-01-04 00:40:41 -05:00
if ( ! exists ( p ) ) {
2010-11-23 10:12:31 -05:00
log ( ) < < " directory " < < p . string ( ) < < " does not exist, there will be no recovery startup step " < < endl ;
2010-11-25 11:09:18 -05:00
okToCleanUp = true ;
2010-11-23 10:12:31 -05:00
return ;
}
2010-11-23 14:13:46 -05:00
vector < path > journalFiles ;
getFiles ( p , journalFiles ) ;
2010-11-23 10:12:31 -05:00
2011-01-04 00:40:41 -05:00
if ( journalFiles . empty ( ) ) {
2010-11-23 14:13:46 -05:00
log ( ) < < " recover : no journal files present, no recovery needed " < < endl ;
2010-11-25 11:09:18 -05:00
okToCleanUp = true ;
2010-11-23 10:12:31 -05:00
return ;
}
2010-12-21 18:22:04 -05:00
RecoveryJob : : get ( ) . go ( journalFiles ) ;
2010-12-21 02:23:31 -05:00
}
2011-01-22 23:38:44 -05:00
extern mutex groupCommitMutex ;
2010-12-21 02:23:31 -05:00
/** recover from a crash
2011-01-22 23:38:44 -05:00
called during startup
2011-01-04 00:40:41 -05:00
throws on error
2010-12-21 02:23:31 -05:00
*/
2011-01-04 00:40:41 -05:00
void recover ( ) {
2010-12-21 02:23:31 -05:00
// we use a lock so that exitCleanly will wait for us
// to finish (or at least to notice what is up and stop)
2011-01-16 11:09:40 -05:00
writelock lk ;
2011-01-22 23:38:44 -05:00
// this is so the mutexdebugger doesn't get confused. we are actually single threaded
// at this point in the program so it wouldn't have been a true problem (I think)
scoped_lock lk2 ( groupCommitMutex ) ;
2010-12-21 02:23:31 -05:00
_recover ( ) ; // throws on interruption
}
2010-11-23 10:12:31 -05:00
2010-12-08 16:23:21 -05:00
struct BufReaderY { int a , b ; } ;
2010-11-26 18:18:24 -05:00
class BufReaderUnitTest : public UnitTest {
public :
2011-01-04 00:40:41 -05:00
void run ( ) {
2010-12-08 16:23:21 -05:00
BufReader r ( ( void * ) " abcdabcdabcd " , 12 ) ;
2010-11-26 18:18:24 -05:00
char x ;
2010-12-08 16:23:21 -05:00
BufReaderY y ;
2010-12-19 10:54:45 -05:00
r . read ( x ) ; //cout << x; // a
2010-11-26 18:18:24 -05:00
assert ( x = = ' a ' ) ;
r . read ( y ) ;
2011-01-04 00:40:41 -05:00
r . read ( x ) ;
2010-11-26 18:18:24 -05:00
assert ( x = = ' b ' ) ;
}
} brunittest ;
2011-03-01 21:08:44 -05:00
// can't free at termination because order of destruction of global vars is arbitrary
RecoveryJob & RecoveryJob : : _instance = * ( new RecoveryJob ( ) ) ;
2010-12-21 18:22:04 -05:00
2010-11-17 22:31:38 -05:00
} // namespace dur
} // namespace mongo