Files
mongo/db/dur_commitjob.h

221 lines
7.6 KiB
C
Raw Normal View History

2010-11-20 15:29:49 -05:00
/* @file dur_commitjob.h used by dur.cpp
*/
/**
* Copyright (C) 2009 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,
* 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/>.
*/
#pragma once
2010-12-20 09:28:19 -05:00
#include "../util/alignedbuilder.h"
2010-11-28 19:26:17 -05:00
#include "../util/mongoutils/hash.h"
#include "../util/concurrency/synchronization.h"
2010-12-12 19:47:10 -05:00
#include "cmdline.h"
2010-11-26 18:18:24 -05:00
#include "durop.h"
2010-12-12 19:47:10 -05:00
#include "dur.h"
2010-12-22 09:44:32 -05:00
#include "taskqueue.h"
2010-11-20 15:29:49 -05:00
2010-12-24 12:13:26 -05:00
//#define DEBUG_WRITE_INTENT 1
2011-01-04 00:40:41 -05:00
namespace mongo {
2010-11-20 15:29:49 -05:00
namespace dur {
2010-12-22 16:29:48 -05:00
/** declaration of an intent to write to a region of a memory mapped view
*
* We store the end rather than the start pointer to make operator< faster
* since that is heavily used in set lookup.
*/
2011-01-04 00:40:41 -05:00
struct WriteIntent { /* copyable */
2011-07-11 08:11:38 -04:00
WriteIntent() : /*w_ptr(0), */ p(0) { }
WriteIntent(void *a, unsigned b) : /*w_ptr(0), */ p((char*)a+b), len(b) { }
2010-12-22 16:29:48 -05:00
void* start() const { return (char*)p - len; }
void* end() const { return p; }
2010-12-30 14:01:01 -05:00
unsigned length() const { return len; }
bool operator < (const WriteIntent& rhs) const { return end() < rhs.end(); }
// can they be merged?
bool overlaps(const WriteIntent& rhs) const {
return (start() <= rhs.end() && end() >= rhs.start());
}
// is merging necessary?
bool contains(const WriteIntent& rhs) const {
return (start() <= rhs.start() && end() >= rhs.end());
}
// merge into me
void absorb(const WriteIntent& other);
friend ostream& operator << (ostream& out, const WriteIntent& wi) {
return (out << "p: " << wi.p << " end: " << wi.end() << " len: " << wi.len);
}
2011-07-11 08:11:38 -04:00
//mutable void *w_ptr; // writable mapping of p.
2011-01-04 00:40:41 -05:00
// mutable because set::iterator is const but this isn't used in op<
2010-12-26 14:01:19 -05:00
#if defined(_EXPERIMENTAL)
mutable unsigned ofsInJournalBuffer;
#endif
private:
2010-12-22 16:29:48 -05:00
void *p; // intent to write up to p
unsigned len; // up to this len
2010-11-20 15:29:49 -05:00
};
2011-01-26 17:25:08 -08:00
/** try to remember things we have already marked for journaling. false negatives are ok if infrequent -
we will just log them twice.
*/
2010-11-20 15:29:49 -05:00
template<int Prime>
2010-12-12 20:18:16 -05:00
class Already : boost::noncopyable {
2010-11-20 15:29:49 -05:00
public:
Already() { clear(); }
void clear() { memset(this, 0, sizeof(*this)); }
/* see if we have Already recorded/indicated our write intent for this region of memory.
2010-12-06 23:16:23 -05:00
automatically upgrades the length if the length was shorter previously.
2010-11-20 15:29:49 -05:00
@return true if already indicated.
*/
2010-12-22 14:20:45 -05:00
bool checkAndSet(void* p, int len) {
unsigned x = mongoutils::hashPointer(p);
pair<void*, int>& nd = nodes[x % N];
2011-01-04 00:40:41 -05:00
if( nd.first == p ) {
2010-12-22 14:20:45 -05:00
if( nd.second < len ) {
nd.second = len;
2010-12-08 02:17:50 -05:00
return false; // haven't indicated this len yet
}
2010-12-07 15:16:00 -05:00
return true; // already indicated
2010-11-20 15:29:49 -05:00
}
2010-12-22 14:20:45 -05:00
nd.first = p;
nd.second = len;
2010-12-07 15:16:00 -05:00
return false; // a new set
2010-11-20 15:29:49 -05:00
}
2010-12-19 11:47:39 -05:00
2010-12-12 20:18:16 -05:00
private:
enum { N = Prime }; // this should be small the idea is that it fits in the cpu cache easily
2010-12-22 14:20:45 -05:00
pair<void*,int> nodes[N];
2010-11-20 15:29:49 -05:00
};
/** our record of pending/uncommitted write intents */
2010-11-26 18:18:24 -05:00
class Writes : boost::noncopyable {
struct D {
2011-01-04 00:40:41 -05:00
void *p;
unsigned len;
static void go(const D& d);
};
2010-11-26 18:18:24 -05:00
public:
2010-12-22 09:44:32 -05:00
TaskQueue<D> _deferred;
2010-11-20 15:29:49 -05:00
Already<127> _alreadyNoted;
set<WriteIntent> _writes;
2010-11-26 18:18:24 -05:00
vector< shared_ptr<DurOp> > _ops; // all the ops other than basic writes
bool _drained; // _deferred is drained? for asserting/testing
2010-12-19 11:47:39 -05:00
2010-12-12 20:18:16 -05:00
/** reset the Writes structure (empties all the above) */
void clear();
2010-12-21 09:56:36 -05:00
2010-12-24 12:13:26 -05:00
/** merges into set (ie non-deferred version) */
2011-01-04 00:40:41 -05:00
void _insertWriteIntent(void* p, int len);
2011-01-04 00:40:41 -05:00
void insertWriteIntent(void* p, int len) {
2010-12-24 12:13:26 -05:00
#if defined(DEBUG_WRITE_INTENT)
if( _debug[p] < len )
_debug[p] = len;
#endif
D d;
2010-12-22 14:20:45 -05:00
d.p = p;
d.len = len;
2011-01-04 00:40:41 -05:00
_deferred.defer(d);
}
#ifdef _DEBUG
WriteIntent _last;
2010-12-24 12:13:26 -05:00
#endif
#if defined(DEBUG_WRITE_INTENT)
map<void*,int> _debug;
#endif
2010-11-20 15:29:49 -05:00
};
2010-12-24 12:13:26 -05:00
#if defined(DEBUG_WRITE_INTENT)
void assertAlreadyDeclared(void *, int len);
#else
2010-12-24 12:14:53 -05:00
inline void assertAlreadyDeclared(void *, int len) { }
2010-12-24 12:13:26 -05:00
#endif
/** A commit job object for a group commit. Currently there is one instance of this object.
concurrency: assumption is caller is appropriately locking.
2010-11-27 17:30:51 -05:00
for example note() invocations are from the write lock.
other uses are in a read lock from a single thread (durThread)
2010-11-20 15:29:49 -05:00
*/
2011-01-04 00:40:41 -05:00
class CommitJob : boost::noncopyable {
2010-11-20 15:29:49 -05:00
public:
2010-12-20 09:28:19 -05:00
AlignedBuilder _ab; // for direct i/o writes to journal
2010-11-20 15:29:49 -05:00
CommitJob();
2010-11-20 15:29:49 -05:00
~CommitJob(){ assert(!"shouldn't destroy CommitJob!"); }
2010-11-20 15:29:49 -05:00
/** record/note an intent to write */
void note(void* p, int len);
2010-11-20 15:29:49 -05:00
2010-11-27 17:30:51 -05:00
/** note an operation other than a "basic write" */
2010-12-12 20:18:16 -05:00
void noteOp(shared_ptr<DurOp> p);
2010-11-26 18:18:24 -05:00
2011-01-04 00:40:41 -05:00
set<WriteIntent>& writes() {
if( !_wi._drained ) {
// generally, you don't want to use the set until it is prepared (after deferred ops are applied)
// thus this assert here.
2011-01-04 00:40:41 -05:00
assert(false);
}
2011-01-04 00:40:41 -05:00
return _wi._writes;
}
2010-11-26 18:18:24 -05:00
vector< shared_ptr<DurOp> >& ops() { return _wi._ops; }
2010-11-20 15:29:49 -05:00
2011-01-04 00:40:41 -05:00
/** this method is safe to call outside of locks. when haswritten is false we don't do any group commit and avoid even
trying to acquire a lock, which might be helpful at times.
2010-11-26 18:18:24 -05:00
*/
2010-11-20 15:29:49 -05:00
bool hasWritten() const { return _hasWritten; }
/** we use the commitjob object over and over, calling reset() rather than reconstructing */
2010-12-12 20:18:16 -05:00
void reset();
void beginCommit();
/** the commit code calls this when data reaches the journal (on disk) */
void notifyCommitted() { _notify.notifyAll(_commitNumber); }
2010-12-19 11:47:39 -05:00
/** we check how much written and if it is getting to be a lot, we commit sooner. */
size_t bytes() const { return _bytes; }
2010-12-16 17:15:50 -05:00
#if defined(_DEBUG)
const WriteIntent& lastWrite() const { return _wi._last; }
#endif
Writes& wi() { return _wi; }
2010-12-12 20:18:16 -05:00
private:
NotifyAll::When _commitNumber;
2010-12-12 20:18:16 -05:00
bool _hasWritten;
Writes _wi; // todo: fix name
size_t _bytes;
public:
NotifyAll _notify; // for getlasterror fsync:true acknowledgements
unsigned _nSinceCommitIfNeededCall;
2010-11-20 15:29:49 -05:00
};
2010-12-12 20:18:16 -05:00
extern CommitJob& commitJob;
2010-12-12 20:18:16 -05:00
2010-11-20 15:29:49 -05:00
}
}