diff options
author | Eliot Horowitz <eliot@10gen.com> | 2014-05-19 16:05:41 -0400 |
---|---|---|
committer | Eliot Horowitz <eliot@10gen.com> | 2014-05-19 16:05:41 -0400 |
commit | db0e1a91fdce2fa43584408a87d637cfe4bcc2ec (patch) | |
tree | 8cbefae4f2f43f41f4a512f5b8f250b1662dfbbf /src/mongo/db | |
parent | f2bfd36a6f3eb7e7e2587268be3cc12636703d42 (diff) | |
download | mongo-db0e1a91fdce2fa43584408a87d637cfe4bcc2ec.tar.gz |
SERVER-13637: add CollectionCatalogEntry, start Database layer
Diffstat (limited to 'src/mongo/db')
62 files changed, 1883 insertions, 1190 deletions
diff --git a/src/mongo/db/catalog/collection.cpp b/src/mongo/db/catalog/collection.cpp index d2f4f5f7190..861a3bbb02f 100644 --- a/src/mongo/db/catalog/collection.cpp +++ b/src/mongo/db/catalog/collection.cpp @@ -35,6 +35,7 @@ #include "mongo/db/clientcursor.h" #include "mongo/db/commands/server_status_metric.h" #include "mongo/db/curop.h" +#include "mongo/db/catalog/collection_catalog_entry.h" #include "mongo/db/catalog/database.h" #include "mongo/db/catalog/index_create.h" #include "mongo/db/dbhelpers.h" @@ -44,11 +45,9 @@ #include "mongo/db/structure/catalog/namespace_details.h" #include "mongo/db/structure/catalog/namespace_details_rsv1_metadata.h" #include "mongo/db/structure/record_store_v1_capped.h" -#include "mongo/db/structure/record_store_v1_simple.h" #include "mongo/db/repl/rs.h" #include "mongo/db/storage/extent.h" #include "mongo/db/storage/extent_manager.h" -#include "mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h" #include "mongo/db/storage/record.h" #include "mongo/db/auth/user_document_parser.h" // XXX-ANDY @@ -78,33 +77,20 @@ namespace mongo { Collection::Collection( OperationContext* txn, const StringData& fullNS, - NamespaceDetails* details, + CollectionCatalogEntry* details, + RecordStore* recordStore, Database* database ) : _ns( fullNS ), + _details( details ), + _recordStore( recordStore ), + _database( database ), _infoCache( this ), - _indexCatalog( this, details ), + _indexCatalog( this ), _cursorCache( fullNS ) { - - _details = details; - _database = database; - - if ( details->isCapped() ) { - _recordStore.reset( new CappedRecordStoreV1( txn, - this, - _ns.ns(), - new NamespaceDetailsRSV1MetaData( details ), - database->getExtentManager(), - _ns.coll() == "system.indexes" ) ); - } - else { - _recordStore.reset( new SimpleRecordStoreV1( txn, - _ns.ns(), - new NamespaceDetailsRSV1MetaData( details ), - database->getExtentManager(), - _ns.coll() == "system.indexes" ) ); - } _magic = 1357924; _indexCatalog.init(txn); + if ( isCapped() ) + _recordStore->setCappedDeleteCallback( this ); } Collection::~Collection() { @@ -203,12 +189,7 @@ namespace mongo { return StatusWith<DiskLoc>( ret ); } - StatusWith<DiskLoc> status = _insertDocument( txn, docToInsert, enforceQuota ); - if ( status.isOK() ) { - _details->paddingFits( txn ); - } - - return status; + return _insertDocument( txn, docToInsert, enforceQuota ); } StatusWith<DiskLoc> Collection::insertDocument( OperationContext* txn, @@ -364,21 +345,21 @@ namespace mongo { } } - if ( oldRecord->netLength() < objNew.objsize() ) { - // doesn't fit, have to move to new location + // this can callback into Collection::recordStoreGoingToMove + StatusWith<DiskLoc> newLocation = _recordStore->updateRecord( txn, + oldLocation, + objNew.objdata(), + objNew.objsize(), + enforceQuota ? largestFileNumberInQuota() : 0, + this ); - if ( isCapped() ) - return StatusWith<DiskLoc>( ErrorCodes::InternalError, - "failing update: objects in a capped ns cannot grow", - 10003 ); + if ( !newLocation.isOK() ) { + return newLocation; + } - moveCounter.increment(); - _details->paddingTooSmall( txn ); + _infoCache.notifyOfWriteOp(); - // unindex old record, don't delete - // this way, if inserting new doc fails, we can re-index this one - _cursorCache.invalidateDocument(oldLocation, INVALIDATION_DELETION); - _indexCatalog.unindexRecord(txn, objOld, oldLocation, true); + if ( newLocation.getValue() != oldLocation ) { if ( debug ) { if (debug->nmoved == -1) // default of -1 rather than 0 @@ -387,24 +368,11 @@ namespace mongo { debug->nmoved += 1; } - StatusWith<DiskLoc> loc = _insertDocument( txn, objNew, enforceQuota ); - - if ( loc.isOK() ) { - // insert successful, now lets deallocate the old location - // remember its already unindexed - _recordStore->deleteRecord( txn, oldLocation ); - } - else { - // new doc insert failed, so lets re-index the old document and location - _indexCatalog.indexRecord(txn, objOld, oldLocation); - } + _indexCatalog.indexRecord(txn, objNew, newLocation.getValue()); - return loc; + return newLocation; } - _infoCache.notifyOfWriteOp(); - _details->paddingFits( txn ); - if ( debug ) debug->keyUpdates = 0; @@ -424,13 +392,20 @@ namespace mongo { // Broadcast the mutation so that query results stay correct. _cursorCache.invalidateDocument(oldLocation, INVALIDATION_MUTATION); - // update in place - int sz = objNew.objsize(); - memcpy(txn->recoveryUnit()->writingPtr(oldRecord->data(), sz), objNew.objdata(), sz); + return newLocation; + } - return StatusWith<DiskLoc>( oldLocation ); + Status Collection::recordStoreGoingToMove( OperationContext* txn, + const DiskLoc& oldLocation, + const char* oldBuffer, + size_t oldSize ) { + moveCounter.increment(); + _cursorCache.invalidateDocument(oldLocation, INVALIDATION_DELETION); + _indexCatalog.unindexRecord(txn, BSONObj(oldBuffer), oldLocation, true); + return Status::OK(); } + Status Collection::updateDocumentWithDamages( OperationContext* txn, const DiskLoc& loc, const char* damangeSource, @@ -438,36 +413,7 @@ namespace mongo { // Broadcast the mutation so that query results stay correct. _cursorCache.invalidateDocument(loc, INVALIDATION_MUTATION); - - _details->paddingFits( txn ); - - Record* rec = _recordStore->recordFor( loc ); - char* root = rec->data(); - - // All updates were in place. Apply them via durability and writing pointer. - mutablebson::DamageVector::const_iterator where = damages.begin(); - const mutablebson::DamageVector::const_iterator end = damages.end(); - for( ; where != end; ++where ) { - const char* sourcePtr = damangeSource + where->sourceOffset; - void* targetPtr = txn->recoveryUnit()->writingPtr(root + where->targetOffset, where->size); - std::memcpy(targetPtr, sourcePtr, where->size); - } - - return Status::OK(); - } - - ExtentManager* Collection::getExtentManager() { - verify( ok() ); - return _database->getExtentManager(); - } - - const ExtentManager* Collection::getExtentManager() const { - verify( ok() ); - return _database->getExtentManager(); - } - - void Collection::increaseStorageSize(OperationContext* txn, int size, bool enforceQuota) { - _recordStore->increaseStorageSize(txn, size, enforceQuota ? largestFileNumberInQuota() : 0); + return _recordStore->updateWithDamages( txn, loc, damangeSource, damages ); } int Collection::largestFileNumberInQuota() const { @@ -483,19 +429,8 @@ namespace mongo { return storageGlobalParams.quotaFiles; } - void Collection::appendCustomStats( BSONObjBuilder* result, double scale ) const { - result->append( "lastExtentSize", _details->lastExtentSize() / scale ); - result->append( "paddingFactor", _details->paddingFactor() ); - result->append( "userFlags", _details->userFlags() ); - - if ( isCapped() ) { - result->appendBool( "capped", true ); - result->appendNumber( "max", _details->maxCappedDocs() ); - } - } - bool Collection::isCapped() const { - return _details->isCapped(); + return _recordStore->isCapped(); } uint64_t Collection::numRecords() const { @@ -643,52 +578,4 @@ namespace mongo { return Status::OK(); } - bool Collection::isUserFlagSet( int flag ) const { - return _details->isUserFlagSet( flag ); - } - - bool Collection::setUserFlag( OperationContext* txn, int flag ) { - if ( !_details->setUserFlag( txn, flag ) ) - return false; - _syncUserFlags(txn); - return true; - } - - bool Collection::clearUserFlag( OperationContext* txn, int flag ) { - if ( !_details->clearUserFlag( txn, flag ) ) - return false; - _syncUserFlags(txn); - return true; - } - - void Collection::_syncUserFlags(OperationContext* txn) { - if ( _ns.coll() == "system.namespaces" ) - return; - string system_namespaces = _ns.getSisterNS( "system.namespaces" ); - Collection* coll = _database->getCollection( txn, system_namespaces ); - - DiskLoc oldLocation = Helpers::findOne( coll, BSON( "name" << _ns.ns() ), false ); - fassert( 17247, !oldLocation.isNull() ); - - BSONObj oldEntry = coll->docFor( oldLocation ); - - BSONObj newEntry = applyUpdateOperators( oldEntry, - BSON( "$set" << - BSON( "options.flags" << - _details->userFlags() ) ) ); - - StatusWith<DiskLoc> loc = coll->updateDocument( txn, oldLocation, newEntry, false, NULL ); - if ( !loc.isOK() ) { - // TODO: should this be an fassert? - error() << "syncUserFlags failed! " - << " ns: " << _ns - << " error: " << loc.toString(); - } - - } - - void Collection::setMaxCappedDocs( OperationContext* txn, long long max ) { - _details->setMaxCappedDocs( txn, max ); - } - } diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h index 8cccf53dc4a..bad0a78ae10 100644 --- a/src/mongo/db/catalog/collection.h +++ b/src/mongo/db/catalog/collection.h @@ -46,9 +46,9 @@ namespace mongo { + class CollectionCatalogEntry; class Database; class ExtentManager; - class NamespaceDetails; class IndexCatalog; class MultiIndexBlock; class OperationContext; @@ -100,19 +100,20 @@ namespace mongo { * this is NOT safe through a yield right now * not sure if it will be, or what yet */ - class Collection : CappedDocumentDeleteCallback { + class Collection : CappedDocumentDeleteCallback, UpdateMoveNotifier { public: Collection( OperationContext* txn, const StringData& fullNS, - NamespaceDetails* details, - Database* database ); + CollectionCatalogEntry* details, // takes ownership + RecordStore* recordStore, // takes ownership + Database* database ); // does not own ~Collection(); bool ok() const { return _magic == 1357924; } - NamespaceDetails* detailsWritable() { return _details; } // TODO: remove - const NamespaceDetails* detailsDeprecated() const { return _details; } + CollectionCatalogEntry* getCatalogEntry() { return _details.get(); } + const CollectionCatalogEntry* getCatalogEntry() const { return _details.get(); } CollectionInfoCache* infoCache() { return &_infoCache; } const CollectionInfoCache* infoCache() const { return &_infoCache; } @@ -123,6 +124,7 @@ namespace mongo { IndexCatalog* getIndexCatalog() { return &_indexCatalog; } const RecordStore* getRecordStore() const { return _recordStore.get(); } + RecordStore* getRecordStore() { return _recordStore.get(); } CollectionCursorCache* cursorCache() const { return &_cursorCache; } @@ -236,23 +238,10 @@ namespace mongo { // ----------- - - // this is temporary, moving up from DB for now - // this will add a new extent the collection - // the new extent will be returned - // it will have been added to the linked list already - void increaseStorageSize( OperationContext* txn, int size, bool enforceQuota ); - // // Stats // - /** - * @param scaleSize - amount by which to scale size metrics - * appends any custom stats from the RecordStore or other unique stats - */ - void appendCustomStats( BSONObjBuilder* resuits, double scaleSize = 1 ) const; - bool isCapped() const; uint64_t numRecords() const; @@ -266,16 +255,15 @@ namespace mongo { return static_cast<int>( dataSize() / n ); } - // TODO(erh) - below till next mark are suspect - bool isUserFlagSet( int flag ) const; - bool setUserFlag( OperationContext* txn, int flag ); - bool clearUserFlag( OperationContext* txn, int flag ); - - void setMaxCappedDocs( OperationContext* txn, long long max ); // --- end suspect things private: + Status recordStoreGoingToMove( OperationContext* txn, + const DiskLoc& oldLocation, + const char* oldBuffer, + size_t oldSize ); + Status aboutToDeleteCapped( OperationContext* txn, const DiskLoc& loc ); /** @@ -287,28 +275,15 @@ namespace mongo { const BSONObj& doc, bool enforceQuota ); - void _compactExtent(OperationContext* txn, - const DiskLoc diskloc, - int extentNumber, - MultiIndexBlock& indexesToInsertTo, - const CompactOptions* compactOptions, - CompactStats* stats ); - - void _syncUserFlags(OperationContext* txn); // TODO: this is bizarre, should go away - - // @return 0 for inf., otherwise a number of files int largestFileNumberInQuota() const; - ExtentManager* getExtentManager(); - const ExtentManager* getExtentManager() const; - int _magic; NamespaceString _ns; - NamespaceDetails* _details; - Database* _database; + scoped_ptr<CollectionCatalogEntry> _details; scoped_ptr<RecordStore> _recordStore; + Database* _database; CollectionInfoCache _infoCache; IndexCatalog _indexCatalog; diff --git a/src/mongo/db/catalog/collection_catalog_entry.h b/src/mongo/db/catalog/collection_catalog_entry.h new file mode 100644 index 00000000000..b3c6b2c890c --- /dev/null +++ b/src/mongo/db/catalog/collection_catalog_entry.h @@ -0,0 +1,97 @@ +// collection_catalog_entry.h + +/** + * Copyright (C) 2014 MongoDB 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/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "mongo/base/string_data.h" +#include "mongo/db/diskloc.h" +#include "mongo/db/namespace_string.h" + +namespace mongo { + + class IndexDescriptor; + class OperationContext; + + class CollectionCatalogEntry { + public: + CollectionCatalogEntry( const StringData& ns ) + : _ns( ns ){ + } + virtual ~CollectionCatalogEntry(){} + + const NamespaceString& ns() const { return _ns; } + + // ------- indexes ---------- + + virtual int getTotalIndexCount() const = 0; + + virtual int getCompletedIndexCount() const = 0; + + virtual int getMaxAllowedIndexes() const = 0; + + virtual void getAllIndexes( std::vector<std::string>* names ) const = 0; + + virtual BSONObj getIndexSpec( const StringData& idxName ) const = 0; + + virtual bool isIndexMultikey( const StringData& indexName) const = 0; + + virtual bool setIndexIsMultikey(OperationContext* txn, + const StringData& indexName, + bool multikey = true) = 0; + + virtual DiskLoc getIndexHead( const StringData& indexName ) const = 0; + + virtual void setIndexHead( OperationContext* txn, + const StringData& indexName, + const DiskLoc& newHead ) = 0; + + virtual bool isIndexReady( const StringData& indexName ) const = 0; + + virtual Status removeIndex( OperationContext* txn, + const StringData& indexName ) = 0; + + virtual Status prepareForIndexBuild( OperationContext* txn, + const IndexDescriptor* spec ) = 0; + + virtual void indexBuildSuccess( OperationContext* txn, + const StringData& indexName ) = 0; + + /* Updates the expireAfterSeconds field of the given index to the value in newExpireSecs. + * The specified index must already contain an expireAfterSeconds field, and the value in + * that field and newExpireSecs must both be numeric. + */ + virtual void updateTTLSetting( OperationContext* txn, + const StringData& idxName, + long long newExpireSeconds ) = 0; + private: + NamespaceString _ns; + }; + +} diff --git a/src/mongo/db/catalog/database.cpp b/src/mongo/db/catalog/database.cpp index 37d82ac8550..d224e3a2a64 100644 --- a/src/mongo/db/catalog/database.cpp +++ b/src/mongo/db/catalog/database.cpp @@ -39,6 +39,7 @@ #include "mongo/db/auth/auth_index_d.h" #include "mongo/db/background.h" #include "mongo/db/clientcursor.h" +#include "mongo/db/catalog/collection_catalog_entry.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/dbhelpers.h" #include "mongo/db/structure/catalog/index_details.h" @@ -52,6 +53,7 @@ #include "mongo/db/storage/extent.h" #include "mongo/db/storage/extent_manager.h" #include "mongo/db/operation_context_impl.h" +#include "mongo/db/storage/mmap_v1/mmap_v1_engine.h" #include "mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h" #include "mongo/db/storage_options.h" #include "mongo/db/structure/catalog/namespace_details.h" @@ -59,8 +61,6 @@ namespace mongo { - MONGO_EXPORT_SERVER_PARAMETER(newCollectionsUsePowerOf2Sizes, bool, true); - Status CollectionOptions::parse( const BSONObj& options ) { reset(); @@ -198,8 +198,7 @@ namespace mongo { Database::Database(OperationContext* txn, const char *nm, bool& newDb, const string& path ) : _name(nm), _path(path), - _namespaceIndex( _path, _name ), - _extentManager(new MmapV1ExtentManager(_name, _path, storageGlobalParams.directoryperdb)), + _dbEntry(new MMAP1DatabaseCatalogEntry( txn, _name, _path, storageGlobalParams.directoryperdb) ), _profileName(_name + ".system.profile"), _namespacesName(_name + ".system.namespaces"), _indexesName(_name + ".system.indexes"), @@ -211,54 +210,11 @@ namespace mongo { uasserted( 10028, status.toString() ); } - try { - newDb = _namespaceIndex.exists(); - _profile = serverGlobalParams.defaultProfile; - checkDuplicateUncasedNames(true); - - // If already exists, open. Otherwise behave as if empty until - // there's a write, then open. - if (!newDb) { - _namespaceIndex.init( txn ); - openAllFiles(txn); - - // upgrade freelist - string oldFreeList = _name + ".$freelist"; - NamespaceDetails* details = _namespaceIndex.details( oldFreeList ); - if ( details ) { - if ( !details->firstExtent().isNull() ) { - _extentManager->freeExtents(txn, - details->firstExtent(), - details->lastExtent()); - } - _namespaceIndex.kill_ns( txn, oldFreeList ); - } - } - _magic = 781231; - } - catch(std::exception& e) { - log() << "warning database " << path << " " << nm << " could not be opened" << endl; - DBException* dbe = dynamic_cast<DBException*>(&e); - if ( dbe != 0 ) { - log() << "DBException " << dbe->getCode() << ": " << e.what() << endl; - } - else { - log() << e.what() << endl; - } - _extentManager.reset(); - throw; - } + _profile = serverGlobalParams.defaultProfile; + _magic = 781231; + newDb = !_dbEntry->exists(); } - void Database::checkDuplicateUncasedNames(bool inholderlock) const { - string duplicate = duplicateUncasedName(inholderlock, _name, _path ); - if ( !duplicate.empty() ) { - stringstream ss; - ss << "db already exists with different case already have: [" << duplicate - << "] trying to create [" << _name << "]"; - uasserted( DatabaseDifferCaseCode , ss.str() ); - } - } /*static*/ string Database::duplicateUncasedName( bool inholderlock, const string &name, const string &path, set< string > *duplicates ) { @@ -296,17 +252,6 @@ namespace mongo { return ""; } - // todo : we stop once a datafile dne. - // if one datafile were missing we should keep going for - // repair purposes yet we do not. - void Database::openAllFiles(OperationContext* txn) { - verify(this); - Status s = _extentManager->init(txn); - if ( !s.isOK() ) { - msgasserted( 16966, str::stream() << "_extentManager.init failed: " << s.toString() ); - } - } - void Database::clearTmpCollections(OperationContext* txn) { Lock::assertWriteLocked( _name ); @@ -347,11 +292,11 @@ namespace mongo { } } - long long Database::fileSize() const { return _extentManager->fileSize(); } + long long Database::fileSize() const { return getExtentManager()->fileSize(); } - int Database::numFiles() const { return _extentManager->numFiles(); } + int Database::numFiles() const { return getExtentManager()->numFiles(); } - void Database::flushFiles( bool sync ) { return _extentManager->flushFiles( sync ); } + void Database::flushFiles( bool sync ) { return getExtentManager()->flushFiles( sync ); } bool Database::setProfilingLevel( OperationContext* txn, int newLevel , string& errmsg ) { if ( _profile == newLevel ) @@ -472,7 +417,7 @@ namespace mongo { } Collection* Database::getCollection( OperationContext* txn, const StringData& ns ) { - verify( _name == nsToDatabaseSubstring( ns ) ); + invariant( _name == nsToDatabaseSubstring( ns ) ); scoped_lock lk( _collectionLock ); @@ -480,24 +425,28 @@ namespace mongo { if ( it != _collections.end() ) { if ( it->second ) { DEV { - NamespaceDetails* details = _namespaceIndex.details( ns ); + /* XXX put back? + NamespaceDetails* details = _dbEntry->namespaceIndex().details( ns ); if ( details != it->second->_details ) { log() << "about to crash for mismatch on ns: " << ns << " current: " << (void*)details << " cached: " << (void*)it->second->_details; } verify( details == it->second->_details ); + */ } return it->second; } } - NamespaceDetails* details = _namespaceIndex.details( ns ); - if ( !details ) { + auto_ptr<CollectionCatalogEntry> catalogEntry( _dbEntry->getCollectionCatalogEntry( txn, ns ) ); + if ( !catalogEntry.get() ) return NULL; - } - Collection* c = new Collection( txn, ns, details, this ); + auto_ptr<RecordStore> rs( _dbEntry->getRecordStore( txn, ns ) ); + invariant( rs.get() ); // if catalogEntry exists, so should this + + Collection* c = new Collection( txn, ns, catalogEntry.release(), rs.release(), this ); _collections[ns] = c; return c; } @@ -514,7 +463,7 @@ namespace mongo { if ( !s.isOK() ) return s; - NamespaceDetails* details = _namespaceIndex.details( toNS ); + NamespaceDetails* details = _dbEntry->namespaceIndex().details( toNS ); verify( details ); audit::logRenameCollection( currentClient.get(), fromNS, toNS ); @@ -578,17 +527,16 @@ namespace mongo { const StringData& fromNS, const StringData& toNS, bool stayTemp ) { - // TODO: make it so we dont't need to do this string fromNSString = fromNS.toString(); string toNSString = toNS.toString(); // some sanity checking - NamespaceDetails* fromDetails = _namespaceIndex.details( fromNS ); + NamespaceDetails* fromDetails = _dbEntry->namespaceIndex().details( fromNS ); if ( !fromDetails ) return Status( ErrorCodes::BadValue, "from namespace doesn't exist" ); - if ( _namespaceIndex.details( toNS ) ) + if ( _dbEntry->namespaceIndex().details( toNS ) ) return Status( ErrorCodes::BadValue, "to namespace already exists" ); // remove anything cached @@ -605,25 +553,25 @@ namespace mongo { // ---- // this could throw, but if it does we're ok - _namespaceIndex.add_ns( txn, toNS, fromDetails ); - NamespaceDetails* toDetails = _namespaceIndex.details( toNS ); + _dbEntry->namespaceIndex().add_ns( txn, toNS, fromDetails ); + NamespaceDetails* toDetails = _dbEntry->namespaceIndex().details( toNS ); try { toDetails->copyingFrom(txn, toNSString.c_str(), - _namespaceIndex, + _dbEntry->namespaceIndex(), fromDetails); // fixes extraOffset } catch( DBException& ) { // could end up here if .ns is full - if so try to clean up / roll back a little - _namespaceIndex.kill_ns( txn, toNSString ); + _dbEntry->namespaceIndex().kill_ns( txn, toNSString ); _clearCollectionCache(toNSString); throw; } // at this point, code .ns stuff moved - _namespaceIndex.kill_ns( txn, fromNSString ); + _dbEntry->namespaceIndex().kill_ns( txn, fromNSString ); _clearCollectionCache(fromNSString); fromDetails = NULL; @@ -671,24 +619,13 @@ namespace mongo { return c; } - namespace { - int _massageExtentSize( const ExtentManager* em, long long size ) { - if ( size < em->minSize() ) - return em->minSize(); - if ( size > em->maxSize() ) - return em->maxSize(); - return static_cast<int>( size ); - } - } - Collection* Database::createCollection( OperationContext* txn, const StringData& ns, const CollectionOptions& options, bool allocateDefaultSpace, bool createIdIndex ) { - massert( 17399, "collection already exists", _namespaceIndex.details( ns ) == NULL ); + massert( 17399, "collection already exists", _dbEntry->namespaceIndex().details( ns ) == NULL ); massertNamespaceNotIndex( ns, "createCollection" ); - _namespaceIndex.init( txn ); if ( serverGlobalParams.configsvr && !( ns.startsWith( "config." ) || @@ -709,55 +646,13 @@ namespace mongo { audit::logCreateCollection( currentClient.get(), ns ); - _namespaceIndex.add_ns( txn, ns, DiskLoc(), options.capped ); - BSONObj optionsAsBSON = options.toBSON(); - _addNamespaceToCatalog( txn, ns, &optionsAsBSON ); + Status status = _dbEntry->createCollection( txn, ns, + options, allocateDefaultSpace ); + massertStatusOK( status ); - Collection* collection = getCollection( txn, ns ); - massert( 17400, "_namespaceIndex.add_ns failed?", collection ); - // allocation strategy set explicitly in flags or by server-wide default - if ( !options.capped ) { - if ( options.flagsSet ) { - collection->setUserFlag( txn, options.flags ); - } - else if ( newCollectionsUsePowerOf2Sizes ) { - collection->setUserFlag( txn, NamespaceDetails::Flag_UsePowerOf2Sizes ); - } - } - - if ( options.cappedMaxDocs > 0 ) - collection->setMaxCappedDocs( txn, options.cappedMaxDocs ); - - if ( allocateDefaultSpace ) { - if ( options.initialNumExtents > 0 ) { - int size = _massageExtentSize( _extentManager.get(), - options.cappedSize ); - for ( int i = 0; i < options.initialNumExtents; i++ ) { - collection->increaseStorageSize( txn, size, false ); - } - } - else if ( !options.initialExtentSizes.empty() ) { - for ( size_t i = 0; i < options.initialExtentSizes.size(); i++ ) { - int size = options.initialExtentSizes[i]; - size = _massageExtentSize( _extentManager.get(), - size ); - collection->increaseStorageSize( txn, size, false ); - } - } - else if ( options.capped ) { - // normal - while ( collection->getRecordStore()->storageSize() < options.cappedSize ) { - int sz = _massageExtentSize( _extentManager.get(), - options.cappedSize - collection->getRecordStore()->storageSize() ); - sz &= 0xffffff00; - collection->increaseStorageSize( txn, sz, true ); - } - } - else { - collection->increaseStorageSize( txn, _extentManager->initialSize( 128 ), false ); - } - } + Collection* collection = getCollection( txn, ns ); + invariant( collection ); if ( createIdIndex ) { if ( collection->requiresIdIndex() ) { @@ -801,7 +696,7 @@ namespace mongo { Status Database::_dropNS( OperationContext* txn, const StringData& ns ) { - NamespaceDetails* d = _namespaceIndex.details( ns ); + NamespaceDetails* d = _dbEntry->namespaceIndex().details( ns ); if ( !d ) return Status( ErrorCodes::NamespaceNotFound, str::stream() << "ns not found: " << ns ); @@ -815,28 +710,48 @@ namespace mongo { } // free extents - if( !d->firstExtent().isNull() ) { - _extentManager->freeExtents(txn, d->firstExtent(), d->lastExtent()); - d->setFirstExtentInvalid(txn); - d->setLastExtentInvalid(txn); + if( !d->firstExtent.isNull() ) { + getExtentManager()->freeExtents(txn, d->firstExtent, d->lastExtent); + *txn->recoveryUnit()->writing( &d->firstExtent ) = DiskLoc().setInvalid(); + *txn->recoveryUnit()->writing( &d->lastExtent ) = DiskLoc().setInvalid(); } // remove from the catalog hashtable - _namespaceIndex.kill_ns( txn, ns ); + _dbEntry->namespaceIndex().kill_ns( txn, ns ); return Status::OK(); } void Database::getFileFormat( int* major, int* minor ) { - if ( _extentManager->numFiles() == 0 ) { + if ( getExtentManager()->numFiles() == 0 ) { *major = 0; *minor = 0; return; } OperationContextImpl txn; // TODO get rid of this once reads need transactions - const DataFile* df = _extentManager->getFile( &txn, 0 ); + const DataFile* df = getExtentManager()->getFile( &txn, 0 ); *major = df->getHeader()->version; *minor = df->getHeader()->versionMinor; } + MmapV1ExtentManager* Database::getExtentManager() { + return _dbEntry->getExtentManager(); + } + + const MmapV1ExtentManager* Database::getExtentManager() const { + return _dbEntry->getExtentManager(); + } + + const NamespaceIndex* Database::namespaceIndex() const { + return &_dbEntry->namespaceIndex(); + } + + NamespaceIndex* Database::namespaceIndex() { + return &_dbEntry->namespaceIndex(); + } + + bool Database::isEmpty() { + return !_dbEntry->namespaceIndex().allocated(); + } + } // namespace mongo diff --git a/src/mongo/db/catalog/database.h b/src/mongo/db/catalog/database.h index 33d9b198517..27b7aeacab0 100644 --- a/src/mongo/db/catalog/database.h +++ b/src/mongo/db/catalog/database.h @@ -30,9 +30,11 @@ #pragma once +#include "mongo/bson/bsonobj.h" #include "mongo/db/namespace_string.h" -#include "mongo/db/structure/catalog/namespace_index.h" #include "mongo/db/storage_options.h" +#include "mongo/util/concurrency/mutex.h" +#include "mongo/util/mongoutils/str.h" #include "mongo/util/string_map.h" namespace mongo { @@ -41,8 +43,10 @@ namespace mongo { class DataFile; class ExtentManager; class IndexCatalog; + class MMAP1DatabaseCatalogEntry; class MmapV1ExtentManager; class NamespaceDetails; + class NamespaceIndex; class OperationContext; struct CollectionOptions { @@ -118,7 +122,7 @@ namespace mongo { */ bool isOk() const { return _magic == 781231; } - bool isEmpty() { return ! _namespaceIndex.allocated(); } + bool isEmpty(); /** * total file size of Database in bytes @@ -141,7 +145,7 @@ namespace mongo { * ns=foo.bar, db=foo returns true */ bool ownsNS( const std::string& ns ) const { - if ( ! startsWith( ns , _name ) ) + if ( ! mongoutils::str::startsWith( ns , _name ) ) return false; return ns[_name.size()] == '.'; } @@ -149,12 +153,12 @@ namespace mongo { int getProfilingLevel() const { return _profile; } const char* getProfilingNS() const { return _profileName.c_str(); } - const NamespaceIndex& namespaceIndex() const { return _namespaceIndex; } - NamespaceIndex& namespaceIndex() { return _namespaceIndex; } + const NamespaceIndex* namespaceIndex() const; + NamespaceIndex* namespaceIndex(); // TODO: do not think this method should exist, so should try and encapsulate better - MmapV1ExtentManager* getExtentManager() { return _extentManager.get(); } - const MmapV1ExtentManager* getExtentManager() const { return _extentManager.get(); } + MmapV1ExtentManager* getExtentManager(); + const MmapV1ExtentManager* getExtentManager() const; Status dropCollection( OperationContext* txn, const StringData& fullns ); @@ -191,8 +195,12 @@ namespace mongo { * @return name of an existing database with same text name but different * casing, if one exists. Otherwise the empty std::string is returned. If * 'duplicates' is specified, it is filled with all duplicate names. + // TODO move??? */ - static std::string duplicateUncasedName( bool inholderlockalready, const std::string &name, const std::string &path, std::set< std::string > *duplicates = 0 ); + static string duplicateUncasedName( bool inholderlockalready, + const std::string &name, + const std::string &path, + std::set< std::string > *duplicates = 0 ); static Status validateDBName( const StringData& dbname ); @@ -218,14 +226,6 @@ namespace mongo { */ Status _dropNS( OperationContext* txn, const StringData& ns ); - /** - * @throws DatabaseDifferCaseCode if the name is a duplicate based on - * case insensitive matching. - */ - void checkDuplicateUncasedNames(bool inholderlockalready) const; - - void openAllFiles(OperationContext* txn); - Status _renameSingleNamespace( OperationContext* txn, const StringData& fromNS, const StringData& toNS, @@ -234,8 +234,7 @@ namespace mongo { const std::string _name; // "alleyinsider" const std::string _path; // "/data/db" - NamespaceIndex _namespaceIndex; - boost::scoped_ptr<MmapV1ExtentManager> _extentManager; + boost::scoped_ptr<MMAP1DatabaseCatalogEntry> _dbEntry; const std::string _profileName; // "alleyinsider.system.profile" const std::string _namespacesName; // "alleyinsider.system.namespaces" @@ -250,7 +249,7 @@ namespace mongo { // but it points to a much more useful data structure typedef StringMap< Collection* > CollectionMap; CollectionMap _collections; - mutex _collectionLock; + mongo::mutex _collectionLock; friend class Collection; friend class NamespaceDetails; diff --git a/src/mongo/db/catalog/database_holder.cpp b/src/mongo/db/catalog/database_holder.cpp index a944ce18631..3516cc06a09 100644 --- a/src/mongo/db/catalog/database_holder.cpp +++ b/src/mongo/db/catalog/database_holder.cpp @@ -37,6 +37,7 @@ #include "mongo/db/catalog/database_holder.h" #include "mongo/db/d_concurrency.h" #include "mongo/db/operation_context_impl.h" +#include "mongo/db/storage/mmap_v1/dur.h" namespace mongo { diff --git a/src/mongo/db/catalog/index_catalog.cpp b/src/mongo/db/catalog/index_catalog.cpp index 140b98f5bc4..9a804f7e5ae 100644 --- a/src/mongo/db/catalog/index_catalog.cpp +++ b/src/mongo/db/catalog/index_catalog.cpp @@ -34,6 +34,7 @@ #include "mongo/db/audit.h" #include "mongo/db/background.h" +#include "mongo/db/catalog/collection_catalog_entry.h" #include "mongo/db/catalog/collection.h" #include "mongo/db/catalog/index_create.h" #include "mongo/db/catalog/index_key_validate.h" @@ -41,15 +42,8 @@ #include "mongo/db/clientcursor.h" #include "mongo/db/curop.h" #include "mongo/db/field_ref.h" -#include "mongo/db/index/2d_access_method.h" -#include "mongo/db/index/btree_access_method.h" -#include "mongo/db/index/btree_based_access_method.h" -#include "mongo/db/index/fts_access_method.h" -#include "mongo/db/index/hash_access_method.h" -#include "mongo/db/index/haystack_access_method.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_descriptor.h" -#include "mongo/db/index/s2_access_method.h" #include "mongo/db/index_legacy.h" #include "mongo/db/index_names.h" #include "mongo/db/jsobj.h" @@ -60,14 +54,14 @@ #include "mongo/db/repl/rs.h" // this is ugly #include "mongo/db/storage/data_file.h" #include "mongo/db/storage/extent_manager.h" -#include "mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h" #include "mongo/db/operation_context.h" -#include "mongo/db/structure/catalog/namespace_details.h" -#include "mongo/db/structure/catalog/namespace_details_rsv1_metadata.h" -#include "mongo/db/structure/record_store_v1_simple.h" #include "mongo/util/assert_util.h" #include "mongo/util/log.h" +#include "mongo/db/storage/mmap_v1/mmap_v1_engine.h" // XXX +#include "mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h" // XXX +#include "mongo/db/storage/extent_manager.h" // XXX + namespace mongo { static const int INDEX_CATALOG_INIT = 283711; @@ -80,8 +74,8 @@ namespace mongo { // ------------- - IndexCatalog::IndexCatalog( Collection* collection, NamespaceDetails* details ) - : _magic(INDEX_CATALOG_UNINIT), _collection( collection ), _details( details ) { + IndexCatalog::IndexCatalog( Collection* collection ) + : _magic(INDEX_CATALOG_UNINIT), _collection( collection ) { } IndexCatalog::~IndexCatalog() { @@ -93,23 +87,23 @@ namespace mongo { } Status IndexCatalog::init(OperationContext* txn) { + vector<string> indexNames; + _collection->getCatalogEntry()->getAllIndexes( &indexNames ); - NamespaceDetails::IndexIterator ii = _details->ii(true); - while ( ii.more() ) { - const IndexDetails& id = ii.next(); - int idxNo = ii.pos() - 1; + for ( size_t i = 0; i < indexNames.size(); i++ ) { + const string& indexName = indexNames[i]; + BSONObj spec = _collection->getCatalogEntry()->getIndexSpec( indexName ).getOwned(); - if ( idxNo >= _details->getCompletedIndexCount() ) { - _unfinishedIndexes.push_back(_collection->docFor(id.info).getOwned()); + if ( !_collection->getCatalogEntry()->isIndexReady( indexName ) ) { + _unfinishedIndexes.push_back( spec ); continue; } - BSONObj ownedInfoObj = _collection->docFor(id.info).getOwned(); - BSONObj keyPattern = ownedInfoObj.getObjectField("key"); + BSONObj keyPattern = spec.getObjectField("key"); IndexDescriptor* descriptor = new IndexDescriptor( _collection, _getAccessMethodName(keyPattern), - ownedInfoObj ); - IndexCatalogEntry* entry = _setupInMemoryStructures(txn, descriptor ); + spec ); + IndexCatalogEntry* entry = _setupInMemoryStructures( txn, descriptor ); fassert( 17340, entry->isReady() ); } @@ -129,25 +123,14 @@ namespace mongo { IndexDescriptor* descriptor) { auto_ptr<IndexDescriptor> descriptorCleanup( descriptor ); - NamespaceDetails* indexMetadata = - _collection->_database->namespaceIndex().details( descriptor->indexNamespace() ); - - massert( 17329, - str::stream() << "no NamespaceDetails for index: " << descriptor->toString(), - indexMetadata ); - - auto_ptr<RecordStore> recordStore( new SimpleRecordStoreV1( txn, - descriptor->indexNamespace(), - new NamespaceDetailsRSV1MetaData( indexMetadata ), - _collection->getExtentManager(), - false ) ); - - auto_ptr<IndexCatalogEntry> entry( new IndexCatalogEntry( _collection, + auto_ptr<IndexCatalogEntry> entry( new IndexCatalogEntry( _collection->ns().ns(), + _collection->getCatalogEntry(), descriptorCleanup.release(), - recordStore.release() ) ); + _collection->infoCache() ) ); - entry->init( _createAccessMethod( entry->descriptor(), - entry.get() ) ); + entry->init( _collection->_database->_dbEntry->getIndex( txn, + _collection->getCatalogEntry(), + entry.get() ) ); IndexCatalogEntry* save = entry.get(); _entries.add( entry.release() ); @@ -155,21 +138,6 @@ namespace mongo { invariant( save == _entries.find( descriptor ) ); invariant( save == _entries.find( descriptor->indexName() ) ); - // check index to see if wants us to do special things - - { // power of 2 - bool requirePowerOf2 = false; - if ( IndexNames::findPluginName( descriptor->keyPattern() ) == IndexNames::TEXT ) - requirePowerOf2 = true; - - if ( descriptor->getInfoElement("expireAfterSeconds").isNumber() ) - requirePowerOf2 = true; - - if ( requirePowerOf2 ) { - _collection->setUserFlag(txn, NamespaceDetails::Flag_UsePowerOf2Sizes); - } - } - return save; } @@ -346,7 +314,6 @@ namespace mongo { string idxName = descriptor->indexName(); // out copy for yields, etc... invariant( entry == _entries.find( descriptor ) ); - invariant(_details->_catalogFindIndexByName(_collection, idxName, true) >= 0); try { Client& client = cc(); @@ -364,8 +331,7 @@ namespace mongo { _inProgressIndexes.erase(it); // sanity check - int idxNo = _details->_catalogFindIndexByName(_collection, idxName, true); - invariant( idxNo < numIndexesReady() ); + invariant( _collection->getCatalogEntry()->isIndexReady( idxName ) ); return Status::OK(); } @@ -428,54 +394,14 @@ namespace mongo { /// ---------- setup on disk structures ---------------- - Database* db = _collection->_database; - - // 1) insert into system.indexes - - Collection* systemIndexes = db->getOrCreateCollection( db->_indexesName ); - invariant( systemIndexes ); - - StatusWith<DiskLoc> systemIndexesEntry = systemIndexes->insertDocument( _txn, _spec, false ); - if ( !systemIndexesEntry.isOK() ) - return systemIndexesEntry.getStatus(); - - // 2) collection's NamespaceDetails - IndexDetails& indexDetails = - _collection->detailsWritable()->getNextIndexDetails( _txn, _collection ); - - try { - *_txn->recoveryUnit()->writing( &indexDetails.info ) = systemIndexesEntry.getValue(); - *_txn->recoveryUnit()->writing( &indexDetails.head ) = DiskLoc(); - } - catch ( DBException& e ) { - log() << "got exception trying to assign loc to IndexDetails" << e; - _catalog->_removeFromSystemIndexes(_txn, descriptor->indexName()); - return Status( ErrorCodes::InternalError, e.toString() ); - } - - int before = _collection->detailsDeprecated()->_indexBuildsInProgress; - try { - _txn->recoveryUnit()->writingInt( _collection->detailsWritable()->_indexBuildsInProgress ) += 1; - } - catch ( DBException& e ) { - log() << "got exception trying to incrementStats _indexBuildsInProgress: " << e; - fassert( 17344, before == _collection->detailsDeprecated()->_indexBuildsInProgress ); - _catalog->_removeFromSystemIndexes(_txn, descriptor->indexName()); - return Status( ErrorCodes::InternalError, e.toString() ); - } + Status status = _collection->getCatalogEntry()->prepareForIndexBuild( _txn, descriptor ); + if ( !status.isOK() ) + return status; // at this point we can do normal clean up procedure, so we mark ourselves // as in progress. _inProgress = true; - // 3) indexes entry in .ns file - NamespaceIndex& nsi = db->namespaceIndex(); - invariant( nsi.details( descriptor->indexNamespace() ) == NULL ); - nsi.add_ns( _txn, descriptor->indexNamespace(), DiskLoc(), false ); - - // 4) system.namespaces entry index ns - db->_addNamespaceToCatalog(_txn, descriptor->indexNamespace(), NULL); - /// ---------- setup in memory structures ---------------- _entry = _catalog->_setupInMemoryStructures(_txn, descriptorCleaner.release()); @@ -513,9 +439,6 @@ namespace mongo { _inProgress = false; // defensive fassert( 17204, _catalog->_collection->ok() ); // defensive - int idxNo = _collection->detailsDeprecated()->_catalogFindIndexByName(_collection, _indexName, true); - fassert( 17205, idxNo >= 0 ); - IndexCatalogEntry* entry = _catalog->_entries.find( _indexName ); invariant( entry == _entry ); @@ -525,8 +448,7 @@ namespace mongo { else { _catalog->_deleteIndexFromDisk( _txn, _indexName, - _indexNamespace, - idxNo ); + _indexNamespace ); } } @@ -542,25 +464,7 @@ namespace mongo { fassert( 17207, _catalog->_collection->ok() ); - NamespaceDetails* nsd = _collection->detailsWritable(); - - int idxNo = nsd->_catalogFindIndexByName(_collection, _indexName, true); - fassert( 17202, idxNo >= 0 ); - - // Make sure the newly created index is relocated to nIndexes, if it isn't already there - if ( idxNo != nsd->getCompletedIndexCount() ) { - log() << "switching indexes at position " << idxNo << " and " - << nsd->getCompletedIndexCount() << endl; - - int toIdxNo = nsd->getCompletedIndexCount(); - - nsd->swapIndex( _txn, idxNo, toIdxNo ); - - idxNo = nsd->getCompletedIndexCount(); - } - - _txn->recoveryUnit()->writingInt( nsd->_indexBuildsInProgress ) -= 1; - _txn->recoveryUnit()->writingInt( nsd->_nIndexes ) += 1; + _catalog->_collection->getCatalogEntry()->indexBuildSuccess( _txn, _indexName ); _catalog->_collection->infoCache()->addedIndex(); @@ -691,7 +595,8 @@ namespace mongo { } } - if ( _details->getTotalIndexCount() >= NamespaceDetails::NIndexesMax ) { + if ( _collection->getCatalogEntry()->getTotalIndexCount() >= + _collection->getCatalogEntry()->getMaxAllowedIndexes() ) { string s = str::stream() << "add index fails, too many indexes for " << _collection->ns().ns() << " key:" << key.toString(); log() << s; @@ -863,9 +768,6 @@ namespace mongo { string indexNamespace = entry->descriptor()->indexNamespace(); string indexName = entry->descriptor()->indexName(); - int idxNo = _details->_catalogFindIndexByName(_collection, indexName, true); - invariant( idxNo >= 0 ); - // --------- START REAL WORK ---------- audit::logDropIndex( currentClient.get(), indexName, _collection->ns().ns() ); @@ -878,8 +780,7 @@ namespace mongo { // **** this is the first disk change **** _deleteIndexFromDisk( txn, indexName, - indexNamespace, - idxNo ); + indexNamespace ); } catch ( std::exception& ) { // this is bad, and we don't really know state @@ -903,11 +804,7 @@ namespace mongo { void IndexCatalog::_deleteIndexFromDisk( OperationContext* txn, const string& indexName, - const string& indexNamespace, - int idxNo ) { - invariant( idxNo >= 0 ); - invariant(_details->_catalogFindIndexByName(_collection, indexName, true) == idxNo); - + const string& indexNamespace ) { // data + system.namespacesa Status status = _collection->_database->_dropNS( txn, indexNamespace ); if ( status.code() == ErrorCodes::NamespaceNotFound ) { @@ -917,29 +814,11 @@ namespace mongo { warning() << "couldn't drop extents for " << indexNamespace << " " << status.toString(); } - // all info in the .ns file - _details->_removeIndexFromMe( txn, idxNo ); - - // remove from system.indexes - // n is how many things were removed from this - // probably should clean this up - int n = _removeFromSystemIndexes(txn, indexName); - wassert( n == 1 ); - } - - int IndexCatalog::_removeFromSystemIndexes(OperationContext* txn, - const StringData& indexName) { - BSONObjBuilder b; - b.append( "ns", _collection->ns() ); - b.append( "name", indexName ); - BSONObj cond = b.obj(); // e.g.: { name: "ts_1", ns: "foo.coll" } - return static_cast<int>( deleteObjects( txn, - _collection->_database, - _collection->_database->_indexesName, - cond, - false, - false, - true ) ); + status = _collection->getCatalogEntry()->removeIndex( txn, indexName ); + if ( !status.isOK() ) { + warning() << "couldn't remove index from catalog " << indexNamespace + << " " << status.toString(); + } } vector<BSONObj> IndexCatalog::getAndClearUnfinishedIndexes(OperationContext* txn) { @@ -951,48 +830,13 @@ namespace mongo { BSONObj keyPattern = spec.getObjectField("key"); IndexDescriptor desc( _collection, _getAccessMethodName(keyPattern), spec ); - int idxNo = _details->_catalogFindIndexByName(_collection, desc.indexName(), true); - invariant( idxNo >= 0 ); - invariant( idxNo >= numIndexesReady() ); - _deleteIndexFromDisk( txn, desc.indexName(), - desc.indexNamespace(), - idxNo ); + desc.indexNamespace() ); } return toReturn; } - void IndexCatalog::updateTTLSetting( OperationContext* txn, - const IndexDescriptor* idx, - long long newExpireSeconds ) { - IndexDetails* indexDetails = _getIndexDetails( idx ); - - const BSONElement oldExpireSecs = - _collection->docFor(indexDetails->info).getField("expireAfterSeconds"); - - // Important that we set the new value in-place. We are writing directly to the - // object here so must be careful not to overwrite with a longer numeric type. - - char* nonConstPtr = const_cast<char*>(oldExpireSecs.value()); - switch( oldExpireSecs.type() ) { - case EOO: - massert( 16631, "index does not have an 'expireAfterSeconds' field", false ); - break; - case NumberInt: - *txn->recoveryUnit()->writing(reinterpret_cast<int*>(nonConstPtr)) = newExpireSeconds; - break; - case NumberDouble: - *txn->recoveryUnit()->writing(reinterpret_cast<double*>(nonConstPtr)) = newExpireSeconds; - break; - case NumberLong: - *txn->recoveryUnit()->writing(reinterpret_cast<long long*>(nonConstPtr)) = newExpireSeconds; - break; - default: - massert( 16632, "current 'expireAfterSeconds' is not a number", false ); - } - } - bool IndexCatalog::isMultikey( const IndexDescriptor* idx ) { IndexCatalogEntry* entry = _entries.find( idx ); invariant( entry ); @@ -1003,11 +847,11 @@ namespace mongo { // --------------------------- int IndexCatalog::numIndexesTotal() const { - return _details->getTotalIndexCount(); + return _collection->getCatalogEntry()->getTotalIndexCount(); } int IndexCatalog::numIndexesReady() const { - return _details->getCompletedIndexCount(); + return _collection->getCatalogEntry()->getCompletedIndexCount(); } bool IndexCatalog::haveIdIndex() const { @@ -1142,39 +986,6 @@ namespace mongo { return entry; } - IndexAccessMethod* IndexCatalog::_createAccessMethod( const IndexDescriptor* desc, - IndexCatalogEntry* entry ) { - const string& type = desc->getAccessMethodName(); - - if (IndexNames::HASHED == type) - return new HashAccessMethod( entry ); - - if (IndexNames::GEO_2DSPHERE == type) - return new S2AccessMethod( entry ); - - if (IndexNames::TEXT == type) - return new FTSAccessMethod( entry ); - - if (IndexNames::GEO_HAYSTACK == type) - return new HaystackAccessMethod( entry ); - - if ("" == type) - return new BtreeAccessMethod( entry ); - - if (IndexNames::GEO_2D == type) - return new TwoDAccessMethod( entry ); - - log() << "Can't find index for keyPattern " << desc->keyPattern(); - invariant(0); - return NULL; - } - - IndexDetails* IndexCatalog::_getIndexDetails( const IndexDescriptor* descriptor ) const { - int idxNo = _details->_catalogFindIndexByName(_collection, descriptor->indexName(), true); - invariant( idxNo >= 0 ); - return &_details->idx( idxNo ); - } - // --------------------------- Status IndexCatalog::_indexRecord(OperationContext* txn, diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h index b45a056cc1a..e33202dcbdf 100644 --- a/src/mongo/db/catalog/index_catalog.h +++ b/src/mongo/db/catalog/index_catalog.h @@ -42,10 +42,8 @@ namespace mongo { class Client; class Collection; - class NamespaceDetails; class IndexDescriptor; - struct IndexDetails; class IndexAccessMethod; /** @@ -54,7 +52,7 @@ namespace mongo { */ class IndexCatalog { public: - IndexCatalog( Collection* collection, NamespaceDetails* details ); + IndexCatalog( Collection* collection ); ~IndexCatalog(); // must be called before used @@ -178,14 +176,6 @@ namespace mongo { // ---- modify single index - /* Updates the expireAfterSeconds field of the given index to the value in newExpireSecs. - * The specified index must already contain an expireAfterSeconds field, and the value in - * that field and newExpireSecs must both be numeric. - */ - void updateTTLSetting( OperationContext* txn, - const IndexDescriptor* idx, - long long newExpireSeconds ); - bool isMultikey( const IndexDescriptor* idex ); // --- these probably become private? @@ -274,13 +264,6 @@ namespace mongo { private: typedef unordered_map<IndexDescriptor*, Client*> InProgressIndexesMap; - // creates a new thing, no caching - IndexAccessMethod* _createAccessMethod( const IndexDescriptor* desc, - IndexCatalogEntry* entry ); - - int _removeFromSystemIndexes(OperationContext* txn, - const StringData& indexName ); - bool _shouldOverridePlugin( const BSONObj& keyPattern ) const; /** @@ -290,8 +273,6 @@ namespace mongo { */ std::string _getAccessMethodName(const BSONObj& keyPattern) const; - IndexDetails* _getIndexDetails( const IndexDescriptor* descriptor ) const; - void _checkMagic() const; @@ -320,8 +301,7 @@ namespace mongo { // doesn't change memory state, etc... void _deleteIndexFromDisk( OperationContext* txn, const std::string& indexName, - const std::string& indexNamespace, - int idxNo ); + const std::string& indexNamespace ); // descriptor ownership passes to _setupInMemoryStructures IndexCatalogEntry* _setupInMemoryStructures(OperationContext* txn, @@ -335,7 +315,6 @@ namespace mongo { int _magic; Collection* _collection; - NamespaceDetails* _details; IndexCatalogEntryContainer _entries; diff --git a/src/mongo/db/catalog/index_catalog_entry.cpp b/src/mongo/db/catalog/index_catalog_entry.cpp index 31436041f3d..9b6f5f63d6d 100644 --- a/src/mongo/db/catalog/index_catalog_entry.cpp +++ b/src/mongo/db/catalog/index_catalog_entry.cpp @@ -30,6 +30,7 @@ #include "mongo/db/catalog/index_catalog_entry.h" +#include "mongo/db/catalog/collection_catalog_entry.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/operation_context.h" @@ -56,12 +57,14 @@ namespace mongo { IndexCatalogEntry* _catalogEntry; }; - IndexCatalogEntry::IndexCatalogEntry( Collection* collection, + IndexCatalogEntry::IndexCatalogEntry( const StringData& ns, + CollectionCatalogEntry* collection, IndexDescriptor* descriptor, - RecordStore* recordstore ) - : _collection( collection ), + CollectionInfoCache* infoCache ) + : _ns( ns.toString() ), + _collection( collection ), _descriptor( descriptor ), - _recordStore( recordstore ), + _infoCache( infoCache ), _accessMethod( NULL ), _headManager(new HeadManagerImpl(this)), _ordering( Ordering::make( descriptor->keyPattern() ) ), @@ -74,7 +77,6 @@ namespace mongo { delete _headManager; delete _accessMethod; - delete _recordStore; delete _descriptor; } @@ -110,22 +112,23 @@ namespace mongo { } void IndexCatalogEntry::setHead( OperationContext* txn, DiskLoc newHead ) { - NamespaceDetails* nsd = _collection->detailsWritable(); - int idxNo = _indexNo(); - IndexDetails& id = nsd->idx( idxNo ); - *txn->recoveryUnit()->writing(&id.head) = newHead; + _collection->setIndexHead( txn, + _descriptor->indexName(), + newHead ); _head = newHead; } void IndexCatalogEntry::setMultikey( OperationContext* txn ) { if ( isMultikey() ) return; - NamespaceDetails* nsd = _collection->detailsWritable(); - int idxNo = _indexNo(); - if ( nsd->setIndexIsMultikey( txn, idxNo, true ) ) { - LOG(1) << _collection->ns().ns() << ": clearing plan cache - index " - << _descriptor->keyPattern() << " set to multi key."; - _collection->infoCache()->clearQueryCache(); + if ( _collection->setIndexIsMultikey( txn, + _descriptor->indexName(), + true ) ) { + if ( _infoCache ) { + LOG(1) << _ns << ": clearing plan cache - index " + << _descriptor->keyPattern() << " set to multi key."; + _infoCache->clearQueryCache(); + } } _isMultikey = true; } @@ -133,29 +136,17 @@ namespace mongo { // ---- bool IndexCatalogEntry::_catalogIsReady() const { - return _indexNo() < _collection->getIndexCatalog()->numIndexesReady(); + return _collection->isIndexReady( _descriptor->indexName() ); } DiskLoc IndexCatalogEntry::_catalogHead() const { - const NamespaceDetails* nsd = _collection->detailsDeprecated(); - int idxNo = _indexNo(); - return nsd->idx( idxNo ).head; + return _collection->getIndexHead( _descriptor->indexName() ); } bool IndexCatalogEntry::_catalogIsMultikey() const { - const NamespaceDetails* nsd = _collection->detailsDeprecated(); - int idxNo = _indexNo(); - return nsd->isMultikey( idxNo ); + return _collection->isIndexMultikey( _descriptor->indexName() ); } - int IndexCatalogEntry::_indexNo() const { - int idxNo = _collection->detailsDeprecated()->_catalogFindIndexByName(_collection, - _descriptor->indexName(), true); - fassert( 17341, idxNo >= 0 ); - return idxNo; - } - - // ------------------ const IndexCatalogEntry* IndexCatalogEntryContainer::find( const IndexDescriptor* desc ) const { diff --git a/src/mongo/db/catalog/index_catalog_entry.h b/src/mongo/db/catalog/index_catalog_entry.h index e5b835405be..77f19c8d99b 100644 --- a/src/mongo/db/catalog/index_catalog_entry.h +++ b/src/mongo/db/catalog/index_catalog_entry.h @@ -38,25 +38,26 @@ namespace mongo { - class Collection; + class CollectionCatalogEntry; + class CollectionInfoCache; class HeadManager; class IndexAccessMethod; class IndexDescriptor; - class RecordStore; class OperationContext; class IndexCatalogEntry { MONGO_DISALLOW_COPYING( IndexCatalogEntry ); public: - IndexCatalogEntry( Collection* collection, + IndexCatalogEntry( const StringData& ns, + CollectionCatalogEntry* collection, // not owned IndexDescriptor* descriptor, // ownership passes to me - RecordStore* recordStore ); // ownership passes to me + CollectionInfoCache* infoCache ); // not owned, optional ~IndexCatalogEntry(); - void init( IndexAccessMethod* accessMethod ); + const string& ns() const { return _ns; } - const Collection* collection() const { return _collection; } + void init( IndexAccessMethod* accessMethod ); IndexDescriptor* descriptor() { return _descriptor; } const IndexDescriptor* descriptor() const { return _descriptor; } @@ -64,9 +65,6 @@ namespace mongo { IndexAccessMethod* accessMethod() { return _accessMethod; } const IndexAccessMethod* accessMethod() const { return _accessMethod; } - RecordStore* recordStore() { return _recordStore; } - const RecordStore* recordStore() const { return _recordStore; } - const Ordering& ordering() const { return _ordering; } /// --------------------- @@ -90,19 +88,19 @@ namespace mongo { private: - int _indexNo() const; - bool _catalogIsReady() const; DiskLoc _catalogHead() const; bool _catalogIsMultikey() const; // ----- - Collection* _collection; // not owned here + string _ns; + + CollectionCatalogEntry* _collection; // not owned here IndexDescriptor* _descriptor; // owned here - RecordStore* _recordStore; // owned here + CollectionInfoCache* _infoCache; // not owned here IndexAccessMethod* _accessMethod; // owned here diff --git a/src/mongo/db/catalog/index_create.cpp b/src/mongo/db/catalog/index_create.cpp index e28bc4b7ec3..0243a74891a 100644 --- a/src/mongo/db/catalog/index_create.cpp +++ b/src/mongo/db/catalog/index_create.cpp @@ -233,7 +233,9 @@ namespace mongo { << status.toString(), status.isOK() ); - IndexAccessMethod* bulk = doInBackground ? NULL : btreeState->accessMethod()->initiateBulk(txn); + IndexAccessMethod* bulk = doInBackground ? + NULL : btreeState->accessMethod()->initiateBulk(txn, + collection->numRecords()); scoped_ptr<IndexAccessMethod> bulkHolder(bulk); IndexAccessMethod* iam = bulk ? bulk : btreeState->accessMethod(); @@ -341,7 +343,7 @@ namespace mongo { if ( !status.isOK() ) return status; - state.bulk = state.real->initiateBulk(_txn); + state.bulk = state.real->initiateBulk(_txn, _collection->numRecords() ); _states.push_back( state ); } diff --git a/src/mongo/db/commands/dbhash.cpp b/src/mongo/db/commands/dbhash.cpp index 183813f196b..df10255418e 100644 --- a/src/mongo/db/commands/dbhash.cpp +++ b/src/mongo/db/commands/dbhash.cpp @@ -34,6 +34,7 @@ #include "mongo/db/commands.h" #include "mongo/db/catalog/database.h" #include "mongo/db/query/internal_plans.h" +#include "mongo/db/structure/catalog/namespace_index.h" #include "mongo/util/md5.hpp" #include "mongo/util/timer.h" @@ -147,7 +148,7 @@ namespace mongo { Client::ReadContext ctx(ns); Database* db = ctx.ctx().db(); if ( db ) - db->namespaceIndex().getNamespaces( colls ); + db->namespaceIndex()->getNamespaces( colls ); colls.sort(); result.appendNumber( "numCollections" , (long long)colls.size() ); diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 245d02d2c02..44a5b3bf461 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -38,9 +38,9 @@ #include "mongo/base/initializer.h" #include "mongo/base/status.h" #include "mongo/db/auth/auth_index_d.h" -#include "mongo/db/auth/authz_manager_external_state_d.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" +#include "mongo/db/auth/authz_manager_external_state_d.h" #include "mongo/db/catalog/index_catalog.h" #include "mongo/db/catalog/index_key_validate.h" #include "mongo/db/client.h" @@ -52,7 +52,6 @@ #include "mongo/db/db.h" #include "mongo/db/dbmessage.h" #include "mongo/db/dbwebserver.h" -#include "mongo/db/storage/mmap_v1/dur.h" #include "mongo/db/index_names.h" #include "mongo/db/index_rebuilder.h" #include "mongo/db/initialize_server_global_state.h" @@ -62,12 +61,13 @@ #include "mongo/db/kill_current_op.h" #include "mongo/db/log_process_details.h" #include "mongo/db/mongod_options.h" +#include "mongo/db/operation_context_impl.h" #include "mongo/db/pdfile_version.h" #include "mongo/db/query/internal_plans.h" #include "mongo/db/range_deleter_service.h" #include "mongo/db/repair_database.h" -#include "mongo/db/repl/repl_start.h" #include "mongo/db/repl/repl_settings.h" +#include "mongo/db/repl/repl_start.h" #include "mongo/db/repl/rs.h" #include "mongo/db/restapi.h" #include "mongo/db/startup_warnings.h" @@ -75,9 +75,10 @@ #include "mongo/db/stats/snapshots.h" #include "mongo/db/storage/data_file.h" #include "mongo/db/storage/extent_manager.h" -#include "mongo/db/operation_context_impl.h" +#include "mongo/db/storage/mmap_v1/dur.h" #include "mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h" #include "mongo/db/storage_options.h" +#include "mongo/db/structure/catalog/namespace_index.h" #include "mongo/db/ttl.h" #include "mongo/platform/process_id.h" #include "mongo/s/d_writeback.h" @@ -327,7 +328,7 @@ namespace mongo { } list<string> collections; - db->namespaceIndex().getNamespaces( collections ); + db->namespaceIndex()->getNamespaces( collections ); // for each collection, ensure there is a $_id_ index for (list<string>::iterator i = collections.begin(); i != collections.end(); ++i) { diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 126866dc82b..9661ccfc0ae 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -70,6 +70,7 @@ #include "mongo/db/storage/extent_manager.h" #include "mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h" #include "mongo/db/storage/record.h" +#include "mongo/db/catalog/collection_catalog_entry.h" #include "mongo/db/structure/catalog/namespace_details.h" #include "mongo/db/write_concern.h" #include "mongo/s/d_logic.h" @@ -170,7 +171,7 @@ namespace mongo { const BSONObj& cmdObj) { invariant(db); std::list<std::string> collections; - db->namespaceIndex().getNamespaces(collections, true /* onlyCollections */); + db->namespaceIndex()->getNamespaces(collections, true /* onlyCollections */); std::vector<BSONObj> allKilledIndexes; for (std::list<std::string>::iterator it = collections.begin(); @@ -256,7 +257,7 @@ namespace mongo { const BSONObj& cmdObj) { invariant(db); std::list<std::string> collections; - db->namespaceIndex().getNamespaces(collections, true /* onlyCollections */); + db->namespaceIndex()->getNamespaces(collections, true /* onlyCollections */); std::vector<BSONObj> allKilledIndexes; for (std::list<std::string>::iterator it = collections.begin(); @@ -1110,7 +1111,7 @@ namespace mongo { scale ); result.append( "nindexes" , collection->getIndexCatalog()->numIndexesReady() ); - collection->appendCustomStats( &result, scale ); + collection->getRecordStore()->appendCustomStats( &result, scale ); BSONObjBuilder indexSizes; result.appendNumber( "totalIndexSize" , getIndexSizeForCollection(dbname, ns, &indexSizes, scale) / scale ); @@ -1165,22 +1166,6 @@ namespace mongo { else if ( LiteParsedQuery::cmdOptionMaxTimeMS == e.fieldNameStringData() ) { // no-op } - else if ( str::equals( "usePowerOf2Sizes", e.fieldName() ) ) { - bool oldPowerOf2 = coll->isUserFlagSet(NamespaceDetails::Flag_UsePowerOf2Sizes); - bool newPowerOf2 = e.trueValue(); - - if ( oldPowerOf2 != newPowerOf2 ) { - // change userFlags - result.appendBool( "usePowerOf2Sizes_old", oldPowerOf2 ); - - if ( newPowerOf2 ) - coll->setUserFlag( txn, NamespaceDetails::Flag_UsePowerOf2Sizes ); - else - coll->clearUserFlag( txn, NamespaceDetails::Flag_UsePowerOf2Sizes ); - - result.appendBool( "usePowerOf2Sizes_new", newPowerOf2 ); - } - } else if ( str::equals( "index", e.fieldName() ) ) { BSONObj indexObj = e.Obj(); BSONObj keyPattern = indexObj.getObjectField( "keyPattern" ); @@ -1225,13 +1210,24 @@ namespace mongo { if ( oldExpireSecs != newExpireSecs ) { // change expireAfterSeconds result.appendAs( oldExpireSecs, "expireAfterSeconds_old" ); - coll->getIndexCatalog()->updateTTLSetting( txn, idx, newExpireSecs.numberLong() ); + coll->getCatalogEntry()->updateTTLSetting( txn, + idx->indexName(), + newExpireSecs.numberLong() ); result.appendAs( newExpireSecs , "expireAfterSeconds_new" ); } } else { - errmsg = str::stream() << "unknown option to collMod: " << e.fieldName(); - ok = false; + Status s = coll->getRecordStore()->setCustomOption( txn, e, &result ); + if ( s.isOK() ) { + // no-op + } + else if ( s.code() == ErrorCodes::InvalidOptions ) { + errmsg = str::stream() << "unknown option to collMod: " << e.fieldName(); + ok = false; + } + else { + return appendCommandStatus( result, s ); + } } } @@ -1290,7 +1286,7 @@ namespace mongo { d = NULL; if ( d ) - d->namespaceIndex().getNamespaces( collections ); + d->namespaceIndex()->getNamespaces( collections ); long long ncollections = 0; long long objects = 0; @@ -1333,7 +1329,7 @@ namespace mongo { result.appendNumber( "indexSize" , indexSize / scale ); if ( d ) { result.appendNumber( "fileSize" , d->fileSize() / scale ); - result.appendNumber( "nsSizeMB", (int) d->namespaceIndex().fileLength() / 1024 / 1024 ); + result.appendNumber( "nsSizeMB", (int) d->namespaceIndex()->fileLength() / 1024 / 1024 ); } else { result.appendNumber( "fileSize" , 0 ); diff --git a/src/mongo/db/dbhelpers.h b/src/mongo/db/dbhelpers.h index 758491e0297..a5e9947f918 100644 --- a/src/mongo/db/dbhelpers.h +++ b/src/mongo/db/dbhelpers.h @@ -28,11 +28,9 @@ #pragma once -// TODO: Remove -#include "mongo/pch.h" - #include "mongo/db/client.h" #include "mongo/db/db.h" +#include "mongo/db/diskloc.h" #include "mongo/db/keypattern.h" #include "mongo/s/range_arithmetic.h" diff --git a/src/mongo/db/exec/2d.cpp b/src/mongo/db/exec/2d.cpp index 5142beb7195..758f17f825b 100644 --- a/src/mongo/db/exec/2d.cpp +++ b/src/mongo/db/exec/2d.cpp @@ -160,7 +160,7 @@ namespace twod_exec { // GeoCircleBrowse::GeoCircleBrowse(const TwoDParams& params, TwoDAccessMethod* accessMethod) - : GeoBrowse(accessMethod, "circle", params.filter) { + : GeoBrowse(params.collection, accessMethod, "circle", params.filter) { _converter = accessMethod->getParams().geoHashConverter; @@ -231,7 +231,7 @@ namespace twod_exec { // GeoBoxBrowse::GeoBoxBrowse(const TwoDParams& params, TwoDAccessMethod* accessMethod) - : GeoBrowse(accessMethod, "box", params.filter) { + : GeoBrowse(params.collection, accessMethod, "box", params.filter) { _converter = accessMethod->getParams().geoHashConverter; @@ -273,7 +273,7 @@ namespace twod_exec { // GeoPolygonBrowse::GeoPolygonBrowse(const TwoDParams& params, TwoDAccessMethod* accessMethod) - : GeoBrowse(accessMethod, "polygon", params.filter) { + : GeoBrowse(params.collection, accessMethod, "polygon", params.filter) { _converter = accessMethod->getParams().geoHashConverter; diff --git a/src/mongo/db/exec/2dcommon.cpp b/src/mongo/db/exec/2dcommon.cpp index a469c591c40..fc2529258f7 100644 --- a/src/mongo/db/exec/2dcommon.cpp +++ b/src/mongo/db/exec/2dcommon.cpp @@ -104,8 +104,11 @@ namespace twod_exec { // GeoAccumulator // - GeoAccumulator::GeoAccumulator(TwoDAccessMethod* accessMethod, MatchExpression* filter) - : _accessMethod(accessMethod), _converter(accessMethod->getParams().geoHashConverter), + GeoAccumulator::GeoAccumulator(Collection* collection, + TwoDAccessMethod* accessMethod, + MatchExpression* filter) + : _collection(collection), + _accessMethod(accessMethod), _converter(accessMethod->getParams().geoHashConverter), _filter(filter), _lookedAt(0), _matchesPerfd(0), _objectsLoaded(0), _pointsLoaded(0), _found(0) { } @@ -135,7 +138,7 @@ namespace twod_exec { GeoMatchableDocument md(_accessMethod->getDescriptor()->keyPattern(), node._key, node.recordLoc, - _accessMethod->collection(), + _collection, &fetched); bool good = _filter->matches(&md); @@ -306,14 +309,15 @@ namespace twod_exec { // GeoBrowse // - GeoBrowse::GeoBrowse(TwoDAccessMethod* accessMethod, string type, MatchExpression* filter) - : GeoAccumulator(accessMethod, filter), + GeoBrowse::GeoBrowse(Collection* collection, + TwoDAccessMethod* accessMethod, string type, MatchExpression* filter) + : GeoAccumulator(collection, accessMethod, filter), _type(type), _firstCall(true), _nscanned(), _centerPrefix(0, 0, 0), _descriptor(accessMethod->getDescriptor()), _converter(accessMethod->getParams().geoHashConverter), _params(accessMethod->getParams()), - _collection(accessMethod->collection()) { + _collection(collection) { // Set up the initial expand state _state = START; diff --git a/src/mongo/db/exec/2dcommon.h b/src/mongo/db/exec/2dcommon.h index 6986e3d4a7b..67307038482 100644 --- a/src/mongo/db/exec/2dcommon.h +++ b/src/mongo/db/exec/2dcommon.h @@ -141,7 +141,8 @@ namespace twod_exec { class GeoAccumulator { public: - GeoAccumulator(TwoDAccessMethod* accessMethod, MatchExpression* filter); + GeoAccumulator(Collection* collection, + TwoDAccessMethod* accessMethod, MatchExpression* filter); virtual ~GeoAccumulator(); @@ -159,6 +160,7 @@ namespace twod_exec { virtual KeyResult approxKeyCheck(const Point& p, double& keyD) = 0; + Collection* _collection; TwoDAccessMethod* _accessMethod; shared_ptr<GeoHashConverter> _converter; std::map<DiskLoc, bool> _matched; @@ -185,7 +187,10 @@ namespace twod_exec { DONE } _state; - GeoBrowse(TwoDAccessMethod* accessMethod, std::string type, MatchExpression* filter); + GeoBrowse(Collection* collection, + TwoDAccessMethod* accessMethod, + std::string type, + MatchExpression* filter); virtual bool ok(); virtual bool advance(); diff --git a/src/mongo/db/exec/2dnear.cpp b/src/mongo/db/exec/2dnear.cpp index 7c4c4dd8598..dda74d064e3 100644 --- a/src/mongo/db/exec/2dnear.cpp +++ b/src/mongo/db/exec/2dnear.cpp @@ -65,7 +65,8 @@ namespace mongo { TwoDAccessMethod* am = static_cast<TwoDAccessMethod*>( indexCatalog->getIndex( desc ) ); auto_ptr<twod_exec::GeoSearch> search; - search.reset(new twod_exec::GeoSearch(am, + search.reset(new twod_exec::GeoSearch(_params.collection, + am, _params.nearQuery.centroid.oldPoint, _params.numWanted, _params.filter, @@ -162,13 +163,14 @@ namespace twod_exec { // GeoHopper // - GeoHopper::GeoHopper(TwoDAccessMethod* accessMethod, + GeoHopper::GeoHopper(Collection* collection, + TwoDAccessMethod* accessMethod, unsigned max, const Point& n, MatchExpression* filter, double maxDistance, GeoDistType type) - : GeoBrowse(accessMethod, "search", filter), + : GeoBrowse(collection, accessMethod, "search", filter), _max(max), _near(n), _maxDistance(maxDistance), @@ -177,7 +179,7 @@ namespace twod_exec { ? accessMethod->getParams().geoHashConverter->getError() : accessMethod->getParams().geoHashConverter->getErrorSphere()), _farthest(0), - _collection(accessMethod->collection()) {} + _collection(collection) {} GeoAccumulator:: KeyResult GeoHopper::approxKeyCheck(const Point& p, double& d) { // Always check approximate distance, since it lets us avoid doing @@ -294,13 +296,14 @@ namespace twod_exec { // GeoSearch // - GeoSearch::GeoSearch(TwoDAccessMethod* accessMethod, + GeoSearch::GeoSearch(Collection* collection, + TwoDAccessMethod* accessMethod, const Point& startPt, int numWanted, MatchExpression* filter, double maxDistance, GeoDistType type) - : GeoHopper(accessMethod, numWanted, startPt, filter, maxDistance, type), + : GeoHopper(collection, accessMethod, numWanted, startPt, filter, maxDistance, type), _start(accessMethod->getParams().geoHashConverter->hash(startPt.x, startPt.y)), _numWanted(numWanted), _type(type), diff --git a/src/mongo/db/exec/2dnear.h b/src/mongo/db/exec/2dnear.h index f7bc255a3ef..6d3ac4254d8 100644 --- a/src/mongo/db/exec/2dnear.h +++ b/src/mongo/db/exec/2dnear.h @@ -106,7 +106,8 @@ namespace twod_exec { public: typedef multiset<GeoPoint> Holder; - GeoHopper(TwoDAccessMethod* accessMethod, + GeoHopper(Collection* collection, + TwoDAccessMethod* accessMethod, unsigned max, const Point& n, MatchExpression* filter, @@ -146,7 +147,8 @@ namespace twod_exec { class GeoSearch : public GeoHopper { public: - GeoSearch(TwoDAccessMethod* accessMethod, + GeoSearch(Collection* collection, + TwoDAccessMethod* accessMethod, const Point& startPt, int numWanted = 100, MatchExpression* filter = NULL, diff --git a/src/mongo/db/geo/haystack.cpp b/src/mongo/db/geo/haystack.cpp index 13eb964376d..b5c61765b79 100644 --- a/src/mongo/db/geo/haystack.cpp +++ b/src/mongo/db/geo/haystack.cpp @@ -111,7 +111,7 @@ namespace mongo { IndexDescriptor* desc = idxs[0]; HaystackAccessMethod* ham = static_cast<HaystackAccessMethod*>( collection->getIndexCatalog()->getIndex(desc) ); - ham->searchCommand(nearElt.Obj(), maxDistance.numberDouble(), search.Obj(), + ham->searchCommand(collection, nearElt.Obj(), maxDistance.numberDouble(), search.Obj(), &result, limit); return 1; } diff --git a/src/mongo/db/index/2d_access_method.cpp b/src/mongo/db/index/2d_access_method.cpp index 7a5cbc9ff0b..8823c3a08c9 100644 --- a/src/mongo/db/index/2d_access_method.cpp +++ b/src/mongo/db/index/2d_access_method.cpp @@ -41,8 +41,9 @@ namespace mongo { - TwoDAccessMethod::TwoDAccessMethod(IndexCatalogEntry* btreeState) - : BtreeBasedAccessMethod(btreeState) { + TwoDAccessMethod::TwoDAccessMethod(IndexCatalogEntry* btreeState, + RecordStore* rs) + : BtreeBasedAccessMethod(btreeState, rs) { const IndexDescriptor* descriptor = btreeState->descriptor(); diff --git a/src/mongo/db/index/2d_access_method.h b/src/mongo/db/index/2d_access_method.h index e7796ea0b4d..2f8eea41959 100644 --- a/src/mongo/db/index/2d_access_method.h +++ b/src/mongo/db/index/2d_access_method.h @@ -68,7 +68,8 @@ namespace mongo { public: using BtreeBasedAccessMethod::_descriptor; - TwoDAccessMethod(IndexCatalogEntry* btreeState); + TwoDAccessMethod(IndexCatalogEntry* btreeState, + RecordStore* rs); virtual ~TwoDAccessMethod() { } private: diff --git a/src/mongo/db/index/btree_access_method.cpp b/src/mongo/db/index/btree_access_method.cpp index 91a76c42f82..f9baf4e94d0 100644 --- a/src/mongo/db/index/btree_access_method.cpp +++ b/src/mongo/db/index/btree_access_method.cpp @@ -40,8 +40,8 @@ namespace mongo { // Standard Btree implementation below. - BtreeAccessMethod::BtreeAccessMethod(IndexCatalogEntry* btreeState) - : BtreeBasedAccessMethod(btreeState) { + BtreeAccessMethod::BtreeAccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs ) + : BtreeBasedAccessMethod(btreeState, rs) { // The key generation wants these values. vector<const char*> fieldNames; diff --git a/src/mongo/db/index/btree_access_method.h b/src/mongo/db/index/btree_access_method.h index d40bc013767..74c4cdfc997 100644 --- a/src/mongo/db/index/btree_access_method.h +++ b/src/mongo/db/index/btree_access_method.h @@ -51,7 +51,8 @@ namespace mongo { // superclass and subclasses (like this) can use them. using BtreeBasedAccessMethod::_descriptor; - BtreeAccessMethod(IndexCatalogEntry* btreeState ); + BtreeAccessMethod(IndexCatalogEntry* btreeState, + RecordStore* rs ); virtual ~BtreeAccessMethod() { } private: diff --git a/src/mongo/db/index/btree_based_access_method.cpp b/src/mongo/db/index/btree_based_access_method.cpp index 6d16f375dc0..8258c23f347 100644 --- a/src/mongo/db/index/btree_based_access_method.cpp +++ b/src/mongo/db/index/btree_based_access_method.cpp @@ -41,8 +41,6 @@ #include "mongo/db/kill_current_op.h" #include "mongo/db/pdfile.h" #include "mongo/db/pdfile_private.h" -#include "mongo/db/repl/is_master.h" -#include "mongo/db/repl/rs.h" #include "mongo/db/server_parameters.h" #include "mongo/db/operation_context.h" #include "mongo/db/structure/btree/btree_interface.h" @@ -66,12 +64,15 @@ namespace mongo { static InvalidateCursorsNotification invalidateCursors; - BtreeBasedAccessMethod::BtreeBasedAccessMethod(IndexCatalogEntry* btreeState) - : _btreeState(btreeState), _descriptor(btreeState->descriptor()) { + BtreeBasedAccessMethod::BtreeBasedAccessMethod(IndexCatalogEntry* btreeState, + RecordStore* recordStore) + : _btreeState(btreeState), + _recordStore( recordStore ), + _descriptor(btreeState->descriptor()) { verify(0 == _descriptor->version() || 1 == _descriptor->version()); _newInterface.reset(BtreeInterface::getInterface(btreeState->headManager(), - btreeState->recordStore(), + recordStore, btreeState->ordering(), _descriptor->indexNamespace(), _descriptor->version(), @@ -104,7 +105,7 @@ namespace mongo { if (ErrorCodes::KeyTooLong == status.code()) { // Ignore this error if we're on a secondary. - if (!isMasterNs(collection()->ns().ns().c_str())) { + if (!txn->isPrimaryFor(_btreeState->ns())) { continue; } @@ -149,7 +150,6 @@ namespace mongo { problem() << "Assertion failure: _unindex failed " << _descriptor->indexNamespace() << endl; out() << "Assertion failure: _unindex failed: " << e.what() << '\n'; - out() << " obj:" << _btreeState->collection()->docFor(loc).toString() << '\n'; out() << " key:" << key.toString() << '\n'; out() << " dl:" << loc.toString() << endl; logContext(); @@ -182,8 +182,7 @@ namespace mongo { ++*numDeleted; } else if (options.logIfError) { log() << "unindex failed (key too big?) " << _descriptor->indexNamespace() - << " key: " << *i << " " - << _btreeState->collection()->docFor(loc)["_id"] << endl; + << " key: " << *i; } } @@ -227,8 +226,9 @@ namespace mongo { return Status::OK(); } + Status BtreeBasedAccessMethod::touch( OperationContext* txn ) const { - return _btreeState->recordStore()->touch( txn, NULL ); + return _recordStore->touch( txn, NULL ); } DiskLoc BtreeBasedAccessMethod::findSingle(const BSONObj& key) const { @@ -323,7 +323,8 @@ namespace mongo { return Status::OK(); } - IndexAccessMethod* BtreeBasedAccessMethod::initiateBulk(OperationContext* txn) { + IndexAccessMethod* BtreeBasedAccessMethod::initiateBulk(OperationContext* txn, + int64_t numRecords ) { // If there's already data in the index, don't do anything. if (!_newInterface->isEmpty()) { return NULL; @@ -333,7 +334,7 @@ namespace mongo { this, _newInterface.get(), _descriptor, - _btreeState->collection()->numRecords()); + static_cast<int>( numRecords ) ); } Status BtreeBasedAccessMethod::commitBulk(IndexAccessMethod* bulkRaw, diff --git a/src/mongo/db/index/btree_based_access_method.h b/src/mongo/db/index/btree_based_access_method.h index decfbb0bc9a..c54c71d401f 100644 --- a/src/mongo/db/index/btree_based_access_method.h +++ b/src/mongo/db/index/btree_based_access_method.h @@ -41,6 +41,7 @@ namespace mongo { class ExternalSortComparison; + class RecordStore; /** * Any access method that is Btree based subclasses from this. @@ -56,7 +57,8 @@ namespace mongo { class BtreeBasedAccessMethod : public IndexAccessMethod { MONGO_DISALLOW_COPYING( BtreeBasedAccessMethod ); public: - BtreeBasedAccessMethod( IndexCatalogEntry* btreeState ); + BtreeBasedAccessMethod( IndexCatalogEntry* btreeState, + RecordStore* recordStore ); virtual ~BtreeBasedAccessMethod() { } @@ -86,7 +88,7 @@ namespace mongo { virtual Status initializeAsEmpty(OperationContext* txn); - virtual IndexAccessMethod* initiateBulk(OperationContext* txn) ; + virtual IndexAccessMethod* initiateBulk(OperationContext* txn, int64_t numRecords ); virtual Status commitBulk( IndexAccessMethod* bulk, bool mayInterrupt, @@ -111,15 +113,9 @@ namespace mongo { virtual void getKeys(const BSONObj &obj, BSONObjSet *keys) = 0; IndexCatalogEntry* _btreeState; // owned by IndexCatalogEntry + scoped_ptr<RecordStore> _recordStore; // owned by us const IndexDescriptor* _descriptor; - /** - * The collection is needed for resolving record locations to actual objects. - */ - const Collection* collection() const { - return _btreeState->collection(); - } - private: bool removeOneKey(OperationContext* txn, const BSONObj& key, diff --git a/src/mongo/db/index/btree_based_bulk_access_method.cpp b/src/mongo/db/index/btree_based_bulk_access_method.cpp index 5d2f0906564..ca6e6022d53 100644 --- a/src/mongo/db/index/btree_based_bulk_access_method.cpp +++ b/src/mongo/db/index/btree_based_bulk_access_method.cpp @@ -141,7 +141,7 @@ namespace mongo { // XXX: do we expect the tree to be empty but have a head set? Looks like so from old code. invariant(!oldHead.isNull()); _real->_btreeState->setHead(_txn, DiskLoc()); - _real->_btreeState->recordStore()->deleteRecord(_txn, oldHead); + _real->_recordStore->deleteRecord(_txn, oldHead); if (_isMultiKey) { _real->_btreeState->setMultikey( _txn ); diff --git a/src/mongo/db/index/btree_based_bulk_access_method.h b/src/mongo/db/index/btree_based_bulk_access_method.h index cedf1eedf80..856a1c3b1de 100644 --- a/src/mongo/db/index/btree_based_bulk_access_method.h +++ b/src/mongo/db/index/btree_based_bulk_access_method.h @@ -119,7 +119,7 @@ namespace mongo { return _notAllowed(); } - virtual IndexAccessMethod* initiateBulk(OperationContext* txn) { + virtual IndexAccessMethod* initiateBulk(OperationContext* txn, int64_t numRecords ) { return NULL; } diff --git a/src/mongo/db/index/fts_access_method.cpp b/src/mongo/db/index/fts_access_method.cpp index c803867b812..5eb20d4f8cf 100644 --- a/src/mongo/db/index/fts_access_method.cpp +++ b/src/mongo/db/index/fts_access_method.cpp @@ -31,8 +31,8 @@ namespace mongo { - FTSAccessMethod::FTSAccessMethod(IndexCatalogEntry* btreeState) - : BtreeBasedAccessMethod(btreeState), _ftsSpec(btreeState->descriptor()->infoObj()) { } + FTSAccessMethod::FTSAccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs ) + : BtreeBasedAccessMethod(btreeState, rs), _ftsSpec(btreeState->descriptor()->infoObj()) { } void FTSAccessMethod::getKeys(const BSONObj& obj, BSONObjSet* keys) { ExpressionKeysPrivate::getFTSKeys(obj, _ftsSpec, keys); diff --git a/src/mongo/db/index/fts_access_method.h b/src/mongo/db/index/fts_access_method.h index ba10b4e2b67..2ea4fecc375 100644 --- a/src/mongo/db/index/fts_access_method.h +++ b/src/mongo/db/index/fts_access_method.h @@ -38,7 +38,7 @@ namespace mongo { class FTSAccessMethod : public BtreeBasedAccessMethod { public: - FTSAccessMethod(IndexCatalogEntry* btreeState ); + FTSAccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs ); virtual ~FTSAccessMethod() { } const fts::FTSSpec& getSpec() const { return _ftsSpec; } diff --git a/src/mongo/db/index/hash_access_method.cpp b/src/mongo/db/index/hash_access_method.cpp index 7457c68c085..bb3684c020a 100644 --- a/src/mongo/db/index/hash_access_method.cpp +++ b/src/mongo/db/index/hash_access_method.cpp @@ -33,8 +33,8 @@ namespace mongo { - HashAccessMethod::HashAccessMethod(IndexCatalogEntry* btreeState) - : BtreeBasedAccessMethod(btreeState) { + HashAccessMethod::HashAccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs) + : BtreeBasedAccessMethod(btreeState, rs) { const IndexDescriptor* descriptor = btreeState->descriptor(); diff --git a/src/mongo/db/index/hash_access_method.h b/src/mongo/db/index/hash_access_method.h index e6e701158ce..0b6777a16da 100644 --- a/src/mongo/db/index/hash_access_method.h +++ b/src/mongo/db/index/hash_access_method.h @@ -45,7 +45,7 @@ namespace mongo { public: using BtreeBasedAccessMethod::_descriptor; - HashAccessMethod(IndexCatalogEntry* btreeState); + HashAccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs); virtual ~HashAccessMethod() { } // This is a NO-OP. diff --git a/src/mongo/db/index/haystack_access_method.cpp b/src/mongo/db/index/haystack_access_method.cpp index 59e97a6191c..9bf1ee6593b 100644 --- a/src/mongo/db/index/haystack_access_method.cpp +++ b/src/mongo/db/index/haystack_access_method.cpp @@ -39,8 +39,8 @@ namespace mongo { - HaystackAccessMethod::HaystackAccessMethod(IndexCatalogEntry* btreeState) - : BtreeBasedAccessMethod(btreeState) { + HaystackAccessMethod::HaystackAccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs) + : BtreeBasedAccessMethod(btreeState, rs) { const IndexDescriptor* descriptor = btreeState->descriptor(); @@ -57,7 +57,8 @@ namespace mongo { ExpressionKeysPrivate::getHaystackKeys(obj, _geoField, _otherFields, _bucketSize, keys); } - void HaystackAccessMethod::searchCommand(const BSONObj& nearObj, double maxDistance, + void HaystackAccessMethod::searchCommand(Collection* collection, + const BSONObj& nearObj, double maxDistance, const BSONObj& search, BSONObjBuilder* result, unsigned limit) { Timer t; @@ -72,7 +73,7 @@ namespace mongo { } int scale = static_cast<int>(ceil(maxDistance / _bucketSize)); - GeoHaystackSearchHopper hopper(nearObj, maxDistance, limit, _geoField, collection()); + GeoHaystackSearchHopper hopper(nearObj, maxDistance, limit, _geoField, collection); long long btreeMatches = 0; @@ -95,7 +96,7 @@ namespace mongo { unordered_set<DiskLoc, DiskLoc::Hasher> thisPass; - scoped_ptr<Runner> runner(InternalPlanner::indexScan(_btreeState->collection(), + scoped_ptr<Runner> runner(InternalPlanner::indexScan(collection, _descriptor, key, key, true)); Runner::RunnerState state; DiskLoc loc; diff --git a/src/mongo/db/index/haystack_access_method.h b/src/mongo/db/index/haystack_access_method.h index 82d8eccf6ef..e1a4fe9e7df 100644 --- a/src/mongo/db/index/haystack_access_method.h +++ b/src/mongo/db/index/haystack_access_method.h @@ -35,6 +35,8 @@ namespace mongo { + class Collection; + /** * Maps (lat, lng) to the bucketSize-sided square bucket that contains it. * Examines all documents in a given radius of a given point. @@ -55,12 +57,13 @@ namespace mongo { public: using BtreeBasedAccessMethod::_descriptor; - HaystackAccessMethod(IndexCatalogEntry* btreeState); + HaystackAccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs); virtual ~HaystackAccessMethod() { } protected: friend class GeoHaystackSearchCommand; - void searchCommand(const BSONObj& nearObj, double maxDistance, const BSONObj& search, + void searchCommand(Collection* collection, + const BSONObj& nearObj, double maxDistance, const BSONObj& search, BSONObjBuilder* result, unsigned limit); private: diff --git a/src/mongo/db/index/index_access_method.h b/src/mongo/db/index/index_access_method.h index 6268ad0cb90..efdc59384aa 100644 --- a/src/mongo/db/index/index_access_method.h +++ b/src/mongo/db/index/index_access_method.h @@ -168,7 +168,7 @@ namespace mongo { * For now (1/8/14) you can only do bulk when the index is empty * it will fail if you try other times. */ - virtual IndexAccessMethod* initiateBulk(OperationContext* txn) = 0; + virtual IndexAccessMethod* initiateBulk(OperationContext* txn, int64_t numRecords ) = 0; /** * Call this when you are ready to finish your bulk work. diff --git a/src/mongo/db/index/s2_access_method.cpp b/src/mongo/db/index/s2_access_method.cpp index 9cbfd2d418b..c75ec66d44e 100644 --- a/src/mongo/db/index/s2_access_method.cpp +++ b/src/mongo/db/index/s2_access_method.cpp @@ -43,8 +43,8 @@ namespace mongo { static const string kIndexVersionFieldName("2dsphereIndexVersion"); - S2AccessMethod::S2AccessMethod(IndexCatalogEntry* btreeState) - : BtreeBasedAccessMethod(btreeState) { + S2AccessMethod::S2AccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs) + : BtreeBasedAccessMethod(btreeState, rs) { const IndexDescriptor* descriptor = btreeState->descriptor(); diff --git a/src/mongo/db/index/s2_access_method.h b/src/mongo/db/index/s2_access_method.h index df12026395b..666d2bb1e98 100644 --- a/src/mongo/db/index/s2_access_method.h +++ b/src/mongo/db/index/s2_access_method.h @@ -43,7 +43,7 @@ namespace mongo { public: using BtreeBasedAccessMethod::_descriptor; - S2AccessMethod(IndexCatalogEntry* btreeState); + S2AccessMethod(IndexCatalogEntry* btreeState, RecordStore* rs); virtual ~S2AccessMethod() { } /** diff --git a/src/mongo/db/index_rebuilder.cpp b/src/mongo/db/index_rebuilder.cpp index da1dc347cec..015a39b95cb 100644 --- a/src/mongo/db/index_rebuilder.cpp +++ b/src/mongo/db/index_rebuilder.cpp @@ -36,6 +36,7 @@ #include "mongo/db/instance.h" #include "mongo/db/pdfile.h" #include "mongo/db/repl/rs.h" +#include "mongo/db/structure/catalog/namespace_index.h" #include "mongo/db/operation_context_impl.h" #include "mongo/util/scopeguard.h" @@ -64,7 +65,7 @@ namespace mongo { dbName++) { Client::ReadContext ctx(*dbName); Database* db = ctx.ctx().db(); - db->namespaceIndex().getNamespaces(collNames, /* onlyCollections */ true); + db->namespaceIndex()->getNamespaces(collNames, /* onlyCollections */ true); } checkNS(collNames); } diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h index 0639c686dc5..53ed2447fb4 100644 --- a/src/mongo/db/namespace_string.h +++ b/src/mongo/db/namespace_string.h @@ -95,6 +95,7 @@ namespace mongo { bool isValid() const { return validDBName( db() ) && !coll().empty(); } bool operator==( const std::string& nsIn ) const { return nsIn == _ns; } + bool operator==( const StringData& nsIn ) const { return nsIn == _ns; } bool operator==( const NamespaceString& nsIn ) const { return nsIn._ns == _ns; } bool operator!=( const std::string& nsIn ) const { return nsIn != _ns; } diff --git a/src/mongo/db/operation_context.h b/src/mongo/db/operation_context.h index 25da4ee3560..d7bfc4f8f5c 100644 --- a/src/mongo/db/operation_context.h +++ b/src/mongo/db/operation_context.h @@ -32,6 +32,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" +#include "mongo/base/string_data.h" #include "mongo/db/storage/recovery_unit.h" namespace mongo { @@ -80,6 +81,11 @@ namespace mongo { int secondsBetween = 3) = 0; /** + * @return true if this instance is primary for this namespace + */ + virtual bool isPrimaryFor( const StringData& ns ) = 0; + + /** * Returns a OperationContext. Caller takes ownership. * * This interface is used for functions that need to create transactions (aka OpCtx), but diff --git a/src/mongo/db/operation_context_impl.cpp b/src/mongo/db/operation_context_impl.cpp index 055ab5d6ef0..dde8ded49c1 100644 --- a/src/mongo/db/operation_context_impl.cpp +++ b/src/mongo/db/operation_context_impl.cpp @@ -31,6 +31,7 @@ #include "mongo/db/client.h" #include "mongo/db/curop.h" #include "mongo/db/kill_current_op.h" +#include "mongo/db/repl/is_master.h" #include "mongo/db/storage/mmap_v1/dur_recovery_unit.h" namespace mongo { @@ -62,6 +63,11 @@ namespace mongo { return Status( ErrorCodes::Interrupted, killed ); } + bool OperationContextImpl::isPrimaryFor( const StringData& ns ) { + string s = ns.toString(); // TODO: fix copy + return isMasterNs( s.c_str() ); + } + OperationContext* OperationContextImpl::factory() { return new OperationContextImpl(); } diff --git a/src/mongo/db/operation_context_impl.h b/src/mongo/db/operation_context_impl.h index 6f7b5a7ff2d..01d21f3acff 100644 --- a/src/mongo/db/operation_context_impl.h +++ b/src/mongo/db/operation_context_impl.h @@ -52,6 +52,8 @@ namespace mongo { virtual Status checkForInterruptNoAssert() const; + virtual bool isPrimaryFor( const StringData& ns ); + /** * Returns an OperationContext. Caller takes ownership. */ diff --git a/src/mongo/db/operation_context_noop.h b/src/mongo/db/operation_context_noop.h index de5832215a7..b442f872ad9 100644 --- a/src/mongo/db/operation_context_noop.h +++ b/src/mongo/db/operation_context_noop.h @@ -58,6 +58,10 @@ namespace mongo { return Status::OK(); } + virtual bool isPrimaryFor( const StringData& ns ) { + return true; + } + private: boost::scoped_ptr<RecoveryUnitNoop> _recoveryUnit; }; diff --git a/src/mongo/db/repair_database.cpp b/src/mongo/db/repair_database.cpp index 0e5087c0b4c..72620d77609 100644 --- a/src/mongo/db/repair_database.cpp +++ b/src/mongo/db/repair_database.cpp @@ -41,6 +41,7 @@ #include "mongo/db/index/index_descriptor.h" #include "mongo/util/file.h" #include "mongo/util/file_allocator.h" +#include "mongo/util/mmap.h" namespace mongo { diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_engine.cpp b/src/mongo/db/storage/mmap_v1/mmap_v1_engine.cpp new file mode 100644 index 00000000000..a8edffb2642 --- /dev/null +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_engine.cpp @@ -0,0 +1,333 @@ +// mmap_v1_engine.cpp + +/** +* Copyright (C) 2014 MongoDB 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/>. +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects for +* all of the code used other than as permitted herein. If you modify file(s) +* with this exception, you may extend this exception to your version of the +* file(s), but you are not obligated to do so. If you do not wish to do so, +* delete this exception statement from your version. If you delete this +* exception statement from all source files in the program, then also delete +* it in the license file. +*/ + +#include "mongo/db/storage/mmap_v1/mmap_v1_engine.h" + +#include "mongo/db/catalog/database.h" +#include "mongo/db/catalog/index_catalog_entry.h" +#include "mongo/db/index/2d_access_method.h" +#include "mongo/db/index/btree_access_method.h" +#include "mongo/db/index/btree_based_access_method.h" +#include "mongo/db/index/fts_access_method.h" +#include "mongo/db/index/hash_access_method.h" +#include "mongo/db/index/haystack_access_method.h" +#include "mongo/db/index/s2_access_method.h" +#include "mongo/db/server_parameters.h" +#include "mongo/db/structure/catalog/namespace_details.h" +#include "mongo/db/structure/catalog/namespace_details_collection_entry.h" +#include "mongo/db/structure/catalog/namespace_details_rsv1_metadata.h" +#include "mongo/db/structure/record_store_v1_capped.h" +#include "mongo/db/structure/record_store_v1_simple.h" + +namespace mongo { + + MONGO_EXPORT_SERVER_PARAMETER(newCollectionsUsePowerOf2Sizes, bool, true); + + MMAP1DatabaseCatalogEntry::MMAP1DatabaseCatalogEntry( OperationContext* txn, + const StringData& name, + const StringData& path, + bool directoryPerDB ) + : _name( name.toString() ), + _path( path.toString() ), + _extentManager( name, path, directoryPerDB ), + _namespaceIndex( _path, _name ) { + + try { + _checkDuplicateUncasedNames(); + + Status s = _extentManager.init(txn); + if ( !s.isOK() ) { + msgasserted( 16966, str::stream() << "_extentManager.init failed: " << s.toString() ); + } + + // If already exists, open. Otherwise behave as if empty until + // there's a write, then open. + + if ( _namespaceIndex.pathExists() ) { + _namespaceIndex.init( txn ); + + // upgrade freelist + string oldFreeList = _name + ".$freelist"; + NamespaceDetails* details = _namespaceIndex.details( oldFreeList ); + if ( details ) { + if ( !details->firstExtent.isNull() ) { + _extentManager.freeExtents(txn, + details->firstExtent, + details->lastExtent); + } + _namespaceIndex.kill_ns( txn, oldFreeList ); + } + } + } + catch(std::exception& e) { + log() << "warning database " << path << " " << name << " could not be opened"; + DBException* dbe = dynamic_cast<DBException*>(&e); + if ( dbe != 0 ) { + log() << "DBException " << dbe->getCode() << ": " << e.what() << endl; + } + else { + log() << e.what() << endl; + } + _extentManager.reset(); + throw; + } + + + } + + MMAP1DatabaseCatalogEntry::~MMAP1DatabaseCatalogEntry() { + } + + void MMAP1DatabaseCatalogEntry::_checkDuplicateUncasedNames() const { + string duplicate = Database::duplicateUncasedName(true, _name, _path ); + if ( !duplicate.empty() ) { + stringstream ss; + ss << "db already exists with different case already have: [" << duplicate + << "] trying to create [" << _name << "]"; + uasserted( DatabaseDifferCaseCode , ss.str() ); + } + } + + namespace { + int _massageExtentSize( const ExtentManager* em, long long size ) { + if ( size < em->minSize() ) + return em->minSize(); + if ( size > em->maxSize() ) + return em->maxSize(); + return static_cast<int>( size ); + } + } + + Status MMAP1DatabaseCatalogEntry::createCollection( OperationContext* txn, + const StringData& ns, + const CollectionOptions& options, + bool allocateDefaultSpace ) { + _namespaceIndex.init( txn ); + + if ( _namespaceIndex.details( ns ) ) { + return Status( ErrorCodes::NamespaceExists, + str::stream() << "namespace already exists: " << ns ); + } + + BSONObj optionsAsBSON = options.toBSON(); + _addNamespaceToNamespaceCollection( txn, ns, &optionsAsBSON ); + + _namespaceIndex.add_ns( txn, ns, DiskLoc(), options.capped ); + + // allocation strategy set explicitly in flags or by server-wide default + if ( !options.capped ) { + NamespaceDetailsRSV1MetaData md( ns, + _namespaceIndex.details( ns ), + _getNamespaceRecordStore( txn, ns ) ); + + if ( options.flagsSet ) { + md.setUserFlag( txn, options.flags ); + } + else if ( newCollectionsUsePowerOf2Sizes ) { + md.setUserFlag( txn, NamespaceDetails::Flag_UsePowerOf2Sizes ); + } + } + else if ( options.cappedMaxDocs > 0 ) { + txn->recoveryUnit()->writingInt( _namespaceIndex.details( ns )->maxDocsInCapped ) = + options.cappedMaxDocs; + } + + if ( allocateDefaultSpace ) { + scoped_ptr<RecordStoreV1Base> rs( _getRecordStore( txn, ns ) ); + if ( options.initialNumExtents > 0 ) { + int size = _massageExtentSize( &_extentManager, options.cappedSize ); + for ( int i = 0; i < options.initialNumExtents; i++ ) { + rs->increaseStorageSize( txn, size, -1 ); + } + } + else if ( !options.initialExtentSizes.empty() ) { + for ( size_t i = 0; i < options.initialExtentSizes.size(); i++ ) { + int size = options.initialExtentSizes[i]; + size = _massageExtentSize( &_extentManager, size ); + rs->increaseStorageSize( txn, size, -1 ); + } + } + else if ( options.capped ) { + // normal + while ( rs->storageSize() < options.cappedSize ) { + int sz = _massageExtentSize( &_extentManager, + options.cappedSize - rs->storageSize() ); + sz &= 0xffffff00; + rs->increaseStorageSize( txn, sz, -1 ); + } + } + else { + rs->increaseStorageSize( txn, _extentManager.initialSize( 128 ), -1 ); + } + } + + return Status::OK(); + } + + CollectionCatalogEntry* MMAP1DatabaseCatalogEntry::getCollectionCatalogEntry( OperationContext* txn, + const StringData& ns ) { + NamespaceDetails* details = _namespaceIndex.details( ns ); + if ( !details ) { + return NULL; + } + + return new NamespaceDetailsCollectionCatalogEntry( ns, + details, + _getIndexRecordStore( txn ), + this ); + } + + RecordStore* MMAP1DatabaseCatalogEntry::getRecordStore( OperationContext* txn, + const StringData& ns ) { + return _getRecordStore( txn, ns ); + } + + RecordStoreV1Base* MMAP1DatabaseCatalogEntry::_getRecordStore( OperationContext* txn, + const StringData& ns ) { + + // XXX TODO - CACHE + + NamespaceString nss( ns ); + NamespaceDetails* details = _namespaceIndex.details( ns ); + if ( !details ) { + return NULL; + } + + auto_ptr<NamespaceDetailsRSV1MetaData> md( new NamespaceDetailsRSV1MetaData( ns, + details, + _getNamespaceRecordStore( txn, ns ) ) ); + + if ( details->isCapped ) { + return new CappedRecordStoreV1( txn, + NULL, //TOD(ERH) this will blow up :) + ns, + md.release(), + &_extentManager, + nss.coll() == "system.indexes" ); + } + + return new SimpleRecordStoreV1( txn, + ns, + md.release(), + &_extentManager, + nss.coll() == "system.indexes" ); + } + + IndexAccessMethod* MMAP1DatabaseCatalogEntry::getIndex( OperationContext* txn, + const CollectionCatalogEntry* collection, + IndexCatalogEntry* entry ) { + const string& type = entry->descriptor()->getAccessMethodName(); + + string ns = collection->ns().ns(); + + if ( IndexNames::TEXT == type || + entry->descriptor()->getInfoElement("expireAfterSeconds").isNumber() ) { + NamespaceDetailsRSV1MetaData md( ns, + _namespaceIndex.details( ns ), + _getNamespaceRecordStore( txn, ns ) ); + md.setUserFlag( txn, NamespaceDetails::Flag_UsePowerOf2Sizes ); + } + + RecordStore* rs = _getRecordStore( txn, entry->descriptor()->indexNamespace() ); + invariant( rs ); + + if (IndexNames::HASHED == type) + return new HashAccessMethod( entry, rs ); + + if (IndexNames::GEO_2DSPHERE == type) + return new S2AccessMethod( entry, rs ); + + if (IndexNames::TEXT == type) + return new FTSAccessMethod( entry, rs ); + + if (IndexNames::GEO_HAYSTACK == type) + return new HaystackAccessMethod( entry, rs ); + + if ("" == type) + return new BtreeAccessMethod( entry, rs ); + + if (IndexNames::GEO_2D == type) + return new TwoDAccessMethod( entry, rs ); + + log() << "Can't find index for keyPattern " << entry->descriptor()->keyPattern(); + fassertFailed(17489); + } + + RecordStoreV1Base* MMAP1DatabaseCatalogEntry::_getIndexRecordStore( OperationContext* txn ) { + NamespaceString nss( _name, "system.indexes" ); + RecordStoreV1Base* rs = _getRecordStore( txn, nss.ns() ); + if ( rs != NULL ) + return rs; + CollectionOptions options; + Status status = createCollection( txn, nss.ns(), options, true ); + massertStatusOK( status ); + rs = _getRecordStore( txn, nss.ns() ); + invariant( rs ); + return rs; + } + + RecordStoreV1Base* MMAP1DatabaseCatalogEntry::_getNamespaceRecordStore( OperationContext* txn, + const StringData& whosAsking) { + NamespaceString nss( _name, "system.namespaces" ); + if ( nss == whosAsking ) + return NULL; + RecordStoreV1Base* rs = _getRecordStore( txn, nss.ns() ); + if ( rs != NULL ) + return rs; + CollectionOptions options; + Status status = createCollection( txn, nss.ns(), options, true ); + massertStatusOK( status ); + rs = _getRecordStore( txn, nss.ns() ); + invariant( rs ); + return rs; + + } + + void MMAP1DatabaseCatalogEntry::_addNamespaceToNamespaceCollection( OperationContext* txn, + const StringData& ns, + const BSONObj* options ) { + if ( nsToCollectionSubstring( ns ) == "system.namespaces" ) { + // system.namespaces holds all the others, so it is not explicitly listed in the catalog. + return; + } + + BSONObjBuilder b; + b.append("name", ns); + if ( options && !options->isEmpty() ) + b.append("options", *options); + BSONObj obj = b.done(); + + RecordStoreV1Base* rs = _getNamespaceRecordStore( txn, ns ); + invariant( rs ); + StatusWith<DiskLoc> loc = rs->insertRecord( txn, obj.objdata(), obj.objsize(), -1 ); + massertStatusOK( loc.getStatus() ); + } + +} diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_engine.h b/src/mongo/db/storage/mmap_v1/mmap_v1_engine.h new file mode 100644 index 00000000000..09d8be4112f --- /dev/null +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_engine.h @@ -0,0 +1,111 @@ +// mmap_v1_engine.h + +/** +* Copyright (C) 2014 MongoDB 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/>. +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects for +* all of the code used other than as permitted herein. If you modify file(s) +* with this exception, you may extend this exception to your version of the +* file(s), but you are not obligated to do so. If you do not wish to do so, +* delete this exception statement from your version. If you delete this +* exception statement from all source files in the program, then also delete +* it in the license file. +*/ + +#include "mongo/base/status.h" +#include "mongo/base/string_data.h" +#include "mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h" +#include "mongo/db/structure/catalog/namespace_index.h" + +namespace mongo { + + class CollectionCatalogEntry; + struct CollectionOptions; + class IndexAccessMethod; + class IndexCatalogEntry; + class IndexDescriptor; + class RecordStore; + class RecordStoreV1Base; + class OperationContext; + + class MMAP1DatabaseCatalogEntry { + public: + MMAP1DatabaseCatalogEntry( OperationContext* txn, + const StringData& name, + const StringData& path, + bool directoryperdb ); + + virtual ~MMAP1DatabaseCatalogEntry(); + + bool exists() const { return _namespaceIndex.pathExists(); } + + Status createCollection( OperationContext* txn, + const StringData& ns, + const CollectionOptions& options, + bool allocateDefaultSpace ); + + /* + * ownership passes to caller + * will return NULL if ns does not exist + */ + CollectionCatalogEntry* getCollectionCatalogEntry( OperationContext* txn, + const StringData& ns ); + + // ownership passes to caller + RecordStore* getRecordStore( OperationContext* txn, + const StringData& ns ); + + // ownership passes to caller + IndexAccessMethod* getIndex( OperationContext* txn, + const CollectionCatalogEntry* collection, + IndexCatalogEntry* index ); + + const MmapV1ExtentManager* getExtentManager() const { return &_extentManager; } // TODO(ERH): remove + MmapV1ExtentManager* getExtentManager() { return &_extentManager; } // TODO(ERH): remove + + const NamespaceIndex& namespaceIndex() const { return _namespaceIndex; } // TODO(ERH): remove + NamespaceIndex& namespaceIndex() { return _namespaceIndex; } // TODO(ERH): remove + + private: + + RecordStoreV1Base* _getIndexRecordStore( OperationContext* txn ); + RecordStoreV1Base* _getNamespaceRecordStore( OperationContext* txn, + const StringData& whosAsking ); + + RecordStoreV1Base* _getRecordStore( OperationContext* txn, + const StringData& ns ); + + void _addNamespaceToNamespaceCollection( OperationContext* txn, + const StringData& ns, + const BSONObj* options ); + /** + * @throws DatabaseDifferCaseCode if the name is a duplicate based on + * case insensitive matching. + */ + void _checkDuplicateUncasedNames() const; + + std::string _name; + std::string _path; + + MmapV1ExtentManager _extentManager; + NamespaceIndex _namespaceIndex; + + friend class NamespaceDetailsCollectionCatalogEntry; + }; +} diff --git a/src/mongo/db/structure/btree/btree_logic.h b/src/mongo/db/structure/btree/btree_logic.h index e0c9942c12e..3cbc5e35241 100644 --- a/src/mongo/db/structure/btree/btree_logic.h +++ b/src/mongo/db/structure/btree/btree_logic.h @@ -42,6 +42,7 @@ namespace mongo { class BucketDeletionNotification; + class RecordStore; /** * This is the logic for manipulating the Btree. It is (mostly) independent of the on-disk diff --git a/src/mongo/db/structure/catalog/namespace_details.cpp b/src/mongo/db/structure/catalog/namespace_details.cpp index e94f6118737..3b3216188fa 100644 --- a/src/mongo/db/structure/catalog/namespace_details.cpp +++ b/src/mongo/db/structure/catalog/namespace_details.cpp @@ -57,30 +57,30 @@ namespace mongo { BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) <= sizeof(NamespaceDetails) ); /* be sure to initialize new fields here -- doesn't default to zeroes the way we use it */ - _firstExtent = _lastExtent = _capExtent = loc; - _stats.datasize = _stats.nrecords = 0; - _lastExtentSize = 0; - _nIndexes = 0; - _isCapped = capped; - _maxDocsInCapped = 0x7fffffff; // no limit (value is for pre-v2.3.2 compatibility) - _paddingFactor = 1.0; - _systemFlagsOld = 0; - _userFlags = 0; - _capFirstNewRecord = DiskLoc(); + firstExtent = lastExtent = capExtent = loc; + stats.datasize = stats.nrecords = 0; + lastExtentSize = 0; + nIndexes = 0; + isCapped = capped; + maxDocsInCapped = 0x7fffffff; // no limit (value is for pre-v2.3.2 compatibility) + paddingFactor = 1.0; + systemFlagsOldDoNotUse = 0; + userFlags = 0; + capFirstNewRecord = DiskLoc(); // Signal that we are on first allocation iteration through extents. - _capFirstNewRecord.setInvalid(); + capFirstNewRecord.setInvalid(); // For capped case, signal that we are doing initial extent allocation. if ( capped ) { // WAS: cappedLastDelRecLastExtent().setInvalid(); - _deletedList[1].setInvalid(); + deletedList[1].setInvalid(); } verify( sizeof(_dataFileVersion) == 2 ); _dataFileVersion = 0; _indexFileVersion = 0; - _multiKeyIndexBits = 0; + multiKeyIndexBits = 0; _reservedA = 0; _extraOffset = 0; - _indexBuildsInProgress = 0; + indexBuildsInProgress = 0; memset(_reserved, 0, sizeof(_reserved)); } @@ -119,49 +119,6 @@ namespace mongo { return e; } - bool NamespaceDetails::setIndexIsMultikey(OperationContext* txn, int i, bool multikey) { - massert(16577, "index number greater than NIndexesMax", i < NIndexesMax ); - - unsigned long long mask = 1ULL << i; - - if (multikey) { - // Shortcut if the bit is already set correctly - if (_multiKeyIndexBits & mask) { - return false; - } - - *txn->recoveryUnit()->writing(&_multiKeyIndexBits) |= mask; - } - else { - // Shortcut if the bit is already set correctly - if (!(_multiKeyIndexBits & mask)) { - return false; - } - - // Invert mask: all 1's except a 0 at the ith bit - mask = ~mask; - *txn->recoveryUnit()->writing(&_multiKeyIndexBits) &= mask; - } - - return true; - } - - IndexDetails& NamespaceDetails::getNextIndexDetails(OperationContext* txn, - Collection* collection) { - IndexDetails *id; - try { - id = &idx(getTotalIndexCount(), true); - } - catch(DBException&) { - allocExtra(txn, - collection->ns().ns(), - collection->_database->namespaceIndex(), - getTotalIndexCount()); - id = &idx(getTotalIndexCount(), false); - } - return *id; - } - IndexDetails& NamespaceDetails::idx(int idxNo, bool missingExpected) { if( idxNo < NIndexesBase ) { IndexDetails& id = _indexes[idxNo]; @@ -211,12 +168,13 @@ namespace mongo { return e->details[i]; } - NamespaceDetails::IndexIterator::IndexIterator(const NamespaceDetails *_d, bool includeBackgroundInProgress) { d = _d; i = 0; - n = includeBackgroundInProgress ? d->getTotalIndexCount() : d->_nIndexes; + n = d->nIndexes; + if ( includeBackgroundInProgress ) + n += d->indexBuildsInProgress; } // must be called when renaming a NS to fix up extra @@ -259,7 +217,7 @@ namespace mongo { massert( 16499, "max in a capped collection has to be < 2^31 or -1", validMaxCappedDocs( &max ) ); - _maxDocsInCapped = max; + maxDocsInCapped = max; } bool NamespaceDetails::validMaxCappedDocs( long long* max ) { @@ -276,155 +234,8 @@ namespace mongo { return false; } - long long NamespaceDetails::maxCappedDocs() const { - verify( isCapped() ); - if ( _maxDocsInCapped == 0x7fffffff ) - return numeric_limits<long long>::max(); - return _maxDocsInCapped; - } - /* ------------------------------------------------------------------------- */ - void NamespaceDetails::setLastExtentSize( OperationContext* txn, int newMax ) { - if ( _lastExtentSize == newMax ) - return; - txn->recoveryUnit()->writingInt(_lastExtentSize) = newMax; - } - - void NamespaceDetails::incrementStats( OperationContext* txn, - long long dataSizeIncrement, - long long numRecordsIncrement ) { - - // durability todo : this could be a bit annoying / slow to record constantly - Stats* s = txn->recoveryUnit()->writing( &_stats ); - s->datasize += dataSizeIncrement; - s->nrecords += numRecordsIncrement; - } - - void NamespaceDetails::setStats( OperationContext* txn, - long long dataSize, - long long numRecords ) { - Stats* s = txn->recoveryUnit()->writing( &_stats ); - s->datasize = dataSize; - s->nrecords = numRecords; - } - - void NamespaceDetails::setFirstExtent( OperationContext* txn, - const DiskLoc& loc ) { - *txn->recoveryUnit()->writing( &_firstExtent ) = loc; - } - - void NamespaceDetails::setLastExtent( OperationContext* txn, - const DiskLoc& loc ) { - *txn->recoveryUnit()->writing( &_lastExtent ) = loc; - } - - void NamespaceDetails::setCapExtent( OperationContext* txn, - const DiskLoc& loc ) { - *txn->recoveryUnit()->writing( &_capExtent ) = loc; - } - - void NamespaceDetails::setCapFirstNewRecord( OperationContext* txn, - const DiskLoc& loc ) { - *txn->recoveryUnit()->writing( &_capFirstNewRecord ) = loc; - } - - void NamespaceDetails::setFirstExtentInvalid( OperationContext* txn ) { - *txn->recoveryUnit()->writing( &_firstExtent ) = DiskLoc().setInvalid(); - } - - void NamespaceDetails::setLastExtentInvalid( OperationContext* txn ) { - *txn->recoveryUnit()->writing( &_lastExtent ) = DiskLoc().setInvalid(); - } - - void NamespaceDetails::setDeletedListEntry( OperationContext* txn, - int bucket, const DiskLoc& loc ) { - *txn->recoveryUnit()->writing( &_deletedList[bucket] ) = loc; - } - - bool NamespaceDetails::setUserFlag( OperationContext* txn, int flags ) { - if ( ( _userFlags & flags ) == flags ) - return false; - - txn->recoveryUnit()->writingInt(_userFlags) |= flags; - return true; - } - - bool NamespaceDetails::clearUserFlag( OperationContext* txn, int flags ) { - if ( ( _userFlags & flags ) == 0 ) - return false; - - txn->recoveryUnit()->writingInt(_userFlags) &= ~flags; - return true; - } - - bool NamespaceDetails::replaceUserFlags( OperationContext* txn, int flags ) { - if ( flags == _userFlags ) - return false; - - txn->recoveryUnit()->writingInt(_userFlags) = flags; - return true; - } - - void NamespaceDetails::setPaddingFactor( OperationContext* txn, double paddingFactor ) { - if ( paddingFactor == _paddingFactor ) - return; - - if ( isCapped() ) - return; - - *txn->recoveryUnit()->writing(&_paddingFactor) = paddingFactor; - } - - /* remove bit from a bit array - actually remove its slot, not a clear - note: this function does not work with x == 63 -- that is ok - but keep in mind in the future if max indexes were extended to - exactly 64 it would be a problem - */ - unsigned long long removeAndSlideBit(unsigned long long b, int x) { - unsigned long long tmp = b; - return - (tmp & ((((unsigned long long) 1) << x)-1)) | - ((tmp >> (x+1)) << x); - } - - void NamespaceDetails::_removeIndexFromMe( OperationContext* txn, int idxNumber ) { - - // TODO: don't do this whole thing, do it piece meal for readability - NamespaceDetails* d = writingWithExtra( txn ); - - // fix the _multiKeyIndexBits, by moving all bits above me down one - d->_multiKeyIndexBits = removeAndSlideBit(d->_multiKeyIndexBits, idxNumber); - - if ( idxNumber >= _nIndexes ) - d->_indexBuildsInProgress--; - else - d->_nIndexes--; - - for ( int i = idxNumber; i < getTotalIndexCount(); i++ ) - d->idx(i) = d->idx(i+1); - - d->idx( getTotalIndexCount() ) = IndexDetails(); - } - - void NamespaceDetails::swapIndex( OperationContext* txn, int a, int b ) { - - // flip main meta data - IndexDetails temp = idx(a); - *txn->recoveryUnit()->writing(&idx(a)) = idx(b); - *txn->recoveryUnit()->writing(&idx(b)) = temp; - - // flip multi key bits - bool tempMultikey = isMultikey(a); - setIndexIsMultikey( txn, a, isMultikey(b) ); - setIndexIsMultikey( txn, b, tempMultikey ); - } - - void NamespaceDetails::orphanDeletedList( OperationContext* txn ) { - for( int i = 0; i < Buckets; i++ ) { - *txn->recoveryUnit()->writing(&_deletedList[i]) = DiskLoc(); - } - } int NamespaceDetails::_catalogFindIndexByName(const Collection* coll, const StringData& name, @@ -443,18 +254,4 @@ namespace mongo { *txn->recoveryUnit()->writing(&_next) = ofs; } - /* ------------------------------------------------------------------------- */ - - class IndexUpdateTest : public StartupTest { - public: - void run() { - verify( removeAndSlideBit(1, 0) == 0 ); - verify( removeAndSlideBit(2, 0) == 1 ); - verify( removeAndSlideBit(2, 1) == 0 ); - verify( removeAndSlideBit(255, 1) == 127 ); - verify( removeAndSlideBit(21, 2) == 9 ); - verify( removeAndSlideBit(0x4000000000000001ULL, 62) == 1 ); - } - } iu_unittest; - } // namespace mongo diff --git a/src/mongo/db/structure/catalog/namespace_details.h b/src/mongo/db/structure/catalog/namespace_details.h index 468e5e967b1..3d8411fa8d9 100644 --- a/src/mongo/db/structure/catalog/namespace_details.h +++ b/src/mongo/db/structure/catalog/namespace_details.h @@ -55,12 +55,12 @@ namespace mongo { public: enum { NIndexesMax = 64, NIndexesExtra = 30, NIndexesBase = 10 }; - private: + /*-------- data fields, as present on disk : */ - DiskLoc _firstExtent; - DiskLoc _lastExtent; + DiskLoc firstExtent; + DiskLoc lastExtent; /* NOTE: capped collections v1 override the meaning of deletedList. deletedList[0] points to a list of free records (DeletedRecord's) for all extents in @@ -69,43 +69,50 @@ namespace mongo { changes, this value is updated. !deletedList[1].isValid() when this value is not yet computed. */ - DiskLoc _deletedList[Buckets]; + DiskLoc deletedList[Buckets]; // ofs 168 (8 byte aligned) struct Stats { // datasize and nrecords MUST Be adjacent code assumes! long long datasize; // this includes padding, but not record headers long long nrecords; - } _stats; + } stats; + - int _lastExtentSize; - int _nIndexes; + int lastExtentSize; + + int nIndexes; // ofs 192 IndexDetails _indexes[NIndexesBase]; + public: // ofs 352 (16 byte aligned) - int _isCapped; // there is wasted space here if I'm right (ERH) - int _maxDocsInCapped; // max # of objects for a capped table, -1 for inf. + int isCapped; // there is wasted space here if I'm right (ERH) + + int maxDocsInCapped; // max # of objects for a capped table, -1 for inf. - double _paddingFactor; // 1.0 = no padding. + double paddingFactor; // 1.0 = no padding. // ofs 368 (16) - int _systemFlagsOld; // things that the system sets/cares about + int systemFlagsOldDoNotUse; // things that the system sets/cares about - DiskLoc _capExtent; // the "current" extent we're writing too for a capped collection - DiskLoc _capFirstNewRecord; + DiskLoc capExtent; // the "current" extent we're writing too for a capped collection + DiskLoc capFirstNewRecord; unsigned short _dataFileVersion; // NamespaceDetails version. So we can do backward compatibility in the future. See filever.h unsigned short _indexFileVersion; - unsigned long long _multiKeyIndexBits; + + unsigned long long multiKeyIndexBits; // ofs 400 (16) unsigned long long _reservedA; long long _extraOffset; // where the $extra info is located (bytes relative to this) - int _indexBuildsInProgress; // Number of indexes currently being built + public: + int indexBuildsInProgress; // Number of indexes currently being built + + int userFlags; - int _userFlags; char _reserved[72]; /*-------- end data 496 bytes */ public: @@ -152,63 +159,14 @@ namespace mongo { NamespaceDetails *src); // must be called when renaming a NS to fix up extra public: - const DiskLoc& capExtent() const { return _capExtent; } - void setCapExtent( OperationContext* txn, const DiskLoc& loc ); - - const DiskLoc& capFirstNewRecord() const { return _capFirstNewRecord; } - void setCapFirstNewRecord( OperationContext* txn, const DiskLoc& loc ); - - public: - - const DiskLoc& firstExtent() const { return _firstExtent; } - void setFirstExtent( OperationContext* txn, const DiskLoc& loc ); - - const DiskLoc& lastExtent() const { return _lastExtent; } - void setLastExtent( OperationContext* txn, const DiskLoc& loc ); - - void setFirstExtentInvalid( OperationContext* txn ); - void setLastExtentInvalid( OperationContext* txn ); - - - long long dataSize() const { return _stats.datasize; } - long long numRecords() const { return _stats.nrecords; } - - void incrementStats( OperationContext* txn, - long long dataSizeIncrement, - long long numRecordsIncrement ); - - void setStats( OperationContext* txn, - long long dataSizeIncrement, - long long numRecordsIncrement ); - - - bool isCapped() const { return _isCapped; } - long long maxCappedDocs() const; void setMaxCappedDocs( OperationContext* txn, long long max ); - int lastExtentSize() const { return _lastExtentSize; } - void setLastExtentSize( OperationContext* txn, int newMax ); - - const DiskLoc& deletedListEntry( int bucket ) const { return _deletedList[bucket]; } - void setDeletedListEntry( OperationContext* txn, int bucket, const DiskLoc& loc ); - - void orphanDeletedList( OperationContext* txn ); - /** * @param max in and out, will be adjusted * @return if the value is valid at all */ static bool validMaxCappedDocs( long long* max ); - /* when a background index build is in progress, we don't count the index in nIndexes until - complete, yet need to still use it in _indexRecord() - thus we use this function for that. - */ - int getTotalIndexCount() const { return _nIndexes + _indexBuildsInProgress; } - - int getCompletedIndexCount() const { return _nIndexes; } - - int getIndexBuildsInProgress() const { return _indexBuildsInProgress; } - enum UserFlags { Flag_UsePowerOf2Sizes = 1 << 0 }; @@ -232,73 +190,12 @@ namespace mongo { return IndexIterator(this, includeBackgroundInProgress); } - /* multikey indexes are indexes where there are more than one key in the index - for a single document. see multikey in docs. - for these, we have to do some dedup work on queries. - */ - bool isMultikey(int i) const { return (_multiKeyIndexBits & (((unsigned long long) 1) << i)) != 0; } - - /** - * @return - if any state was changed - */ - bool setIndexIsMultikey(OperationContext* txn, int i, bool multikey = true); - /** * This fetches the IndexDetails for the next empty index slot. The caller must populate * returned object. This handles allocating extra index space, if necessary. */ IndexDetails& getNextIndexDetails(OperationContext* txn, Collection* collection); - double paddingFactor() const { return _paddingFactor; } - - void setPaddingFactor( OperationContext* txn, double paddingFactor ); - - /* called to indicate that an update fit in place. - fits also called on an insert -- idea there is that if you had some mix and then went to - pure inserts it would adapt and PF would trend to 1.0. note update calls insert on a move - so there is a double count there that must be adjusted for below. - - todo: greater sophistication could be helpful and added later. for example the absolute - size of documents might be considered -- in some cases smaller ones are more likely - to grow than larger ones in the same collection? (not always) - */ - void paddingFits( OperationContext* txn ) { - MONGO_SOMETIMES(sometimes, 4) { // do this on a sampled basis to journal less - double x = std::max(1.0, _paddingFactor - 0.001 ); - setPaddingFactor( txn, x ); - } - } - void paddingTooSmall( OperationContext* txn ) { - MONGO_SOMETIMES(sometimes, 4) { // do this on a sampled basis to journal less - /* the more indexes we have, the higher the cost of a move. so we take that into - account herein. note on a move that insert() calls paddingFits(), thus - here for example with no inserts and nIndexes = 1 we have - .001*4-.001 or a 3:1 ratio to non moves -> 75% nonmoves. insert heavy - can pushes this down considerably. further tweaking will be a good idea but - this should be an adequate starting point. - */ - double N = std::min(_nIndexes,7) + 3; - double x = std::min(2.0,_paddingFactor + (0.001 * N)); - setPaddingFactor( txn, x ); - } - } - - int userFlags() const { return _userFlags; } - bool isUserFlagSet( int flag ) const { return _userFlags & flag; } - - /** - * these methods only modify NamespaceDetails and do not - * sync changes back to system.namespaces - * a typical call might - if ( nsd->setUserFlag( 4 ) ) { - nsd->syncUserFlags(); - } - * these methods all return true iff only something was modified - */ - bool setUserFlag( OperationContext* txn, int flag ); - bool clearUserFlag( OperationContext* txn, int flag ); - bool replaceUserFlags( OperationContext* txn, int flags ); - NamespaceDetails *writingWithoutExtra( OperationContext* txn ); /** Make all linked Extra objects writeable as well */ @@ -316,8 +213,6 @@ namespace mongo { private: - void _removeIndexFromMe( OperationContext* txn, int idx ); - /** * swaps all meta data for 2 indexes * a and b are 2 index ids, whose contents will be swapped @@ -334,7 +229,7 @@ namespace mongo { BOOST_STATIC_ASSERT( NIndexesMax <= 64 ); // multiKey bits BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) == 496 ); }; // NamespaceDetails - BOOST_STATIC_ASSERT( sizeof(NamespaceDetails) == 496 ); + BOOST_STATIC_ASSERT( sizeof(NamespaceDetails) == 496 ); // XXX #pragma pack() } // namespace mongo diff --git a/src/mongo/db/structure/catalog/namespace_details_collection_entry.cpp b/src/mongo/db/structure/catalog/namespace_details_collection_entry.cpp new file mode 100644 index 00000000000..32c78a19100 --- /dev/null +++ b/src/mongo/db/structure/catalog/namespace_details_collection_entry.cpp @@ -0,0 +1,325 @@ +// namespace_details_collection_entry.h + +/** +* Copyright (C) 2014 MongoDB 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/>. +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects for +* all of the code used other than as permitted herein. If you modify file(s) +* with this exception, you may extend this exception to your version of the +* file(s), but you are not obligated to do so. If you do not wish to do so, +* delete this exception statement from your version. If you delete this +* exception statement from all source files in the program, then also delete +* it in the license file. +*/ + +#include "mongo/db/structure/catalog/namespace_details_collection_entry.h" + +#include "mongo/db/index/index_descriptor.h" +#include "mongo/db/storage/mmap_v1/mmap_v1_engine.h" +#include "mongo/db/structure/catalog/namespace_details.h" +#include "mongo/db/structure/record_store.h" +#include "mongo/util/startup_test.h" + +namespace mongo { + NamespaceDetailsCollectionCatalogEntry::NamespaceDetailsCollectionCatalogEntry( const StringData& ns, + NamespaceDetails* details, + RecordStore* indexRecordStore, + MMAP1DatabaseCatalogEntry* db ) + : CollectionCatalogEntry( ns ), + _details( details ), + _indexRecordStore( indexRecordStore ), + _db( db ) { + } + + int NamespaceDetailsCollectionCatalogEntry::getTotalIndexCount() const { + return _details->nIndexes + _details->indexBuildsInProgress; + } + + int NamespaceDetailsCollectionCatalogEntry::getCompletedIndexCount() const { + return _details->nIndexes; + } + + int NamespaceDetailsCollectionCatalogEntry::getMaxAllowedIndexes() const { + return NamespaceDetails::NIndexesMax; + } + + void NamespaceDetailsCollectionCatalogEntry::getAllIndexes( std::vector<std::string>* names ) const { + NamespaceDetails::IndexIterator i = _details->ii( true ); + while ( i.more() ) { + const IndexDetails& id = i.next(); + const BSONObj obj( _indexRecordStore->recordFor( id.info )->data() ); + names->push_back( obj.getStringField("name") ); + } + } + + bool NamespaceDetailsCollectionCatalogEntry::isIndexMultikey(const StringData& idxName) const { + int idxNo = _findIndexNumber( idxName ); + invariant( idxNo >= 0 ); + return isIndexMultikey( idxNo ); + } + + bool NamespaceDetailsCollectionCatalogEntry::isIndexMultikey(int idxNo) const { + return (_details->multiKeyIndexBits & (((unsigned long long) 1) << idxNo)) != 0; + } + + bool NamespaceDetailsCollectionCatalogEntry::setIndexIsMultikey(OperationContext* txn, + const StringData& indexName, + bool multikey ) { + + int idxNo = _findIndexNumber( indexName ); + invariant( idxNo >= 0 ); + return setIndexIsMultikey( txn, idxNo, multikey ); + } + + bool NamespaceDetailsCollectionCatalogEntry::setIndexIsMultikey(OperationContext* txn, + int idxNo, + bool multikey ) { + unsigned long long mask = 1ULL << idxNo; + + if (multikey) { + // Shortcut if the bit is already set correctly + if (_details->multiKeyIndexBits & mask) { + return false; + } + + *txn->recoveryUnit()->writing(&_details->multiKeyIndexBits) |= mask; + } + else { + // Shortcut if the bit is already set correctly + if (!(_details->multiKeyIndexBits & mask)) { + return false; + } + + // Invert mask: all 1's except a 0 at the ith bit + mask = ~mask; + *txn->recoveryUnit()->writing(&_details->multiKeyIndexBits) &= mask; + } + + return true; + } + + DiskLoc NamespaceDetailsCollectionCatalogEntry::getIndexHead( const StringData& idxName ) const { + int idxNo = _findIndexNumber( idxName ); + invariant( idxNo >= 0 ); + return _details->idx( idxNo ).head; + } + + BSONObj NamespaceDetailsCollectionCatalogEntry::getIndexSpec( const StringData& idxName ) const { + int idxNo = _findIndexNumber( idxName ); + invariant( idxNo >= 0 ); + const IndexDetails& id = _details->idx( idxNo ); + return BSONObj( _indexRecordStore->recordFor( id.info )->data() ); + } + + void NamespaceDetailsCollectionCatalogEntry::setIndexHead( OperationContext* txn, + const StringData& idxName, + const DiskLoc& newHead ) { + int idxNo = _findIndexNumber( idxName ); + invariant( idxNo >= 0 ); + *txn->recoveryUnit()->writing( &_details->idx( idxNo ).head) = newHead; + } + + bool NamespaceDetailsCollectionCatalogEntry::isIndexReady( const StringData& idxName ) const { + int idxNo = _findIndexNumber( idxName ); + invariant( idxNo >= 0 ); + return idxNo < getCompletedIndexCount(); + } + + int NamespaceDetailsCollectionCatalogEntry::_findIndexNumber( const StringData& idxName ) const { + NamespaceDetails::IndexIterator i = _details->ii( true ); + while ( i.more() ) { + const IndexDetails& id = i.next(); + int idxNo = i.pos() - 1; + const BSONObj obj( _indexRecordStore->recordFor( id.info )->data() ); + if ( idxName == obj.getStringField("name") ) + return idxNo; + } + return -1; + } + + /* remove bit from a bit array - actually remove its slot, not a clear + note: this function does not work with x == 63 -- that is ok + but keep in mind in the future if max indexes were extended to + exactly 64 it would be a problem + */ + unsigned long long removeAndSlideBit(unsigned long long b, int x) { + unsigned long long tmp = b; + return + (tmp & ((((unsigned long long) 1) << x)-1)) | + ((tmp >> (x+1)) << x); + } + + class IndexUpdateTest : public StartupTest { + public: + void run() { + verify( removeAndSlideBit(1, 0) == 0 ); + verify( removeAndSlideBit(2, 0) == 1 ); + verify( removeAndSlideBit(2, 1) == 0 ); + verify( removeAndSlideBit(255, 1) == 127 ); + verify( removeAndSlideBit(21, 2) == 9 ); + verify( removeAndSlideBit(0x4000000000000001ULL, 62) == 1 ); + } + } iu_unittest; + + Status NamespaceDetailsCollectionCatalogEntry::removeIndex( OperationContext* txn, + const StringData& indexName ) { + int idxNo = _findIndexNumber( indexName ); + invariant( idxNo >= 0 ); + + DiskLoc infoLocation = _details->idx( idxNo ).info; + + // all info in the .ns file + { + NamespaceDetails* d = _details->writingWithExtra( txn ); + + // fix the _multiKeyIndexBits, by moving all bits above me down one + d->multiKeyIndexBits = removeAndSlideBit(d->multiKeyIndexBits, idxNo); + + if ( idxNo >= d->nIndexes ) + d->indexBuildsInProgress--; + else + d->nIndexes--; + + for ( int i = idxNo; i < getTotalIndexCount(); i++ ) + d->idx(i) = d->idx(i+1); + + d->idx( getTotalIndexCount() ) = IndexDetails(); + } + + // remove from system.indexes + + { // sanity check + Record* record = _indexRecordStore->recordFor( infoLocation ); + invariant( record ); + BSONObj info( record->data() ); + invariant( info["name"].String() == indexName ); + } + + _indexRecordStore->deleteRecord( txn, infoLocation ); + + return Status::OK(); + } + + Status NamespaceDetailsCollectionCatalogEntry::prepareForIndexBuild( OperationContext* txn, + const IndexDescriptor* desc ) { + BSONObj spec = desc->infoObj(); + // 1) entry in system.indexs + StatusWith<DiskLoc> systemIndexesEntry = _indexRecordStore->insertRecord( txn, + spec.objdata(), + spec.objsize(), + -1 ); + if ( !systemIndexesEntry.isOK() ) + return systemIndexesEntry.getStatus(); + + // 2) NamespaceDetails mods + IndexDetails *id; + try { + id = &_details->idx(getTotalIndexCount(), true); + } + catch( DBException& ) { + _details->allocExtra(txn, + ns().ns(), + _db->namespaceIndex(), + getTotalIndexCount()); + id = &_details->idx(getTotalIndexCount(), false); + } + + *txn->recoveryUnit()->writing( &id->info ) = systemIndexesEntry.getValue(); + *txn->recoveryUnit()->writing( &id->head ) = DiskLoc(); + + txn->recoveryUnit()->writingInt( _details->indexBuildsInProgress ) += 1; + + // 3) indexes entry in .ns file + NamespaceIndex& nsi = _db->namespaceIndex(); + invariant( nsi.details( desc->indexNamespace() ) == NULL ); + nsi.add_ns( txn, desc->indexNamespace(), DiskLoc(), false ); + + // 4) system.namespaces entry index ns + _db->_addNamespaceToNamespaceCollection( txn, desc->indexNamespace(), NULL); + + return Status::OK(); + } + + void NamespaceDetailsCollectionCatalogEntry::indexBuildSuccess( OperationContext* txn, + const StringData& indexName ) { + int idxNo = _findIndexNumber( indexName ); + fassert( 17202, idxNo >= 0 ); + + // Make sure the newly created index is relocated to nIndexes, if it isn't already there + if ( idxNo != getCompletedIndexCount() ) { + int toIdxNo = getCompletedIndexCount(); + + //_details->swapIndex( txn, idxNo, toIdxNo ); + + // flip main meta data + IndexDetails temp = _details->idx(idxNo); + *txn->recoveryUnit()->writing(&_details->idx(idxNo)) = _details->idx(toIdxNo); + *txn->recoveryUnit()->writing(&_details->idx(toIdxNo)) = temp; + + // flip multi key bits + bool tempMultikey = isIndexMultikey(idxNo); + setIndexIsMultikey( txn, idxNo, isIndexMultikey(toIdxNo) ); + setIndexIsMultikey( txn, toIdxNo, tempMultikey ); + + idxNo = toIdxNo; + invariant( idxNo = _findIndexNumber( indexName ) ); + } + + txn->recoveryUnit()->writingInt( _details->indexBuildsInProgress ) -= 1; + txn->recoveryUnit()->writingInt( _details->nIndexes ) += 1; + + invariant( isIndexReady( indexName ) ); + } + + void NamespaceDetailsCollectionCatalogEntry::updateTTLSetting( OperationContext* txn, + const StringData& idxName, + long long newExpireSeconds ) { + int idx = _findIndexNumber( idxName ); + invariant( idx >= 0 ); + + IndexDetails& indexDetails = _details->idx( idx ); + + Record* record = _indexRecordStore->recordFor( indexDetails.info ); + BSONObj obj( record->data() ); + const BSONElement oldExpireSecs = obj.getField("expireAfterSeconds"); + + // Important that we set the new value in-place. We are writing directly to the + // object here so must be careful not to overwrite with a longer numeric type. + + char* nonConstPtr = const_cast<char*>(oldExpireSecs.value()); + switch( oldExpireSecs.type() ) { + case EOO: + massert( 16631, "index does not have an 'expireAfterSeconds' field", false ); + break; + case NumberInt: + *txn->recoveryUnit()->writing(reinterpret_cast<int*>(nonConstPtr)) = newExpireSeconds; + break; + case NumberDouble: + *txn->recoveryUnit()->writing(reinterpret_cast<double*>(nonConstPtr)) = newExpireSeconds; + break; + case NumberLong: + *txn->recoveryUnit()->writing(reinterpret_cast<long long*>(nonConstPtr)) = newExpireSeconds; + break; + default: + massert( 16632, "current 'expireAfterSeconds' is not a number", false ); + } + } + + +} diff --git a/src/mongo/db/structure/catalog/namespace_details_collection_entry.h b/src/mongo/db/structure/catalog/namespace_details_collection_entry.h new file mode 100644 index 00000000000..d87d4ef2a9a --- /dev/null +++ b/src/mongo/db/structure/catalog/namespace_details_collection_entry.h @@ -0,0 +1,104 @@ +// namespace_details_collection_entry.h + +#pragma once + +/** +* Copyright (C) 2014 MongoDB 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/>. +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects for +* all of the code used other than as permitted herein. If you modify file(s) +* with this exception, you may extend this exception to your version of the +* file(s), but you are not obligated to do so. If you do not wish to do so, +* delete this exception statement from your version. If you delete this +* exception statement from all source files in the program, then also delete +* it in the license file. +*/ + +#include "mongo/base/string_data.h" +#include "mongo/bson/bsonobj.h" +#include "mongo/db/catalog/collection_catalog_entry.h" +#include "mongo/db/diskloc.h" + +namespace mongo { + + class NamespaceDetails; + + class MMAP1DatabaseCatalogEntry;; + class RecordStore; + class OperationContext; + + class NamespaceDetailsCollectionCatalogEntry : public CollectionCatalogEntry { + public: + NamespaceDetailsCollectionCatalogEntry( const StringData& ns, + NamespaceDetails* details, + RecordStore* indexRecordStore, + MMAP1DatabaseCatalogEntry* db ); + + virtual ~NamespaceDetailsCollectionCatalogEntry(){} + + virtual int getTotalIndexCount() const; + + virtual int getCompletedIndexCount() const; + + virtual int getMaxAllowedIndexes() const; + + virtual void getAllIndexes( std::vector<std::string>* names ) const; + + virtual BSONObj getIndexSpec( const StringData& idxName ) const; + + virtual bool isIndexMultikey(const StringData& indexName) const; + virtual bool isIndexMultikey(int idxNo) const; + + virtual bool setIndexIsMultikey(OperationContext* txn, + int idxNo, + bool multikey = true); + virtual bool setIndexIsMultikey(OperationContext* txn, + const StringData& indexName, + bool multikey = true); + + virtual DiskLoc getIndexHead( const StringData& indexName ) const; + + virtual void setIndexHead( OperationContext* txn, + const StringData& indexName, + const DiskLoc& newHead ); + + virtual bool isIndexReady( const StringData& indexName ) const; + + virtual Status removeIndex( OperationContext* txn, + const StringData& indexName ); + + virtual Status prepareForIndexBuild( OperationContext* txn, + const IndexDescriptor* spec ); + + virtual void indexBuildSuccess( OperationContext* txn, + const StringData& indexName ); + + virtual void updateTTLSetting( OperationContext* txn, + const StringData& idxName, + long long newExpireSeconds ); + private: + int _findIndexNumber( const StringData& indexName) const; + + NamespaceDetails* _details; + RecordStore* _indexRecordStore; + MMAP1DatabaseCatalogEntry* _db; + + friend class MMAP1DatabaseCatalogEntry; + }; +} diff --git a/src/mongo/db/structure/catalog/namespace_details_rsv1_metadata.cpp b/src/mongo/db/structure/catalog/namespace_details_rsv1_metadata.cpp new file mode 100644 index 00000000000..7d3062dcca9 --- /dev/null +++ b/src/mongo/db/structure/catalog/namespace_details_rsv1_metadata.cpp @@ -0,0 +1,225 @@ +// namespace_details_rsv1_metadata.cpp + +/** + * Copyright (C) 2014 MongoDB 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/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/db/ops/update.h" +#include "mongo/db/structure/catalog/namespace_details_rsv1_metadata.h" + +namespace mongo { + NamespaceDetailsRSV1MetaData::NamespaceDetailsRSV1MetaData( const StringData& ns, + NamespaceDetails* details, + RecordStore* namespaceRecordStore ) + : _ns( ns.toString() ), + _details( details ), + _namespaceRecordStore( namespaceRecordStore ) { + } + + const DiskLoc& NamespaceDetailsRSV1MetaData::capExtent() const { + return _details->capExtent; + } + + void NamespaceDetailsRSV1MetaData::setCapExtent( OperationContext* txn, const DiskLoc& loc ) { + *txn->recoveryUnit()->writing( &_details->capExtent ) = loc; + } + + const DiskLoc& NamespaceDetailsRSV1MetaData::capFirstNewRecord() const { + return _details->capFirstNewRecord; + } + + void NamespaceDetailsRSV1MetaData::setCapFirstNewRecord( OperationContext* txn, + const DiskLoc& loc ) { + *txn->recoveryUnit()->writing( &_details->capFirstNewRecord ) = loc; + } + + bool NamespaceDetailsRSV1MetaData::capLooped() const { + return _details->capFirstNewRecord.isValid(); + } + + long long NamespaceDetailsRSV1MetaData::dataSize() const { + return _details->stats.datasize; + } + long long NamespaceDetailsRSV1MetaData::numRecords() const { + return _details->stats.nrecords; + } + + void NamespaceDetailsRSV1MetaData::incrementStats( OperationContext* txn, + long long dataSizeIncrement, + long long numRecordsIncrement ) { + // durability todo : this could be a bit annoying / slow to record constantly + NamespaceDetails::Stats* s = txn->recoveryUnit()->writing( &_details->stats ); + s->datasize += dataSizeIncrement; + s->nrecords += numRecordsIncrement; + } + + void NamespaceDetailsRSV1MetaData::setStats( OperationContext* txn, + long long dataSize, + long long numRecords ) { + NamespaceDetails::Stats* s = txn->recoveryUnit()->writing( &_details->stats ); + s->datasize = dataSize; + s->nrecords = numRecords; + } + + const DiskLoc& NamespaceDetailsRSV1MetaData::deletedListEntry( int bucket ) const { + return _details->deletedList[ bucket ]; + } + + void NamespaceDetailsRSV1MetaData::setDeletedListEntry( OperationContext* txn, + int bucket, + const DiskLoc& loc ) { + *txn->recoveryUnit()->writing( &_details->deletedList[bucket] ) = loc; + } + + void NamespaceDetailsRSV1MetaData::orphanDeletedList( OperationContext* txn ) { + for( int i = 0; i < Buckets; i++ ) { + setDeletedListEntry( txn, i, DiskLoc() ); + } + } + + const DiskLoc& NamespaceDetailsRSV1MetaData::firstExtent() const { + return _details->firstExtent; + } + + void NamespaceDetailsRSV1MetaData::setFirstExtent( OperationContext* txn, const DiskLoc& loc ) { + *txn->recoveryUnit()->writing( &_details->firstExtent ) = loc; + } + + const DiskLoc& NamespaceDetailsRSV1MetaData::lastExtent() const { + return _details->lastExtent; + } + + void NamespaceDetailsRSV1MetaData::setLastExtent( OperationContext* txn, const DiskLoc& loc ) { + *txn->recoveryUnit()->writing( &_details->lastExtent ) = loc; + } + + bool NamespaceDetailsRSV1MetaData::isCapped() const { + return _details->isCapped; + } + + bool NamespaceDetailsRSV1MetaData::isUserFlagSet( int flag ) const { + return _details->userFlags & flag; + } + + int NamespaceDetailsRSV1MetaData::userFlags() const { + return _details->userFlags; + } + + bool NamespaceDetailsRSV1MetaData::setUserFlag( OperationContext* txn, int flag ) { + if ( ( _details->userFlags & flag ) == flag ) + return false; + + txn->recoveryUnit()->writingInt( _details->userFlags) |= flag; + _syncUserFlags( txn ); + return true; + } + + bool NamespaceDetailsRSV1MetaData::clearUserFlag( OperationContext* txn, int flag ) { + if ( ( _details->userFlags & flag ) == 0 ) + return false; + + txn->recoveryUnit()->writingInt(_details->userFlags) &= ~flag; + _syncUserFlags( txn ); + return true; + } + + bool NamespaceDetailsRSV1MetaData::replaceUserFlags( OperationContext* txn, int flags ) { + if ( _details->userFlags == flags ) + return false; + + txn->recoveryUnit()->writingInt(_details->userFlags) = flags; + _syncUserFlags( txn ); + return true; + } + + int NamespaceDetailsRSV1MetaData::lastExtentSize() const { + return _details->lastExtentSize; + } + + void NamespaceDetailsRSV1MetaData::setLastExtentSize( OperationContext* txn, int newMax ) { + if ( _details->lastExtentSize == newMax ) + return; + txn->recoveryUnit()->writingInt(_details->lastExtentSize) = newMax; + } + + long long NamespaceDetailsRSV1MetaData::maxCappedDocs() const { + invariant( _details->isCapped ); + if ( _details->maxDocsInCapped == 0x7fffffff ) + return numeric_limits<long long>::max(); + return _details->maxDocsInCapped; + } + + double NamespaceDetailsRSV1MetaData::paddingFactor() const { + return _details->paddingFactor; + } + + void NamespaceDetailsRSV1MetaData::setPaddingFactor( OperationContext* txn, double paddingFactor ) { + if ( paddingFactor == _details->paddingFactor ) + return; + + if ( _details->isCapped ) + return; + + *txn->recoveryUnit()->writing(&_details->paddingFactor) = paddingFactor; + } + + void NamespaceDetailsRSV1MetaData::_syncUserFlags( OperationContext* txn ) { + if ( !_namespaceRecordStore ) + return; + + scoped_ptr<RecordIterator> iterator( _namespaceRecordStore->getIterator( DiskLoc(), + false, + CollectionScanParams::FORWARD ) ); + while ( !iterator->isEOF() ) { + DiskLoc loc = iterator->getNext(); + const Record* rec = iterator->recordFor( loc ); + + BSONObj oldEntry( rec->data() ); + BSONElement e = oldEntry["name"]; + if ( e.type() != String ) + continue; + + if ( e.String() != _ns ) + continue; + + BSONObj newEntry = applyUpdateOperators( oldEntry, + BSON( "$set" << BSON( "options.flags" << userFlags() ) ) ); + + StatusWith<DiskLoc> result = _namespaceRecordStore->updateRecord( txn, + loc, + newEntry.objdata(), + newEntry.objsize(), + -1, + NULL ); + fassert( 17486, result.isOK() ); + return; + } + + fassertFailed( 17488 ); + } + +} diff --git a/src/mongo/db/structure/catalog/namespace_details_rsv1_metadata.h b/src/mongo/db/structure/catalog/namespace_details_rsv1_metadata.h index 90015c86cb3..d4460de8bf2 100644 --- a/src/mongo/db/structure/catalog/namespace_details_rsv1_metadata.h +++ b/src/mongo/db/structure/catalog/namespace_details_rsv1_metadata.h @@ -30,120 +30,82 @@ #pragma once +#include <string> + +#include "mongo/base/string_data.h" #include "mongo/db/structure/catalog/namespace_details.h" #include "mongo/db/structure/record_store_v1_base.h" namespace mongo { + class RecordStore; + /* * NOTE: NamespaceDetails will become a struct * all dur, etc... will move here */ class NamespaceDetailsRSV1MetaData : public RecordStoreV1MetaData { public: - explicit NamespaceDetailsRSV1MetaData( NamespaceDetails* details ) { - _details = details; - } + explicit NamespaceDetailsRSV1MetaData( const StringData& ns, + NamespaceDetails* details, + RecordStore* namespaceRecordStore ); virtual ~NamespaceDetailsRSV1MetaData(){} - virtual const DiskLoc& capExtent() const { - return _details->capExtent(); - } - - virtual void setCapExtent( OperationContext* txn, const DiskLoc& loc ) { - _details->setCapExtent( txn, loc ); - } + virtual const DiskLoc& capExtent() const; + virtual void setCapExtent( OperationContext* txn, const DiskLoc& loc ); - virtual const DiskLoc& capFirstNewRecord() const { - return _details->capFirstNewRecord(); - } + virtual const DiskLoc& capFirstNewRecord() const; + virtual void setCapFirstNewRecord( OperationContext* txn, const DiskLoc& loc ); - virtual void setCapFirstNewRecord( OperationContext* txn, const DiskLoc& loc ) { - _details->setCapFirstNewRecord( txn, loc ); - } + virtual bool capLooped() const; - virtual long long dataSize() const { - return _details->dataSize(); - } - virtual long long numRecords() const { - return _details->numRecords(); - } + virtual long long dataSize() const; + virtual long long numRecords() const; virtual void incrementStats( OperationContext* txn, long long dataSizeIncrement, - long long numRecordsIncrement ) { - _details->incrementStats( txn, dataSizeIncrement, numRecordsIncrement ); - } + long long numRecordsIncrement ); virtual void setStats( OperationContext* txn, - long long dataSizeIncrement, - long long numRecordsIncrement ) { - _details->setStats( txn, - dataSizeIncrement, - numRecordsIncrement ); - } - - virtual const DiskLoc& deletedListEntry( int bucket ) const { - return _details->deletedListEntry( bucket ); - } + long long dataSize, + long long numRecords ); + virtual const DiskLoc& deletedListEntry( int bucket ) const; virtual void setDeletedListEntry( OperationContext* txn, int bucket, - const DiskLoc& loc ) { - _details->setDeletedListEntry( txn, bucket, loc ); - } - - virtual void orphanDeletedList(OperationContext* txn) { - _details->orphanDeletedList( txn ); - } + const DiskLoc& loc ); + virtual void orphanDeletedList(OperationContext* txn); - virtual const DiskLoc& firstExtent() const { - return _details->firstExtent(); - } + virtual const DiskLoc& firstExtent() const; + virtual void setFirstExtent( OperationContext* txn, const DiskLoc& loc ); - virtual void setFirstExtent( OperationContext* txn, const DiskLoc& loc ) { - _details->setFirstExtent( txn, loc ); - } + virtual const DiskLoc& lastExtent() const; + virtual void setLastExtent( OperationContext* txn, const DiskLoc& loc ); - virtual const DiskLoc& lastExtent() const { - return _details->lastExtent(); - } + virtual bool isCapped() const; - virtual void setLastExtent( OperationContext* txn, const DiskLoc& loc ) { - _details->setLastExtent( txn, loc ); - } + virtual bool isUserFlagSet( int flag ) const; + virtual int userFlags() const; + virtual bool setUserFlag( OperationContext* txn, int flag ); + virtual bool clearUserFlag( OperationContext* txn, int flag ); + virtual bool replaceUserFlags( OperationContext* txn, int flags ); - virtual bool isCapped() const { - return _details->isCapped(); - } + virtual int lastExtentSize() const; + virtual void setLastExtentSize( OperationContext* txn, int newMax ); - virtual bool isUserFlagSet( int flag ) const { - return _details->isUserFlagSet( flag ); - } + virtual long long maxCappedDocs() const; - virtual int lastExtentSize() const { - return _details->lastExtentSize(); - } + virtual double paddingFactor() const; + virtual void setPaddingFactor( OperationContext* txn, double paddingFactor ); - virtual void setLastExtentSize( OperationContext* txn, int newMax ) { - _details->setLastExtentSize( txn, newMax ); - } - - virtual long long maxCappedDocs() const { - return _details->maxCappedDocs(); - } - - virtual double paddingFactor() const { - return _details->paddingFactor(); - } + private: - virtual void setPaddingFactor( OperationContext* txn, double paddingFactor ) { - _details->setPaddingFactor( txn, paddingFactor ); - } + void _syncUserFlags( OperationContext* txn ); - private: + std::string _ns; NamespaceDetails* _details; + RecordStore* _namespaceRecordStore; }; } diff --git a/src/mongo/db/structure/catalog/namespace_index.cpp b/src/mongo/db/structure/catalog/namespace_index.cpp index 3bb52d27d6a..04eb462c06b 100644 --- a/src/mongo/db/structure/catalog/namespace_index.cpp +++ b/src/mongo/db/structure/catalog/namespace_index.cpp @@ -94,8 +94,8 @@ namespace mongo { } } - bool NamespaceIndex::exists() const { - return !boost::filesystem::exists(path()); + bool NamespaceIndex::pathExists() const { + return boost::filesystem::exists(path()); } boost::filesystem::path NamespaceIndex::path() const { diff --git a/src/mongo/db/structure/catalog/namespace_index.h b/src/mongo/db/structure/catalog/namespace_index.h index f5871ba8e0c..8564887ff29 100644 --- a/src/mongo/db/structure/catalog/namespace_index.h +++ b/src/mongo/db/structure/catalog/namespace_index.h @@ -33,6 +33,7 @@ #include <list> #include <string> +#include "mongo/base/disallow_copying.h" #include "mongo/db/diskloc.h" #include "mongo/db/structure/catalog/hashtab.h" #include "mongo/db/structure/catalog/namespace.h" @@ -46,12 +47,13 @@ namespace mongo { if you will: at least the core parts. (Additional info in system.* collections.) */ class NamespaceIndex { + MONGO_DISALLOW_COPYING(NamespaceIndex); public: NamespaceIndex(const std::string &dir, const std::string &database) : _ht( 0 ), _dir( dir ), _database( database ) {} - /* returns true if new db will be created if we init lazily */ - bool exists() const; + /* returns true if the file represented by this file exists on disk */ + bool pathExists() const; void init( OperationContext* txn ) { if ( !_ht.get() ) @@ -84,7 +86,7 @@ namespace mongo { void maybeMkdir() const; DurableMappedFile _f; - std::auto_ptr<HashTable<Namespace,NamespaceDetails> > _ht; + scoped_ptr<HashTable<Namespace,NamespaceDetails> > _ht; std::string _dir; std::string _database; }; diff --git a/src/mongo/db/structure/record_store.h b/src/mongo/db/structure/record_store.h index 1b006c464cd..020c76b6af7 100644 --- a/src/mongo/db/structure/record_store.h +++ b/src/mongo/db/structure/record_store.h @@ -31,11 +31,13 @@ #pragma once #include "mongo/base/owned_pointer_vector.h" +#include "mongo/bson/mutable/damage_vector.h" #include "mongo/db/diskloc.h" #include "mongo/db/exec/collection_scan_common.h" namespace mongo { + class CappedDocumentDeleteCallback; class Collection; struct CompactOptions; struct CompactStats; @@ -65,6 +67,18 @@ namespace mongo { }; /** + * @see RecordStore::updateRecord + */ + class UpdateMoveNotifier { + public: + virtual ~UpdateMoveNotifier(){} + virtual Status recordStoreGoingToMove( OperationContext* txn, + const DiskLoc& oldLocation, + const char* oldBuffer, + size_t oldSize ) = 0; + }; + + /** * A RecordIterator provides an interface for walking over a RecordStore. * The details of navigating the collection's structure are below this interface. */ @@ -113,6 +127,10 @@ namespace mongo { virtual long long numRecords() const = 0; + virtual bool isCapped() const = 0; + + virtual void setCappedDeleteCallback(CappedDocumentDeleteCallback*) {invariant( false );} + /** * @param extraInfo - optional more debug info * @param level - optional, level of debug info to put in (higher is more) @@ -135,6 +153,23 @@ namespace mongo { int quotaMax ) = 0; /** + * @param notifier - this is called if the document is moved + * it is to be called after the document has been written to new + * location, before deleted from old. + * @return Status or DiskLoc, DiskLoc might be different + */ + virtual StatusWith<DiskLoc> updateRecord( OperationContext* txn, + const DiskLoc& oldLocation, + const char* data, + int len, + int quotaMax, + UpdateMoveNotifier* notifier ) = 0; + + virtual Status updateWithDamages( OperationContext* txn, + const DiskLoc& loc, + const char* damangeSource, + const mutablebson::DamageVector& damages ) = 0; + /** * returned iterator owned by caller * canonical to get all would be * getIterator( DiskLoc(), false, CollectionScanParams::FORWARD ) @@ -183,16 +218,26 @@ namespace mongo { ValidateResults* results, BSONObjBuilder* output ) const = 0; /** + * @param scaleSize - amount by which to scale size metrics + * appends any custom stats from the RecordStore or other unique stats + */ + virtual void appendCustomStats( BSONObjBuilder* result, double scale ) const = 0; + + /** * Load all data into cache. * What cache depends on implementation. * @param output (optional) - where to put detailed stats */ virtual Status touch( OperationContext* txn, BSONObjBuilder* output ) const = 0; - // TODO: this makes me sad, it shouldn't be in the interface - // do not use this anymore - virtual void increaseStorageSize( OperationContext* txn, int size, int quotaMax ) = 0; - + /** + * @return Status::OK() if option hanlded + * InvalidOptions is option not supported + * other errors indicate option supported, but error setting + */ + virtual Status setCustomOption( OperationContext* txn, + const BSONElement& option, + BSONObjBuilder* info = NULL ) = 0; protected: std::string _ns; }; diff --git a/src/mongo/db/structure/record_store_v1_base.cpp b/src/mongo/db/structure/record_store_v1_base.cpp index ff391111e9b..6fb81234071 100644 --- a/src/mongo/db/structure/record_store_v1_base.cpp +++ b/src/mongo/db/structure/record_store_v1_base.cpp @@ -231,6 +231,8 @@ namespace mongo { _details->incrementStats( txn, r->netLength(), 1 ); + _paddingFits( txn ); + return loc; } @@ -244,6 +246,18 @@ namespace mongo { "record has to be >= 4 bytes" ); } + StatusWith<DiskLoc> status = _insertRecord( txn, data, len, quotaMax ); + if ( status.isOK() ) + _paddingFits( txn ); + + return status; + } + + StatusWith<DiskLoc> RecordStoreV1Base::_insertRecord( OperationContext* txn, + const char* data, + int len, + int quotaMax ) { + int lenWHdr = getRecordAllocationSize( len + Record::HeaderSize ); fassert( 17208, lenWHdr >= ( len + Record::HeaderSize ) ); @@ -265,6 +279,70 @@ namespace mongo { return loc; } + StatusWith<DiskLoc> RecordStoreV1Base::updateRecord( OperationContext* txn, + const DiskLoc& oldLocation, + const char* data, + int dataSize, + int quotaMax, + UpdateMoveNotifier* notifier ) { + Record* oldRecord = recordFor( oldLocation ); + if ( oldRecord->netLength() >= dataSize ) { + // we fit + _paddingFits( txn ); + memcpy( txn->recoveryUnit()->writingPtr( oldRecord->data(), dataSize ), data, dataSize ); + return StatusWith<DiskLoc>( oldLocation ); + } + + if ( isCapped() ) + return StatusWith<DiskLoc>( ErrorCodes::InternalError, + "failing update: objects in a capped ns cannot grow", + 10003 ); + + // we have to move + + _paddingTooSmall( txn ); + + StatusWith<DiskLoc> newLocation = _insertRecord( txn, data, dataSize, quotaMax ); + if ( !newLocation.isOK() ) + return newLocation; + + // insert worked, so we delete old record + if ( notifier ) { + Status moveStatus = notifier->recordStoreGoingToMove( txn, + oldLocation, + oldRecord->data(), + oldRecord->netLength() ); + if ( !moveStatus.isOK() ) + return StatusWith<DiskLoc>( moveStatus ); + } + + deleteRecord( txn, oldLocation ); + + return newLocation; + } + + + Status RecordStoreV1Base::updateWithDamages( OperationContext* txn, + const DiskLoc& loc, + const char* damangeSource, + const mutablebson::DamageVector& damages ) { + _paddingFits( txn ); + + Record* rec = recordFor( loc ); + char* root = rec->data(); + + // All updates were in place. Apply them via durability and writing pointer. + mutablebson::DamageVector::const_iterator where = damages.begin(); + const mutablebson::DamageVector::const_iterator end = damages.end(); + for( ; where != end; ++where ) { + const char* sourcePtr = damangeSource + where->sourceOffset; + void* targetPtr = txn->recoveryUnit()->writingPtr(root + where->targetOffset, where->size); + std::memcpy(targetPtr, sourcePtr, where->size); + } + + return Status::OK(); + } + void RecordStoreV1Base::deleteRecord( OperationContext* txn, const DiskLoc& dl ) { Record* todelete = recordFor( dl ); @@ -688,6 +766,18 @@ namespace mongo { return Status::OK(); } + void RecordStoreV1Base::appendCustomStats( BSONObjBuilder* result, double scale ) const { + result->append( "lastExtentSize", _details->lastExtentSize() / scale ); + result->append( "paddingFactor", _details->paddingFactor() ); + result->append( "userFlags", _details->userFlags() ); + + if ( isCapped() ) { + result->appendBool( "capped", true ); + result->appendNumber( "max", _details->maxCappedDocs() ); + } + } + + namespace { struct touch_location { const char* root; @@ -815,4 +905,51 @@ namespace mongo { return MaxBucket; } + void RecordStoreV1Base::_paddingFits( OperationContext* txn ) { + MONGO_SOMETIMES(sometimes, 4) { // do this on a sampled basis to journal less + double x = max(1.0, _details->paddingFactor() - 0.001 ); + _details->setPaddingFactor( txn, x ); + } + } + + void RecordStoreV1Base::_paddingTooSmall( OperationContext* txn ) { + MONGO_SOMETIMES(sometimes, 4) { // do this on a sampled basis to journal less + /* the more indexes we have, the higher the cost of a move. so we take that into + account herein. note on a move that insert() calls paddingFits(), thus + here for example with no inserts and nIndexes = 1 we have + .001*4-.001 or a 3:1 ratio to non moves -> 75% nonmoves. insert heavy + can pushes this down considerably. further tweaking will be a good idea but + this should be an adequate starting point. + */ + double N = 4; // magic + double x = min(2.0,_details->paddingFactor() + (0.001 * N)); + _details->setPaddingFactor( txn, x ); + } + } + + Status RecordStoreV1Base::setCustomOption( OperationContext* txn, + const BSONElement& option, + BSONObjBuilder* info ) { + if ( str::equals( "usePowerOf2Sizes", option.fieldName() ) ) { + bool oldPowerOf2 = _details->isUserFlagSet( Flag_UsePowerOf2Sizes ); + bool newPowerOf2 = option.trueValue(); + + if ( oldPowerOf2 != newPowerOf2 ) { + // change userFlags + info->appendBool( "usePowerOf2Sizes_old", oldPowerOf2 ); + + if ( newPowerOf2 ) + _details->setUserFlag( txn, Flag_UsePowerOf2Sizes ); + else + _details->clearUserFlag( txn, Flag_UsePowerOf2Sizes ); + + info->appendBool( "usePowerOf2Sizes_new", newPowerOf2 ); + } + + return Status::OK(); + } + + return Status( ErrorCodes::InvalidOptions, + str::stream() << "no such option: " << option.fieldName() ); + } } diff --git a/src/mongo/db/structure/record_store_v1_base.h b/src/mongo/db/structure/record_store_v1_base.h index c83c94a4603..a3da188ba75 100644 --- a/src/mongo/db/structure/record_store_v1_base.h +++ b/src/mongo/db/structure/record_store_v1_base.h @@ -80,6 +80,10 @@ namespace mongo { virtual bool isCapped() const = 0; virtual bool isUserFlagSet( int flag ) const = 0; + virtual int userFlags() const = 0; + virtual bool setUserFlag( OperationContext* txn, int flag ) = 0; + virtual bool clearUserFlag( OperationContext* txn, int flag ) = 0; + virtual bool replaceUserFlags( OperationContext* txn, int flags ) = 0; virtual int lastExtentSize() const = 0; virtual void setLastExtentSize( OperationContext* txn, int newMax ) = 0; @@ -138,6 +142,18 @@ namespace mongo { const DocWriter* doc, int quotaMax ); + virtual StatusWith<DiskLoc> updateRecord( OperationContext* txn, + const DiskLoc& oldLocation, + const char* data, + int len, + int quotaMax, + UpdateMoveNotifier* notifier ); + + virtual Status updateWithDamages( OperationContext* txn, + const DiskLoc& loc, + const char* damangeSource, + const mutablebson::DamageVector& damages ); + virtual RecordIterator* getIteratorForRepair() const; void increaseStorageSize( OperationContext* txn, int size, int quotaMax ); @@ -147,6 +163,8 @@ namespace mongo { ValidateAdaptor* adaptor, ValidateResults* results, BSONObjBuilder* output ) const; + virtual void appendCustomStats( BSONObjBuilder* result, double scale ) const; + virtual Status touch( OperationContext* txn, BSONObjBuilder* output ) const; const DeletedRecord* deletedRecordFor( const DiskLoc& loc ) const; @@ -182,6 +200,9 @@ namespace mongo { /* return which "deleted bucket" for this size object */ static int bucket(int size); + virtual Status setCustomOption( OperationContext* txn, + const BSONElement& option, + BSONObjBuilder* info = NULL ); protected: virtual bool isCapped() const = 0; @@ -218,6 +239,18 @@ namespace mongo { */ void _addRecordToRecListInExtent(OperationContext* txn, Record* r, DiskLoc loc); + void _paddingTooSmall( OperationContext* txn ); + void _paddingFits( OperationContext* txn ); + + /** + * internal + * doesn't check inputs or change padding + */ + StatusWith<DiskLoc> _insertRecord( OperationContext* txn, + const char* data, + int len, + int quotaMax ); + scoped_ptr<RecordStoreV1MetaData> _details; ExtentManager* _extentManager; bool _isSystemIndexes; diff --git a/src/mongo/db/structure/record_store_v1_capped.h b/src/mongo/db/structure/record_store_v1_capped.h index 198a0d0275c..d3c7a1c1ee8 100644 --- a/src/mongo/db/structure/record_store_v1_capped.h +++ b/src/mongo/db/structure/record_store_v1_capped.h @@ -83,6 +83,10 @@ namespace mongo { virtual bool isCapped() const { return true; } + virtual void setCappedDeleteCallback( CappedDocumentDeleteCallback* cb ) { + _deleteCallback = cb; + } + virtual StatusWith<DiskLoc> allocRecord( OperationContext* txn, int lengthWithHeaders, int quotaMax ); diff --git a/src/mongo/db/structure/record_store_v1_test_help.cpp b/src/mongo/db/structure/record_store_v1_test_help.cpp index cc2662ef498..cebb035b87c 100644 --- a/src/mongo/db/structure/record_store_v1_test_help.cpp +++ b/src/mongo/db/structure/record_store_v1_test_help.cpp @@ -147,6 +147,30 @@ namespace mongo { return _userFlags & flag; } + bool DummyRecordStoreV1MetaData::setUserFlag( OperationContext* txn, int flag ) { + if ( ( _userFlags & flag ) == flag ) + return false; + + _userFlags |= flag; + return true; + + } + bool DummyRecordStoreV1MetaData::clearUserFlag( OperationContext* txn, int flag ) { + if ( ( _userFlags & flag ) == 0 ) + return false; + + _userFlags &= ~flag; + return true; + + } + bool DummyRecordStoreV1MetaData::replaceUserFlags( OperationContext* txn, int flags ) { + if ( _userFlags == flags ) + return false; + _userFlags = flags; + return true; + } + + int DummyRecordStoreV1MetaData::lastExtentSize() const { return _lastExtentSize; } diff --git a/src/mongo/db/structure/record_store_v1_test_help.h b/src/mongo/db/structure/record_store_v1_test_help.h index 2f33dbb5676..b4694f6acb9 100644 --- a/src/mongo/db/structure/record_store_v1_test_help.h +++ b/src/mongo/db/structure/record_store_v1_test_help.h @@ -74,6 +74,11 @@ namespace mongo { virtual bool isCapped() const; virtual bool isUserFlagSet( int flag ) const; + virtual int userFlags() const { return _userFlags; } + virtual bool setUserFlag( OperationContext* txn, int flag ); + virtual bool clearUserFlag( OperationContext* txn, int flag ); + virtual bool replaceUserFlags( OperationContext* txn, int flags ); + virtual int lastExtentSize() const; virtual void setLastExtentSize( OperationContext* txn, int newMax ); |