summaryrefslogtreecommitdiff
path: root/db/database.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'db/database.cpp')
-rw-r--r--db/database.cpp211
1 files changed, 187 insertions, 24 deletions
diff --git a/db/database.cpp b/db/database.cpp
index dde117f..d164ba5 100644
--- a/db/database.cpp
+++ b/db/database.cpp
@@ -20,15 +20,29 @@
#include "pdfile.h"
#include "database.h"
#include "instance.h"
+#include "clientcursor.h"
namespace mongo {
bool Database::_openAllFiles = false;
+ Database::~Database() {
+ magic = 0;
+ size_t n = files.size();
+ for ( size_t i = 0; i < n; i++ )
+ delete files[i];
+ if( ccByLoc.size() ) {
+ log() << "\n\n\nWARNING: ccByLoc not empty on database close! " << ccByLoc.size() << ' ' << name << endl;
+ }
+ }
+
Database::Database(const char *nm, bool& newDb, const string& _path )
- : name(nm), path(_path), namespaceIndex( path, name ) {
-
- { // check db name is valid
+ : name(nm), path(_path), namespaceIndex( path, name ),
+ profileName(name + ".system.profile") {
+ try {
+
+ {
+ // check db name is valid
size_t L = strlen(nm);
uassert( 10028 , "db name is empty", L > 0 );
uassert( 10029 , "bad db name [1]", *nm != '.' );
@@ -36,66 +50,184 @@ namespace mongo {
uassert( 10031 , "bad char(s) in db name", strchr(nm, ' ') == 0 );
uassert( 10032 , "db name too long", L < 64 );
}
-
+
newDb = namespaceIndex.exists();
profile = 0;
- profileName = name + ".system.profile";
{
vector<string> others;
getDatabaseNames( others , path );
-
- for ( unsigned i=0; i<others.size(); i++ ){
+
+ for ( unsigned i=0; i<others.size(); i++ ) {
if ( strcasecmp( others[i].c_str() , nm ) )
continue;
if ( strcmp( others[i].c_str() , nm ) == 0 )
continue;
-
+
stringstream ss;
ss << "db already exists with different case other: [" << others[i] << "] me [" << nm << "]";
uasserted( DatabaseDifferCaseCode , ss.str() );
}
}
-
+
// 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(...) {
+ // since destructor won't be called:
+ for ( size_t i = 0; i < files.size(); i++ )
+ delete files[i];
+ throw;
+ }
+ }
+
+ 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;
+ }
+
+ void Database::openAllFiles() {
+ int n = 0;
+ while( exists(n) ) {
+ 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() ) {
+ delete files[ n - 1 ];
+ files.pop_back();
+ }
+ }
+
+ MongoDataFile* Database::getFile( int n, int sizeNeeded , bool preallocateOnly) {
+ assert(this);
+
+ 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 {
+ if ( n > 100 )
+ out() << "getFile(): n=" << n << "?" << endl;
+ }
+ MongoDataFile* p = 0;
+ if ( !preallocateOnly ) {
+ while ( n >= (int) files.size() )
+ files.push_back(0);
+ p = files[n];
+ }
+ if ( p == 0 ) {
+ boost::filesystem::path fullName = fileName( n );
+ string fullNameString = fullName.string();
+ p = new MongoDataFile(n);
+ int minSize = 0;
+ if ( n != 0 && files[ n - 1 ] )
+ minSize = files[ n - 1 ]->getHeader()->fileLength;
+ 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
+ files[n] = p;
+ }
+ return preallocateOnly ? 0 : p;
+ }
+
+ MongoDataFile* Database::addAFile( int sizeNeeded, bool preallocateNextFile ) {
+ int n = (int) files.size();
+ MongoDataFile *ret = getFile( n, sizeNeeded );
+ if ( preallocateNextFile )
+ preallocateAFile();
+ return ret;
}
+ MongoDataFile* Database::suitableFile( int sizeNeeded, bool preallocate ) {
- bool Database::setProfilingLevel( int newLevel , string& errmsg ){
+ // check existing files
+ for ( int i=numFiles()-1; i>=0; i-- ) {
+ MongoDataFile* f = getFile( i );
+ if ( f->getHeader()->unusedLength >= sizeNeeded )
+ return f;
+ }
+
+ // allocate files until we either get one big enough or hit maxSize
+ for ( int i = 0; i < 8; i++ ) {
+ MongoDataFile* f = addAFile( sizeNeeded, preallocate );
+
+ if ( f->getHeader()->unusedLength >= sizeNeeded )
+ return f;
+
+ if ( f->getHeader()->fileLength >= MongoDataFile::maxSize() ) // this is as big as they get so might as well stop
+ return f;
+ }
+
+ return 0;
+ }
+
+ MongoDataFile* Database::newestFile() {
+ int n = numFiles();
+ if ( n == 0 )
+ return 0;
+ return getFile(n-1);
+ }
+
+
+ Extent* Database::allocExtent( const char *ns, int size, bool capped ) {
+ Extent *e = DataFileMgr::allocFromFreeList( ns, size, capped );
+ if( e )
+ return e;
+ return suitableFile( size, !capped )->createExtent( ns, size, capped );
+ }
+
+
+ bool Database::setProfilingLevel( int newLevel , string& errmsg ) {
if ( profile == newLevel )
return true;
-
- if ( newLevel < 0 || newLevel > 2 ){
+
+ if ( newLevel < 0 || newLevel > 2 ) {
errmsg = "profiling level has to be >=0 and <= 2";
return false;
}
-
- if ( newLevel == 0 ){
+
+ if ( newLevel == 0 ) {
profile = 0;
return true;
}
-
+
assert( cc().database() == this );
- if ( ! namespaceIndex.details( profileName.c_str() ) ){
+ if ( ! namespaceIndex.details( profileName.c_str() ) ) {
log(1) << "creating profile ns: " << profileName << endl;
BSONObjBuilder spec;
spec.appendBool( "capped", true );
spec.append( "size", 131072.0 );
- if ( ! userCreateNS( profileName.c_str(), spec.done(), errmsg , true ) ){
+ if ( ! userCreateNS( profileName.c_str(), spec.done(), errmsg , true ) ) {
return false;
}
}
@@ -103,26 +235,57 @@ namespace mongo {
return true;
}
- void Database::finishInit(){
+ void Database::finishInit() {
if ( cmdLine.defaultProfile == profile )
return;
-
+
string errmsg;
massert( 12506 , errmsg , setProfilingLevel( cmdLine.defaultProfile , errmsg ) );
}
- bool Database::validDBName( const string& ns ){
+ bool Database::validDBName( const string& ns ) {
if ( ns.size() == 0 || ns.size() > 64 )
return false;
size_t good = strcspn( ns.c_str() , "/\\. \"" );
return good == ns.size();
}
- void Database::flushFiles( bool sync ){
+ void Database::flushFiles( bool sync ) const {
dbMutex.assertAtLeastReadLocked();
- for ( unsigned i=0; i<files.size(); i++ ){
+ for ( unsigned i=0; i<files.size(); i++ ) {
files[i]->flush( sync );
}
}
+ 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;
+ }
+
+ Database* DatabaseHolder::getOrCreate( const string& ns , const string& path , bool& justCreated ) {
+ dbMutex.assertWriteLocked();
+ DBs& m = _paths[path];
+
+ string dbname = _todb( ns );
+
+ Database* & db = m[dbname];
+ if ( db ) {
+ justCreated = false;
+ return db;
+ }
+
+ log(1) << "Accessing: " << dbname << " for the first time" << endl;
+ try {
+ db = new Database( dbname.c_str() , justCreated , path );
+ }
+ catch ( ... ) {
+ m.erase( dbname );
+ throw;
+ }
+ _size++;
+ return db;
+ }
+
} // namespace mongo