2009-12-29 11:49:24 -05:00
// database.cpp
/**
* Copyright ( C ) 2008 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/>.
*/
2010-04-27 15:27:52 -04:00
# include "pch.h"
2009-12-29 11:49:24 -05:00
# include "pdfile.h"
# include "database.h"
2010-06-15 02:48:09 -04:00
# include "instance.h"
2010-09-30 12:19:09 -04:00
# include "clientcursor.h"
2011-11-27 15:19:55 -05:00
# include "databaseholder.h"
2009-12-29 11:49:24 -05:00
namespace mongo {
2011-11-29 14:43:56 -05:00
bool Database : : _openAllFiles = true ;
2009-12-29 11:49:24 -05:00
2011-11-27 15:19:55 -05:00
void assertDbAtLeastReadLocked ( const Database * ) {
// temp impl
dbMutex . assertAtLeastReadLocked ( ) ;
}
void assertDbWriteLocked ( const Database * ) {
// temp impl
dbMutex . assertWriteLocked ( ) ;
}
2010-08-26 10:42:07 -04:00
Database : : ~ Database ( ) {
2011-11-27 15:19:55 -05:00
dbMutex . assertWriteLocked ( ) ;
2010-08-26 10:42:07 -04:00
magic = 0 ;
2011-11-27 15:19:55 -05:00
size_t n = _files . size ( ) ;
2010-08-26 10:42:07 -04:00
for ( size_t i = 0 ; i < n ; i + + )
2011-11-27 15:19:55 -05:00
delete _files [ i ] ;
2011-01-04 00:40:41 -05:00
if ( ccByLoc . size ( ) ) {
2010-08-26 10:42:07 -04:00
log ( ) < < " \n \n \n WARNING: ccByLoc not empty on database close! " < < ccByLoc . size ( ) < < ' ' < < name < < endl ;
}
}
2010-06-15 02:48:09 -04:00
Database : : Database ( const char * nm , bool & newDb , const string & _path )
2011-01-04 00:40:41 -05:00
: name ( nm ) , path ( _path ) , namespaceIndex ( path , name ) ,
2011-11-27 15:19:55 -05:00
profileName ( name + " .system.profile " )
{
2011-02-09 16:15:47 -05:00
try {
2011-11-27 15:19:55 -05:00
{
// check db name is valid
size_t L = strlen ( nm ) ;
uassert ( 10028 , " db name is empty " , L > 0 ) ;
uassert ( 10032 , " db name too long " , L < 64 ) ;
uassert ( 10029 , " bad db name [1] " , * nm ! = ' . ' ) ;
uassert ( 10030 , " bad db name [2] " , nm [ L - 1 ] ! = ' . ' ) ;
uassert ( 10031 , " bad char(s) in db name " , strchr ( nm , ' ' ) = = 0 ) ;
}
newDb = namespaceIndex . exists ( ) ;
profile = cmdLine . defaultProfile ;
2011-12-01 12:42:23 -05:00
checkDuplicateUncasedNames ( true ) ;
2011-11-27 15:19:55 -05:00
// If already exists, open. Otherwise behave as if empty until
// there's a write, then open.
if ( ! newDb | | cmdLine . defaultProfile ) {
namespaceIndex . init ( ) ;
if ( _openAllFiles )
openAllFiles ( ) ;
}
magic = 781231 ;
} catch ( std : : exception & e ) {
log ( ) < < " warning database " < < path < < ' ' < < nm < < " could not be opened " < < endl ;
log ( ) < < e . what ( ) < < endl ;
2011-02-09 16:15:47 -05:00
// since destructor won't be called:
2011-11-27 15:19:55 -05:00
for ( size_t i = 0 ; i < _files . size ( ) ; i + + )
delete _files [ i ] ;
2011-02-09 16:15:47 -05:00
throw ;
}
2010-06-15 02:48:09 -04:00
}
2011-03-29 00:44:01 -07:00
2011-12-01 12:42:23 -05:00
void Database : : checkDuplicateUncasedNames ( bool inholderlock ) const {
string duplicate = duplicateUncasedName ( inholderlock , name , path ) ;
2011-04-12 21:53:55 -07:00
if ( ! duplicate . empty ( ) ) {
stringstream ss ;
ss < < " db already exists with different case other: [ " < < duplicate < < " ] me [ " < < name < < " ] " ;
uasserted ( DatabaseDifferCaseCode , ss . str ( ) ) ;
}
}
2011-12-01 12:42:23 -05:00
/*static*/
string Database : : duplicateUncasedName ( bool inholderlock , const string & name , const string & path , set < string > * duplicates ) {
dbMutex . assertAtLeastReadLocked ( ) ;
2011-04-12 21:53:55 -07:00
if ( duplicates ) {
duplicates - > clear ( ) ;
}
2011-03-29 00:44:01 -07:00
vector < string > others ;
getDatabaseNames ( others , path ) ;
2011-04-12 21:53:55 -07:00
2011-03-29 00:44:01 -07:00
set < string > allShortNames ;
2011-12-03 13:32:11 -05:00
dbHolder ( ) . getAllShortNames ( inholderlock , allShortNames ) ;
2011-03-29 00:44:01 -07:00
others . insert ( others . end ( ) , allShortNames . begin ( ) , allShortNames . end ( ) ) ;
for ( unsigned i = 0 ; i < others . size ( ) ; i + + ) {
2011-04-12 21:53:55 -07:00
2011-03-29 00:44:01 -07:00
if ( strcasecmp ( others [ i ] . c_str ( ) , name . c_str ( ) ) )
continue ;
if ( strcmp ( others [ i ] . c_str ( ) , name . c_str ( ) ) = = 0 )
continue ;
2010-06-15 02:48:09 -04:00
2011-04-12 21:53:55 -07:00
if ( duplicates ) {
duplicates - > insert ( others [ i ] ) ;
} else {
return others [ i ] ;
}
}
if ( duplicates ) {
return duplicates - > empty ( ) ? " " : * duplicates - > begin ( ) ;
}
return " " ;
}
2010-12-30 13:38:09 -05:00
boost : : filesystem : : path Database : : fileName ( int n ) const {
stringstream ss ;
ss < < name < < ' . ' < < n ;
boost : : filesystem : : path fullName ;
fullName = boost : : filesystem : : path ( path ) ;
if ( directoryperdb )
fullName / = name ;
fullName / = ss . str ( ) ;
return fullName ;
}
2011-11-27 15:19:55 -05:00
bool Database : : openExistingFile ( int n ) {
assert ( this ) ;
2011-12-01 17:50:32 -05:00
dbMutex . assertWriteLocked ( ) ;
2011-11-27 15:19:55 -05:00
{
// must not yet be visible to others as we aren't in the db's write lock and
// we will write to _files vector - thus this assert.
2011-12-03 13:32:11 -05:00
bool loaded = dbHolder ( ) . __isLoaded ( name , path ) ;
2011-11-27 15:19:55 -05:00
assert ( ! loaded ) ;
}
// additionally must be in the dbholder mutex (no assert for that yet)
// todo: why here? that could be bad as we may be read locked only here
namespaceIndex . init ( ) ;
if ( n < 0 | | n > = DiskLoc : : MaxFiles ) {
massert ( 15924 , str : : stream ( ) < < " getFile(): bad file number value " < < n < < " (corrupt db?): run repair " , false ) ;
}
{
2011-11-27 14:49:16 -05:00
if ( n < ( int ) _files . size ( ) & & _files [ n ] ) {
2011-11-27 15:19:55 -05:00
dlog ( 2 ) < < " openExistingFile " < < n < < " is already open " < < endl ;
return true ;
}
}
{
boost : : filesystem : : path fullName = fileName ( n ) ;
string fullNameString = fullName . string ( ) ;
MongoDataFile * df = new MongoDataFile ( n ) ;
try {
if ( ! df - > openExisting ( fullNameString . c_str ( ) ) ) {
delete df ;
return false ;
}
}
catch ( AssertionException & ) {
delete df ;
throw ;
}
while ( n > = ( int ) _files . size ( ) ) {
_files . push_back ( 0 ) ;
}
_files [ n ] = df ;
}
return true ;
}
// todo : we stop once a datafile dne.
// if one datafile were missing we should keep going for
// repair purposes yet we do not.
2011-01-04 00:40:41 -05:00
void Database : : openAllFiles ( ) {
2011-11-29 14:43:56 -05:00
//log() << "TEMP openallfiles " << path << ' ' << name << endl;
2011-11-27 15:19:55 -05:00
assert ( this ) ;
int n = 0 ;
while ( openExistingFile ( n ) ) {
n + + ;
}
/*
2010-12-30 13:38:09 -05:00
int n = 0 ;
2011-01-04 00:40:41 -05:00
while ( exists ( n ) ) {
2010-12-30 13:38:09 -05:00
getFile ( n ) ;
n + + ;
}
// If last file is empty, consider it preallocated and make sure it's not mapped
// until a write is requested
if ( n > 1 & & getFile ( n - 1 ) - > getHeader ( ) - > isEmpty ( ) ) {
2011-11-27 15:19:55 -05:00
delete _files [ n - 1 ] ;
_files . pop_back ( ) ;
} */
2010-12-30 13:38:09 -05:00
}
2011-11-29 14:43:56 -05:00
// todo: this is called a lot. streamline the common case
2010-12-30 13:38:09 -05:00
MongoDataFile * Database : : getFile ( int n , int sizeNeeded , bool preallocateOnly ) {
assert ( this ) ;
2011-11-27 15:19:55 -05:00
DEV assertDbAtLeastReadLocked ( this ) ;
2011-01-04 00:40:41 -05:00
2010-12-30 13:38:09 -05:00
namespaceIndex . init ( ) ;
if ( n < 0 | | n > = DiskLoc : : MaxFiles ) {
out ( ) < < " getFile(): n= " < < n < < endl ;
massert ( 10295 , " getFile(): bad file number value (corrupt db?): run repair " , false ) ;
}
DEV {
2011-11-29 14:43:56 -05:00
if ( n > 100 ) {
2011-11-27 15:19:55 -05:00
out ( ) < < " getFile(): n= " < < n < < endl ;
2011-11-29 14:43:56 -05:00
}
2010-12-30 13:38:09 -05:00
}
MongoDataFile * p = 0 ;
if ( ! preallocateOnly ) {
2011-11-27 15:19:55 -05:00
while ( n > = ( int ) _files . size ( ) ) {
2011-11-29 14:43:56 -05:00
DEV if ( ! dbMutex . isWriteLocked ( ) ) {
log ( ) < < " error: getFile() called in a read lock, yet file to return is not yet open " < < endl ;
2011-12-05 15:55:51 -05:00
log ( ) < < " getFile( " < < n < < " ) _files.size: " < < _files . size ( ) < < ' ' < < fileName ( n ) . string ( ) < < endl ;
log ( ) < < " context ns: " < < cc ( ) . ns ( ) < < " openallfiles: " < < _openAllFiles < < endl ;
2011-11-29 14:43:56 -05:00
}
assertDbWriteLocked ( this ) ;
2011-11-27 15:19:55 -05:00
_files . push_back ( 0 ) ;
}
p = _files [ n ] ;
2010-12-30 13:38:09 -05:00
}
if ( p = = 0 ) {
2011-11-29 14:43:56 -05:00
assertDbWriteLocked ( this ) ;
2010-12-30 13:38:09 -05:00
boost : : filesystem : : path fullName = fileName ( n ) ;
string fullNameString = fullName . string ( ) ;
p = new MongoDataFile ( n ) ;
int minSize = 0 ;
2011-11-27 15:19:55 -05:00
if ( n ! = 0 & & _files [ n - 1 ] )
minSize = _files [ n - 1 ] - > getHeader ( ) - > fileLength ;
2010-12-30 13:38:09 -05:00
if ( sizeNeeded + DataFileHeader : : HeaderSize > minSize )
minSize = sizeNeeded + DataFileHeader : : HeaderSize ;
try {
p - > open ( fullNameString . c_str ( ) , minSize , preallocateOnly ) ;
}
catch ( AssertionException & ) {
delete p ;
throw ;
}
if ( preallocateOnly )
delete p ;
else
2011-11-27 15:19:55 -05:00
_files [ n ] = p ;
2010-12-30 13:38:09 -05:00
}
return preallocateOnly ? 0 : p ;
}
2011-01-04 00:40:41 -05:00
2010-12-30 13:38:09 -05:00
MongoDataFile * Database : : addAFile ( int sizeNeeded , bool preallocateNextFile ) {
2011-11-27 15:19:55 -05:00
assertDbWriteLocked ( this ) ;
int n = ( int ) _files . size ( ) ;
2010-12-30 13:38:09 -05:00
MongoDataFile * ret = getFile ( n , sizeNeeded ) ;
if ( preallocateNextFile )
preallocateAFile ( ) ;
return ret ;
}
2011-07-12 13:45:32 -07:00
bool fileIndexExceedsQuota ( const char * ns , int fileIndex , bool enforceQuota ) {
return
cmdLine . quota & &
enforceQuota & &
fileIndex > = cmdLine . quotaFiles & &
// we don't enforce the quota on "special" namespaces as that could lead to problems -- e.g.
// rejecting an index insert after inserting the main record.
! NamespaceString : : special ( ns ) & &
NamespaceString ( ns ) . db ! = " local " ;
}
2011-05-23 20:01:42 -04:00
MongoDataFile * Database : : suitableFile ( const char * ns , int sizeNeeded , bool preallocate , bool enforceQuota ) {
2010-12-30 14:12:26 -05:00
// check existing files
2011-01-04 00:40:41 -05:00
for ( int i = numFiles ( ) - 1 ; i > = 0 ; i - - ) {
2010-12-30 14:12:26 -05:00
MongoDataFile * f = getFile ( i ) ;
2011-05-23 20:01:42 -04:00
if ( f - > getHeader ( ) - > unusedLength > = sizeNeeded ) {
2011-07-12 13:45:32 -07:00
if ( fileIndexExceedsQuota ( ns , i - 1 , enforceQuota ) ) // NOTE i-1 is the value used historically for this check.
2011-05-23 20:01:42 -04:00
;
else
return f ;
}
2010-12-30 13:38:09 -05:00
}
2010-12-30 14:12:26 -05:00
2011-07-12 13:45:32 -07:00
if ( fileIndexExceedsQuota ( ns , numFiles ( ) , enforceQuota ) )
2011-05-23 20:01:42 -04:00
uasserted ( 12501 , " quota exceeded " ) ;
2010-12-30 14:12:26 -05:00
// allocate files until we either get one big enough or hit maxSize
2010-12-30 13:38:09 -05:00
for ( int i = 0 ; i < 8 ; i + + ) {
2010-12-30 14:12:26 -05:00
MongoDataFile * f = addAFile ( sizeNeeded , preallocate ) ;
2011-01-04 00:40:41 -05:00
2010-12-30 13:38:09 -05:00
if ( f - > getHeader ( ) - > unusedLength > = sizeNeeded )
2010-12-30 14:12:26 -05:00
return f ;
2010-12-30 13:38:09 -05:00
if ( f - > getHeader ( ) - > fileLength > = MongoDataFile : : maxSize ( ) ) // this is as big as they get so might as well stop
2010-12-30 14:12:26 -05:00
return f ;
2010-12-30 13:38:09 -05:00
}
2011-01-04 00:40:41 -05:00
2011-05-23 20:01:42 -04:00
uasserted ( 14810 , " couldn't allocate space (suitableFile) " ) ; // callers don't check for null return code
2010-12-30 14:12:26 -05:00
return 0 ;
2010-12-30 13:38:09 -05:00
}
MongoDataFile * Database : : newestFile ( ) {
int n = numFiles ( ) ;
if ( n = = 0 )
return 0 ;
return getFile ( n - 1 ) ;
}
2010-06-15 02:48:09 -04:00
2011-01-04 00:40:41 -05:00
2011-05-23 20:01:42 -04:00
Extent * Database : : allocExtent ( const char * ns , int size , bool capped , bool enforceQuota ) {
2011-11-12 12:01:55 -05:00
// todo: when profiling, these may be worth logging into profile collection
bool fromFreeList = true ;
2010-12-30 13:38:09 -05:00
Extent * e = DataFileMgr : : allocFromFreeList ( ns , size , capped ) ;
2011-11-12 12:01:55 -05:00
if ( e = = 0 ) {
fromFreeList = false ;
e = suitableFile ( ns , size , ! capped , enforceQuota ) - > createExtent ( ns , size , capped ) ;
}
LOG ( 1 ) < < " allocExtent " < < ns < < " size " < < size < < ' ' < < fromFreeList < < endl ;
return e ;
2010-12-30 13:38:09 -05:00
}
2011-01-04 00:40:41 -05:00
bool Database : : setProfilingLevel ( int newLevel , string & errmsg ) {
2009-12-29 12:08:13 -05:00
if ( profile = = newLevel )
return true ;
2011-01-04 00:40:41 -05:00
if ( newLevel < 0 | | newLevel > 2 ) {
2009-12-29 12:08:13 -05:00
errmsg = " profiling level has to be >=0 and <= 2 " ;
return false ;
}
2011-01-04 00:40:41 -05:00
if ( newLevel = = 0 ) {
2009-12-29 12:08:13 -05:00
profile = 0 ;
return true ;
}
2011-01-04 00:40:41 -05:00
2009-12-29 12:08:13 -05:00
assert ( cc ( ) . database ( ) = = this ) ;
2011-01-04 00:40:41 -05:00
if ( ! namespaceIndex . details ( profileName . c_str ( ) ) ) {
2011-04-23 13:50:53 -04:00
log ( ) < < " creating profile collection: " < < profileName < < endl ;
2009-12-29 12:08:13 -05:00
BSONObjBuilder spec ;
spec . appendBool ( " capped " , true ) ;
2011-08-16 17:22:54 -04:00
spec . append ( " size " , 1024 * 1024 ) ;
2011-07-26 17:38:00 -04:00
if ( ! userCreateNS ( profileName . c_str ( ) , spec . done ( ) , errmsg , false /* we don't replica profile messages */ ) ) {
2009-12-29 12:08:13 -05:00
return false ;
}
}
profile = newLevel ;
return true ;
}
2011-11-27 15:19:55 -05:00
bool Database : : exists ( int n ) const {
return boost : : filesystem : : exists ( fileName ( n ) ) ;
}
int Database : : numFiles ( ) const {
DEV assertDbAtLeastReadLocked ( this ) ;
return ( int ) _files . size ( ) ;
}
void Database : : flushFiles ( bool sync ) {
assertDbAtLeastReadLocked ( this ) ;
for ( vector < MongoDataFile * > : : iterator i = _files . begin ( ) ; i ! = _files . end ( ) ; i + + ) {
MongoDataFile * f = * i ;
f - > flush ( sync ) ;
2010-07-26 17:28:24 -04:00
}
}
2010-12-30 13:38:09 -05:00
long long Database : : fileSize ( ) const {
long long size = 0 ;
for ( int n = 0 ; exists ( n ) ; n + + )
size + = boost : : filesystem : : file_size ( fileName ( n ) ) ;
return size ;
}
2011-01-04 00:40:41 -05:00
Database * DatabaseHolder : : getOrCreate ( const string & ns , const string & path , bool & justCreated ) {
2011-11-26 21:05:53 -05:00
dbMutex . assertAtLeastReadLocked ( ) ;
2010-09-30 12:19:09 -04:00
DBs & m = _paths [ path ] ;
2011-01-04 00:40:41 -05:00
2010-09-30 12:19:09 -04:00
string dbname = _todb ( ns ) ;
2011-01-04 00:40:41 -05:00
2011-11-27 15:19:55 -05:00
{
DBs : : iterator i = m . find ( dbname ) ;
if ( i ! = m . end ( ) ) {
justCreated = false ;
return i - > second ;
}
2010-09-30 12:19:09 -04:00
}
2011-01-04 00:40:41 -05:00
2011-12-01 12:42:23 -05:00
// todo: protect against getting sprayed with requests for different db names that DNE -
// that would make the DBs map very large. not clear what to do to handle though,
// perhaps just log it, which is what we do here with the "> 40" :
2011-12-02 13:10:21 -05:00
bool cant = ! dbMutex . isWriteLocked ( ) ;
if ( logLevel > = 1 | | m . size ( ) > 40 | | cant | | DEBUG_BUILD ) {
log ( ) < < " opening db: " < < ( path = = dbpath ? " " : path ) < < ' ' < < dbname < < endl ;
}
2011-12-05 10:27:38 -05:00
massert ( 15927 , " can't open database in a read lock. if db was just closed, consider retrying the query. might otherwise indicate an internal error " , ! cant ) ;
2011-12-02 13:10:21 -05:00
2011-11-27 15:19:55 -05:00
Database * db = new Database ( dbname . c_str ( ) , justCreated , path ) ;
m [ dbname ] = db ;
2010-09-30 12:19:09 -04:00
_size + + ;
return db ;
}
2011-01-04 00:40:41 -05:00
2009-12-29 11:49:24 -05:00
} // namespace mongo