summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorEliot Horowitz <eliot@10gen.com>2014-04-11 14:59:12 -0400
committerEliot Horowitz <eliot@10gen.com>2014-04-14 00:23:08 -0400
commit78acc64523fbc05cd9a8c2f8980bbe51a0d957e7 (patch)
treef4662301863279c55717c2cdc13548ac9074f7c6 /src/mongo
parent12a2947ebd5ebbf5282e2d5e2c673eeae840f578 (diff)
downloadmongo-78acc64523fbc05cd9a8c2f8980bbe51a0d957e7.tar.gz
SERVER-13084: move compact details down into RecordStore layer
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/exec/oplogstart.cpp4
-rw-r--r--src/mongo/db/storage/record.h2
-rw-r--r--src/mongo/db/structure/collection_compact.cpp251
-rw-r--r--src/mongo/db/structure/record_store.h22
-rw-r--r--src/mongo/db/structure/record_store_v1_capped.cpp6
-rw-r--r--src/mongo/db/structure/record_store_v1_capped.h8
-rw-r--r--src/mongo/db/structure/record_store_v1_simple.cpp204
-rw-r--r--src/mongo/db/structure/record_store_v1_simple.h13
8 files changed, 304 insertions, 206 deletions
diff --git a/src/mongo/db/exec/oplogstart.cpp b/src/mongo/db/exec/oplogstart.cpp
index 7554548dac8..f6754d42670 100644
--- a/src/mongo/db/exec/oplogstart.cpp
+++ b/src/mongo/db/exec/oplogstart.cpp
@@ -114,7 +114,7 @@ namespace mongo {
// Set up our extent hopping state. Get the start of the extent that we were collection
// scanning.
- Extent* e = _curloc.rec()->myExtent(_curloc).ext();
+ Extent* e = _curloc.rec()->myExtentLoc(_curloc).ext();
if (!_nsd->capLooped() || (e->myLoc != _nsd->capExtent())) {
_curloc = e->firstRecord;
}
@@ -187,7 +187,7 @@ namespace mongo {
// static
DiskLoc OplogStart::prevExtentFirstLoc(const NamespaceDetails* nsd, const DiskLoc& rec ) {
- Extent *e = rec.rec()->myExtent( rec ).ext();
+ Extent *e = rec.rec()->myExtentLoc( rec ).ext();
if (nsd->capLooped() ) {
while( true ) {
// Advance e to preceding extent (looping to lastExtent if necessary).
diff --git a/src/mongo/db/storage/record.h b/src/mongo/db/storage/record.h
index 8ac063f9e7d..db6a703b0ae 100644
--- a/src/mongo/db/storage/record.h
+++ b/src/mongo/db/storage/record.h
@@ -75,7 +75,7 @@ namespace mongo {
/* use this when a record is deleted. basically a union with next/prev fields */
DeletedRecord& asDeleted() { return *((DeletedRecord*) this); }
- DiskLoc myExtent(const DiskLoc& myLoc) const { return DiskLoc(myLoc.a(), extentOfs() ); }
+ DiskLoc myExtentLoc(const DiskLoc& myLoc) const { return DiskLoc(myLoc.a(), extentOfs() ); }
struct NP {
int nextOfs;
diff --git a/src/mongo/db/structure/collection_compact.cpp b/src/mongo/db/structure/collection_compact.cpp
index a59e232cfb4..92a0002567f 100644
--- a/src/mongo/db/structure/collection_compact.cpp
+++ b/src/mongo/db/structure/collection_compact.cpp
@@ -47,198 +47,71 @@
namespace mongo {
- class CompactDocWriter : public DocWriter {
- public:
- /**
- * param allocationSize - allocation size WITH header
- */
- CompactDocWriter( const BSONObj& doc, size_t allocationSize )
- : _doc( doc ), _allocationSize( allocationSize ) {
- }
-
- virtual ~CompactDocWriter() {}
-
- virtual void writeDocument( char* buf ) const {
- memcpy( buf, _doc.objdata(), _doc.objsize() );
- }
-
- virtual size_t documentSize() const {
- return _allocationSize - Record::HeaderSize;
- }
-
- virtual bool addPadding() const {
- return false;
- }
-
- private:
- BSONObj _doc;
- size_t _allocationSize;
- };
-
- void Collection::_compactExtent(const DiskLoc diskloc, int extentNumber,
- MultiIndexBlock& indexesToInsertTo,
- const CompactOptions* compactOptions, CompactStats* stats ) {
-
- log() << "compact begin extent #" << extentNumber
- << " for namespace " << _ns << " " << diskloc;
-
- unsigned oldObjSize = 0; // we'll report what the old padding was
- unsigned oldObjSizeWithPadding = 0;
-
- Extent *e = getExtentManager()->getExtent( diskloc );
- e->assertOk();
- verify( e->validates(diskloc) );
-
- {
- // the next/prev pointers within the extent might not be in order so we first
- // page the whole thing in sequentially
- log() << "compact paging in len=" << e->length/1000000.0 << "MB" << endl;
- Timer t;
- size_t length = e->length;
-
- touch_pages( reinterpret_cast<const char*>(e), length );
- int ms = t.millis();
- if( ms > 1000 )
- log() << "compact end paging in " << ms << "ms "
- << e->length/1000000.0/t.seconds() << "MB/sec" << endl;
+ namespace {
+ BSONObj _compactAdjustIndexSpec( const BSONObj& oldSpec ) {
+ BSONObjBuilder b;
+ BSONObj::iterator i( oldSpec );
+ while( i.more() ) {
+ BSONElement e = i.next();
+ if ( str::equals( e.fieldName(), "v" ) ) {
+ // Drop any preexisting index version spec. The default index version will
+ // be used instead for the new index.
+ continue;
+ }
+ if ( str::equals( e.fieldName(), "background" ) ) {
+ // Create the new index in the foreground.
+ continue;
+ }
+ // Pass the element through to the new index spec.
+ b.append(e);
+ }
+ return b.obj();
}
- {
- log() << "compact copying records" << endl;
- long long datasize = 0;
- long long nrecords = 0;
- DiskLoc L = e->firstRecord;
- if( !L.isNull() ) {
- while( 1 ) {
- Record *recOld = _recordStore->recordFor(L);
- BSONObj objOld = docFor( L );
- L = getExtentManager()->getNextRecordInExtent(L);
-
- if ( compactOptions->validateDocuments && !objOld.valid() ) {
- // object is corrupt!
- log() << "compact skipping corrupt document!";
- stats->corruptDocuments++;
- }
- else {
- unsigned docSize = objOld.objsize();
-
- nrecords++;
- oldObjSize += docSize;
- oldObjSizeWithPadding += recOld->netLength();
-
- unsigned lenWHdr = docSize + Record::HeaderSize;
- unsigned lenWPadding = lenWHdr;
-
- switch( compactOptions->paddingMode ) {
- case CompactOptions::NONE:
- if ( details()->isUserFlagSet(NamespaceDetails::Flag_UsePowerOf2Sizes) )
- lenWPadding = details()->quantizePowerOf2AllocationSpace(lenWPadding);
- break;
- case CompactOptions::PRESERVE:
- // if we are preserving the padding, the record should not change size
- lenWPadding = recOld->lengthWithHeaders();
- break;
- case CompactOptions::MANUAL:
- lenWPadding = compactOptions->computeRecordSize(lenWPadding);
- if (lenWPadding < lenWHdr || lenWPadding > BSONObjMaxUserSize / 2 ) {
- lenWPadding = lenWHdr;
- }
- break;
- }
-
- CompactDocWriter writer( objOld, lenWPadding );
- StatusWith<DiskLoc> status = _recordStore->insertRecord( &writer, 0 );
- uassertStatusOK( status.getStatus() );
- datasize += _recordStore->recordFor( status.getValue() )->netLength();
-
- InsertDeleteOptions options;
- options.logIfError = false;
- options.dupsAllowed = true; // in compact we should be doing no checking
-
- indexesToInsertTo.insert( objOld, status.getValue(), options );
- }
-
- if( L.isNull() ) {
- // we just did the very last record from the old extent. it's still pointed to
- // by the old extent ext, but that will be fixed below after this loop
- break;
- }
+ class MyCompactAdaptor : public RecordStoreCompactAdaptor {
+ public:
+ MyCompactAdaptor( Collection* collection,
+ MultiIndexBlock* indexBlock )
+ : _collection( collection ),
+ _multiIndexBlock( indexBlock ) {
+ }
- // remove the old records (orphan them) periodically so our commit block doesn't get too large
- bool stopping = false;
- RARELY stopping = *killCurrentOp.checkForInterruptNoAssert() != 0;
- if( stopping || getDur().isCommitNeeded() ) {
- e->firstRecord.writing() = L;
- Record *r = _recordStore->recordFor(L);
- getDur().writingInt(r->prevOfs()) = DiskLoc::NullOfs;
- getDur().commitIfNeeded();
- killCurrentOp.checkForInterrupt();
- }
- }
- } // if !L.isNull()
+ virtual bool isDataValid( Record* rec ) {
+ return BSONObj( rec->data() ).valid();
+ }
- verify( details()->firstExtent() == diskloc );
- verify( details()->lastExtent() != diskloc );
- DiskLoc newFirst = e->xnext;
- details()->firstExtent().writing() = newFirst;
- getExtentManager()->getExtent( newFirst )->xprev.writing().Null();
- getDur().writing(e)->markEmpty();
- getExtentManager()->freeExtents( diskloc, diskloc );
+ virtual size_t dataSize( Record* rec ) {
+ return BSONObj( rec->data() ).objsize();
+ }
- getDur().commitIfNeeded();
+ virtual void inserted( Record* rec, const DiskLoc& newLocation ) {
+ InsertDeleteOptions options;
+ options.logIfError = false;
+ options.dupsAllowed = true; // in compact we should be doing no checking
- {
- double op = 1.0;
- if( oldObjSize )
- op = static_cast<double>(oldObjSizeWithPadding)/oldObjSize;
- log() << "compact finished extent #" << extentNumber << " containing " << nrecords
- << " documents (" << datasize/1000000.0 << "MB)"
- << " oldPadding: " << op << ' ' << static_cast<unsigned>(op*100.0)/100;
+ _multiIndexBlock->insert( BSONObj( rec->data() ), newLocation, options );
}
- }
- }
+ private:
+ Collection* _collection;
+ MultiIndexBlock* _multiIndexBlock;
+ };
- BSONObj _compactAdjustIndexSpec( const BSONObj& oldSpec ) {
- BSONObjBuilder b;
- BSONObj::iterator i( oldSpec );
- while( i.more() ) {
- BSONElement e = i.next();
- if ( str::equals( e.fieldName(), "v" ) ) {
- // Drop any preexisting index version spec. The default index version will
- // be used instead for the new index.
- continue;
- }
- if ( str::equals( e.fieldName(), "background" ) ) {
- // Create the new index in the foreground.
- continue;
- }
- // Pass the element through to the new index spec.
- b.append(e);
- }
- return b.obj();
}
+
StatusWith<CompactStats> Collection::compact( const CompactOptions* compactOptions ) {
- if ( isCapped() )
+ if ( !_recordStore->compactSupported() )
return StatusWith<CompactStats>( ErrorCodes::BadValue,
- "cannot compact capped collection" );
+ str::stream() <<
+ "cannot compact collection with record store: " <<
+ _recordStore->name() );
if ( _indexCatalog.numIndexesInProgress() )
return StatusWith<CompactStats>( ErrorCodes::BadValue,
"cannot compact when indexes in progress" );
- // this is a big job, so might as well make things tidy before we start just to be nice.
- getDur().commitIfNeeded();
-
- list<DiskLoc> extents;
- for( DiskLoc extLocation = _details->firstExtent();
- !extLocation.isNull();
- extLocation = getExtentManager()->getExtent( extLocation )->xnext ) {
- extents.push_back( extLocation );
- }
- log() << "compact " << extents.size() << " extents";
// same data, but might perform a little different after compact?
_infoCache.reset();
@@ -263,17 +136,6 @@ namespace mongo {
}
}
- log() << "compact orphan deleted lists" << endl;
- _details->orphanDeletedList();
-
- // Start over from scratch with our extent sizing and growth
- _details->setLastExtentSize( 0 );
-
- // before dropping indexes, at least make sure we can allocate one extent!
- // this will allocate an extent and add to free list
- // if it cannot, it will throw an exception
- increaseStorageSize( _details->lastExtentSize(), true );
-
// note that the drop indexes call also invalidates all clientcursors for the namespace,
// which is important and wanted here
log() << "compact dropping indexes" << endl;
@@ -282,7 +144,6 @@ namespace mongo {
return StatusWith<CompactStats>( status );
}
- getDur().commitIfNeeded();
killCurrentOp.checkForInterrupt();
CompactStats stats;
@@ -292,27 +153,11 @@ namespace mongo {
if ( !status.isOK() )
return StatusWith<CompactStats>( status );
- // reset data size and record counts to 0 for this namespace
- // as we're about to tally them up again for each new extent
- _details->setStats( 0, 0 );
-
- ProgressMeterHolder pm(cc().curop()->setMessage("compact extent",
- "Extent Compacting Progress",
- extents.size()));
-
- int extentNumber = 0;
- for( list<DiskLoc>::iterator i = extents.begin(); i != extents.end(); i++ ) {
- _compactExtent(*i, extentNumber++, multiIndexBlock, compactOptions, &stats );
- pm.hit();
- }
-
- invariant( getExtentManager()->getExtent( _details->firstExtent() )->xprev.isNull() );
+ MyCompactAdaptor adaptor( this, &multiIndexBlock );
- // indexes will do their own progress meter
- pm.finished();
+ _recordStore->compact( &adaptor, compactOptions, &stats );
log() << "starting index commits";
-
status = multiIndexBlock.commit();
if ( !status.isOK() )
return StatusWith<CompactStats>( status );
diff --git a/src/mongo/db/structure/record_store.h b/src/mongo/db/structure/record_store.h
index e1ba247c581..2712bbc80d5 100644
--- a/src/mongo/db/structure/record_store.h
+++ b/src/mongo/db/structure/record_store.h
@@ -37,11 +37,15 @@
namespace mongo {
class Collection;
+ struct CompactOptions;
+ struct CompactStats;
class DocWriter;
class ExtentManager;
class MAdvise;
class NamespaceDetails;
class Record;
+
+ class RecordStoreCompactAdaptor;
class RecordStore;
/**
@@ -84,6 +88,11 @@ namespace mongo {
RecordStore( const StringData& ns );
virtual ~RecordStore();
+ // META
+
+ // name of the RecordStore implementation
+ virtual const char* name() const = 0;
+
// CRUD related
virtual Record* recordFor( const DiskLoc& loc ) const = 0;
@@ -110,6 +119,12 @@ namespace mongo {
*/
virtual Status truncate() = 0;
+ // does this RecordStore support the compact operation
+ virtual bool compactSupported() const = 0;
+ virtual Status compact( RecordStoreCompactAdaptor* adaptor,
+ const CompactOptions* options,
+ CompactStats* stats ) = 0;
+
// TODO: this makes me sad, it shouldn't be in the interface
// do not use this anymore
virtual void increaseStorageSize( int size, int quotaMax ) = 0;
@@ -121,4 +136,11 @@ namespace mongo {
std::string _ns;
};
+ class RecordStoreCompactAdaptor {
+ public:
+ virtual ~RecordStoreCompactAdaptor(){}
+ virtual bool isDataValid( Record* rec ) = 0;
+ virtual size_t dataSize( Record* rec ) = 0;
+ virtual void inserted( Record* rec, const DiskLoc& newLocation ) = 0;
+ };
}
diff --git a/src/mongo/db/structure/record_store_v1_capped.cpp b/src/mongo/db/structure/record_store_v1_capped.cpp
index 5cd9e9cf240..728c388315d 100644
--- a/src/mongo/db/structure/record_store_v1_capped.cpp
+++ b/src/mongo/db/structure/record_store_v1_capped.cpp
@@ -583,4 +583,10 @@ namespace mongo {
return new CappedRecordStoreV1Iterator( this, start, tailable, dir );
}
+ Status CappedRecordStoreV1::compact( RecordStoreCompactAdaptor* adaptor,
+ const CompactOptions* options,
+ CompactStats* stats ) {
+ invariant(false);
+ }
+
}
diff --git a/src/mongo/db/structure/record_store_v1_capped.h b/src/mongo/db/structure/record_store_v1_capped.h
index 961ed2aa584..77dabd10a5d 100644
--- a/src/mongo/db/structure/record_store_v1_capped.h
+++ b/src/mongo/db/structure/record_store_v1_capped.h
@@ -49,6 +49,8 @@ namespace mongo {
virtual ~CappedRecordStoreV1();
+ const char* name() const { return "CappedRecordStoreV1"; }
+
virtual Status truncate();
/**
@@ -63,6 +65,12 @@ namespace mongo {
virtual RecordIterator* getIterator( const DiskLoc& start, bool tailable,
const CollectionScanParams::Direction& dir) const;
+ virtual bool compactSupported() const { return false; }
+
+ virtual Status compact( RecordStoreCompactAdaptor* adaptor,
+ const CompactOptions* options,
+ CompactStats* stats );
+
protected:
virtual StatusWith<DiskLoc> allocRecord( int lengthWithHeaders, int quotaMax );
diff --git a/src/mongo/db/structure/record_store_v1_simple.cpp b/src/mongo/db/structure/record_store_v1_simple.cpp
index c094ef46b60..227b7c5c51e 100644
--- a/src/mongo/db/structure/record_store_v1_simple.cpp
+++ b/src/mongo/db/structure/record_store_v1_simple.cpp
@@ -32,14 +32,19 @@
#include "mongo/base/counter.h"
#include "mongo/db/catalog/collection.h"
+#include "mongo/db/client.h"
#include "mongo/db/commands/server_status.h"
+#include "mongo/db/curop.h"
#include "mongo/db/dur.h"
+#include "mongo/db/kill_current_op.h"
#include "mongo/db/storage/extent.h"
#include "mongo/db/storage/extent_manager.h"
#include "mongo/db/storage/record.h"
#include "mongo/db/structure/catalog/namespace_details.h"
#include "mongo/db/structure/record_store_v1_simple_iterator.h"
#include "mongo/util/mmap.h"
+#include "mongo/util/progress_meter.h"
+#include "mongo/util/touch_pages.h"
namespace mongo {
@@ -267,4 +272,203 @@ namespace mongo {
return new SimpleRecordStoreV1Iterator( this, start, dir );
}
+ class CompactDocWriter : public DocWriter {
+ public:
+ /**
+ * param allocationSize - allocation size WITH header
+ */
+ CompactDocWriter( const Record* rec, unsigned dataSize, size_t allocationSize )
+ : _rec( rec ),
+ _dataSize( dataSize ),
+ _allocationSize( allocationSize ) {
+ }
+
+ virtual ~CompactDocWriter() {}
+
+ virtual void writeDocument( char* buf ) const {
+ memcpy( buf, _rec->data(), _dataSize );
+ }
+
+ virtual size_t documentSize() const {
+ return _allocationSize - Record::HeaderSize;
+ }
+
+ virtual bool addPadding() const {
+ return false;
+ }
+
+ private:
+ const Record* _rec;
+ size_t _dataSize;
+ size_t _allocationSize;
+ };
+
+ void SimpleRecordStoreV1::_compactExtent(const DiskLoc diskloc, int extentNumber,
+ RecordStoreCompactAdaptor* adaptor,
+ const CompactOptions* compactOptions,
+ CompactStats* stats ) {
+
+ log() << "compact begin extent #" << extentNumber
+ << " for namespace " << _ns << " " << diskloc;
+
+ unsigned oldObjSize = 0; // we'll report what the old padding was
+ unsigned oldObjSizeWithPadding = 0;
+
+ Extent *e = _extentManager->getExtent( diskloc );
+ e->assertOk();
+ fassert( 17437, e->validates(diskloc) );
+
+ {
+ // the next/prev pointers within the extent might not be in order so we first
+ // page the whole thing in sequentially
+ log() << "compact paging in len=" << e->length/1000000.0 << "MB" << endl;
+ Timer t;
+ size_t length = e->length;
+
+ touch_pages( reinterpret_cast<const char*>(e), length );
+ int ms = t.millis();
+ if( ms > 1000 )
+ log() << "compact end paging in " << ms << "ms "
+ << e->length/1000000.0/t.seconds() << "MB/sec" << endl;
+ }
+
+ {
+ log() << "compact copying records" << endl;
+ long long datasize = 0;
+ long long nrecords = 0;
+ DiskLoc L = e->firstRecord;
+ if( !L.isNull() ) {
+ while( 1 ) {
+ Record *recOld = recordFor(L);
+ L = _extentManager->getNextRecordInExtent(L);
+
+ if ( compactOptions->validateDocuments && !adaptor->isDataValid(recOld) ) {
+ // object is corrupt!
+ log() << "compact skipping corrupt document!";
+ stats->corruptDocuments++;
+ }
+ else {
+ unsigned dataSize = adaptor->dataSize( recOld );
+ unsigned docSize = dataSize;
+
+ nrecords++;
+ oldObjSize += docSize;
+ oldObjSizeWithPadding += recOld->netLength();
+
+ unsigned lenWHdr = docSize + Record::HeaderSize;
+ unsigned lenWPadding = lenWHdr;
+
+ switch( compactOptions->paddingMode ) {
+ case CompactOptions::NONE:
+ if ( _details->isUserFlagSet(NamespaceDetails::Flag_UsePowerOf2Sizes) )
+ lenWPadding = _details->quantizePowerOf2AllocationSpace(lenWPadding);
+ break;
+ case CompactOptions::PRESERVE:
+ // if we are preserving the padding, the record should not change size
+ lenWPadding = recOld->lengthWithHeaders();
+ break;
+ case CompactOptions::MANUAL:
+ lenWPadding = compactOptions->computeRecordSize(lenWPadding);
+ if (lenWPadding < lenWHdr || lenWPadding > BSONObjMaxUserSize / 2 ) {
+ lenWPadding = lenWHdr;
+ }
+ break;
+ }
+
+ CompactDocWriter writer( recOld, dataSize, lenWPadding );
+ StatusWith<DiskLoc> status = insertRecord( &writer, 0 );
+ uassertStatusOK( status.getStatus() );
+ datasize += recordFor( status.getValue() )->netLength();
+
+ adaptor->inserted( recordFor( status.getValue() ), status.getValue() );
+ }
+
+ if( L.isNull() ) {
+ // we just did the very last record from the old extent. it's still pointed to
+ // by the old extent ext, but that will be fixed below after this loop
+ break;
+ }
+
+ // remove the old records (orphan them) periodically so our commit block doesn't get too large
+ bool stopping = false;
+ RARELY stopping = *killCurrentOp.checkForInterruptNoAssert() != 0;
+ if( stopping || getDur().isCommitNeeded() ) {
+ e->firstRecord.writing() = L;
+ Record *r = recordFor(L);
+ getDur().writingInt(r->prevOfs()) = DiskLoc::NullOfs;
+ getDur().commitIfNeeded();
+ killCurrentOp.checkForInterrupt();
+ }
+ }
+ } // if !L.isNull()
+
+ invariant( _details->firstExtent() == diskloc );
+ invariant( _details->lastExtent() != diskloc );
+ DiskLoc newFirst = e->xnext;
+ _details->firstExtent().writing() = newFirst;
+ _extentManager->getExtent( newFirst )->xprev.writing().Null();
+ getDur().writing(e)->markEmpty();
+ _extentManager->freeExtents( diskloc, diskloc );
+
+ getDur().commitIfNeeded();
+
+ {
+ double op = 1.0;
+ if( oldObjSize )
+ op = static_cast<double>(oldObjSizeWithPadding)/oldObjSize;
+ log() << "compact finished extent #" << extentNumber << " containing " << nrecords
+ << " documents (" << datasize/1000000.0 << "MB)"
+ << " oldPadding: " << op << ' ' << static_cast<unsigned>(op*100.0)/100;
+ }
+ }
+
+ }
+
+ Status SimpleRecordStoreV1::compact( RecordStoreCompactAdaptor* adaptor,
+ const CompactOptions* options,
+ CompactStats* stats ) {
+
+ // this is a big job, so might as well make things tidy before we start just to be nice.
+ getDur().commitIfNeeded();
+
+ list<DiskLoc> extents;
+ for( DiskLoc extLocation = _details->firstExtent();
+ !extLocation.isNull();
+ extLocation = _extentManager->getExtent( extLocation )->xnext ) {
+ extents.push_back( extLocation );
+ }
+ log() << "compact " << extents.size() << " extents";
+
+ log() << "compact orphan deleted lists" << endl;
+ _details->orphanDeletedList();
+
+ // Start over from scratch with our extent sizing and growth
+ _details->setLastExtentSize( 0 );
+
+ // create a new extent so new records go there
+ increaseStorageSize( _details->lastExtentSize(), true );
+
+ // reset data size and record counts to 0 for this namespace
+ // as we're about to tally them up again for each new extent
+ _details->setStats( 0, 0 );
+
+ ProgressMeterHolder pm(cc().curop()->setMessage("compact extent",
+ "Extent Compacting Progress",
+ extents.size()));
+
+ int extentNumber = 0;
+ for( list<DiskLoc>::iterator i = extents.begin(); i != extents.end(); i++ ) {
+ _compactExtent(*i, extentNumber++, adaptor, options, stats );
+ pm.hit();
+ }
+
+ invariant( _extentManager->getExtent( _details->firstExtent() )->xprev.isNull() );
+ invariant( _extentManager->getExtent( _details->lastExtent() )->xnext.isNull() );
+
+ // indexes will do their own progress meter
+ pm.finished();
+
+ return Status::OK();
+ }
+
}
diff --git a/src/mongo/db/structure/record_store_v1_simple.h b/src/mongo/db/structure/record_store_v1_simple.h
index 1af6f002767..bb808d3e4a7 100644
--- a/src/mongo/db/structure/record_store_v1_simple.h
+++ b/src/mongo/db/structure/record_store_v1_simple.h
@@ -47,10 +47,18 @@ namespace mongo {
virtual ~SimpleRecordStoreV1();
+ const char* name() const { return "SimpleRecordStoreV1"; }
+
virtual RecordIterator* getIterator( const DiskLoc& start, bool tailable,
const CollectionScanParams::Direction& dir) const;
virtual Status truncate();
+
+ virtual bool compactSupported() const { return true; }
+ virtual Status compact( RecordStoreCompactAdaptor* adaptor,
+ const CompactOptions* options,
+ CompactStats* stats );
+
protected:
virtual StatusWith<DiskLoc> allocRecord( int lengthWithHeaders, int quotaMax );
@@ -58,6 +66,11 @@ namespace mongo {
private:
DiskLoc _allocFromExistingExtents( int lengthWithHeaders );
+ void _compactExtent(const DiskLoc diskloc, int extentNumber,
+ RecordStoreCompactAdaptor* adaptor,
+ const CompactOptions* compactOptions,
+ CompactStats* stats );
+
bool _normalCollection;
friend class SimpleRecordStoreV1Iterator;