diff options
Diffstat (limited to 'db/compact.cpp')
| -rw-r--r-- | db/compact.cpp | 303 |
1 files changed, 0 insertions, 303 deletions
diff --git a/db/compact.cpp b/db/compact.cpp deleted file mode 100644 index c1000061e64..00000000000 --- a/db/compact.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/** @file compact.cpp - compaction of deleted space in pdfiles (datafiles) -*/ - -/* NOTE 6Oct2010 : this file PRELIMINARY, EXPERIMENTAL, NOT DONE, NOT USED YET (not in SConstruct) */ - -/** -* Copyright (C) 2010 10gen 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,b -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "pch.h" -#include "pdfile.h" -#include "concurrency.h" -#include "commands.h" -#include "curop-inl.h" -#include "background.h" -#include "extsort.h" -#include "compact.h" -#include "../util/concurrency/task.h" - -namespace mongo { - - char faux; - - void addRecordToRecListInExtent(Record *r, DiskLoc loc); - DiskLoc allocateSpaceForANewRecord(const char *ns, NamespaceDetails *d, int lenWHdr, bool god); - void freeExtents(DiskLoc firstExt, DiskLoc lastExt); - - /** @return number of skipped (invalid) documents */ - unsigned compactExtent(const char *ns, NamespaceDetails *d, const DiskLoc ext, int n, - const scoped_array<IndexSpec> &indexSpecs, - scoped_array<SortPhaseOne>& phase1, int nidx, bool validate) - { - log() << "compact extent #" << n << endl; - - Extent *e = ext.ext(); - e->assertOk(); - assert( e->validates() ); - unsigned skipped = 0; - - { - // 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; - MAdvise adv(e, e->length, MAdvise::Sequential); - const char *p = (const char *) e; - for( int i = 0; i < e->length; i += 4096 ) { - faux += *p; - } - int ms = t.millis(); - if( ms > 1000 ) - log() << "compact end paging in " << ms << "ms " << e->length/1000000.0/ms << "MB/sec" << endl; - } - - { - log() << "compact copying records" << endl; - unsigned totalSize = 0; - int nrecs = 0; - DiskLoc L = e->firstRecord; - if( !L.isNull() ) - while( 1 ) { - Record *recOld = L.rec(); - L = recOld->nextInExtent(L); - nrecs++; - BSONObj objOld(recOld); - - if( !validate || objOld.valid() ) { - unsigned sz = objOld.objsize(); - unsigned lenWHdr = sz + Record::HeaderSize; - totalSize += lenWHdr; - DiskLoc extentLoc; - DiskLoc loc = allocateSpaceForANewRecord(ns, d, lenWHdr, false); - uassert(14024, "compact error out of space during compaction", !loc.isNull()); - Record *recNew = loc.rec(); - recNew = (Record *) getDur().writingPtr(recNew, lenWHdr); - addRecordToRecListInExtent(recNew, loc); - memcpy(recNew->data, objOld.objdata(), sz); - - { - // extract keys for all indexes we will be rebuilding - for( int x = 0; x < nidx; x++ ) { - phase1[x].addKeys(indexSpecs[x], objOld, loc); - } - } - } - else { - if( ++skipped <= 10 ) - log() << "compact skipping invalid object" << endl; - } - - 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(false) != 0; - if( stopping || getDur().aCommitIsNeeded() ) { - e->firstRecord.writing() = L; - Record *r = L.rec(); - getDur().writingInt(r->prevOfs) = DiskLoc::NullOfs; - getDur().commitIfNeeded(); - killCurrentOp.checkForInterrupt(false); - } - } - - assert( d->firstExtent == ext ); - assert( d->lastExtent != ext ); - DiskLoc newFirst = e->xnext; - d->firstExtent.writing() = newFirst; - newFirst.ext()->xprev.writing().Null(); - getDur().writing(e)->markEmpty(); - freeExtents(ext,ext); - getDur().commitIfNeeded(); - - log() << "compact " << nrecs << " documents " << totalSize/1000000.0 << "MB" << endl; - } - - return skipped; - } - - extern SortPhaseOne *precalced; - - bool _compact(const char *ns, NamespaceDetails *d, string& errmsg, bool validate, BSONObjBuilder& result) { - //int les = d->lastExtentSize; - - // this is a big job, so might as well make things tidy before we start just to be nice. - getDur().commitNow(); - - list<DiskLoc> extents; - for( DiskLoc L = d->firstExtent; !L.isNull(); L = L.ext()->xnext ) - extents.push_back(L); - log() << "compact " << extents.size() << " extents" << endl; - - ProgressMeterHolder pm( cc().curop()->setMessage( "compact extent" , extents.size() ) ); - - // same data, but might perform a little different after compact? - NamespaceDetailsTransient::get_w(ns).clearQueryCache(); - - int nidx = d->nIndexes; - scoped_array<IndexSpec> indexSpecs( new IndexSpec[nidx] ); - scoped_array<SortPhaseOne> phase1( new SortPhaseOne[nidx] ); - { - NamespaceDetails::IndexIterator ii = d->ii(); - int x = 0; - while( ii.more() ) { - BSONObjBuilder b; - IndexDetails& idx = ii.next(); - BSONObj::iterator i(idx.info.obj()); - while( i.more() ) { - BSONElement e = i.next(); - if( !str::equals(e.fieldName(), "v") && !str::equals(e.fieldName(), "background") ) { - b.append(e); - } - } - BSONObj o = b.obj().getOwned(); - phase1[x].sorter.reset( new BSONObjExternalSorter( idx.idxInterface(), o.getObjectField("key") ) ); - phase1[x].sorter->hintNumObjects( d->stats.nrecords ); - indexSpecs[x++].reset(o); - } - } - - log() << "compact orphan deleted lists" << endl; - for( int i = 0; i < Buckets; i++ ) { - d->deletedList[i].writing().Null(); - } - - - - // Start over from scratch with our extent sizing and growth - d->lastExtentSize=0; - - // before dropping indexes, at least make sure we can allocate one extent! - uassert(14025, "compact error no space available to allocate", !allocateSpaceForANewRecord(ns, d, Record::HeaderSize+1, false).isNull()); - - // note that the drop indexes call also invalidates all clientcursors for the namespace, which is important and wanted here - log() << "compact dropping indexes" << endl; - BSONObjBuilder b; - if( !dropIndexes(d, ns, "*", errmsg, b, true) ) { - errmsg = "compact drop indexes failed"; - log() << errmsg << endl; - return false; - } - - getDur().commitNow(); - - long long skipped = 0; - int n = 0; - for( list<DiskLoc>::iterator i = extents.begin(); i != extents.end(); i++ ) { - skipped += compactExtent(ns, d, *i, n++, indexSpecs, phase1, nidx, validate); - pm.hit(); - } - - if( skipped ) { - result.append("invalidObjects", skipped); - } - - assert( d->firstExtent.ext()->xprev.isNull() ); - - // indexes will do their own progress meter? - pm.finished(); - - // build indexes - NamespaceString s(ns); - string si = s.db + ".system.indexes"; - for( int i = 0; i < nidx; i++ ) { - killCurrentOp.checkForInterrupt(false); - BSONObj info = indexSpecs[i].info; - log() << "compact create index " << info["key"].Obj().toString() << endl; - try { - precalced = &phase1[i]; - theDataFileMgr.insert(si.c_str(), info.objdata(), info.objsize()); - } - catch(...) { - precalced = 0; - throw; - } - precalced = 0; - } - - return true; - } - - bool compact(const string& ns, string &errmsg, bool validate, BSONObjBuilder& result) { - massert( 14028, "bad ns", NamespaceString::normal(ns.c_str()) ); - massert( 14027, "can't compact a system namespace", !str::contains(ns, ".system.") ); // items in system.indexes cannot be moved there are pointers to those disklocs in NamespaceDetails - - bool ok; - { - writelock lk; - BackgroundOperation::assertNoBgOpInProgForNs(ns.c_str()); - Client::Context ctx(ns); - NamespaceDetails *d = nsdetails(ns.c_str()); - massert( 13660, str::stream() << "namespace " << ns << " does not exist", d ); - massert( 13661, "cannot compact capped collection", !d->capped ); - log() << "compact " << ns << " begin" << endl; - try { - ok = _compact(ns.c_str(), d, errmsg, validate, result); - } - catch(...) { - log() << "compact " << ns << " end (with error)" << endl; - throw; - } - log() << "compact " << ns << " end" << endl; - } - return ok; - } - - bool isCurrentlyAReplSetPrimary(); - - class CompactCmd : public Command { - public: - virtual LockType locktype() const { return NONE; } - virtual bool adminOnly() const { return false; } - virtual bool slaveOk() const { return true; } - virtual bool maintenanceMode() const { return true; } - virtual bool logTheOp() { return false; } - virtual void help( stringstream& help ) const { - help << "compact collection\n" - "warning: this operation blocks the server and is slow. you can cancel with cancelOp()\n" - "{ compact : <collection_name>, [force:true], [validate:true] }\n" - " force - allows to run on a replica set primary\n" - " validate - check records are noncorrupt before adding to newly compacting extents. slower but safer (default is true in this version)\n"; - } - virtual bool requiresAuth() { return true; } - CompactCmd() : Command("compact") { } - - virtual bool run(const string& db, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { - string coll = cmdObj.firstElement().valuestr(); - if( coll.empty() || db.empty() ) { - errmsg = "no collection name specified"; - return false; - } - - if( isCurrentlyAReplSetPrimary() && !cmdObj["force"].trueValue() ) { - errmsg = "will not run compact on an active replica set primary as this is a slow blocking operation. use force:true to force"; - return false; - } - - string ns = db + '.' + coll; - bool validate = !cmdObj.hasElement("validate") || cmdObj["validate"].trueValue(); // default is true at the moment - bool ok = compact(ns, errmsg, validate, result); - return ok; - } - }; - static CompactCmd compactCmd; - -} |
