/** caller handles locking */ static bool PREPLOGBUFFER(BufBuilder& bb) { if( writes.empty() ) return false; bb.reset(); unsigned *lenInBlockHeader; { // JSectHeader bb.appendStr("\nHH\n", false); lenInBlockHeader = (unsigned *) bb.skip(4); } string lastFilePath; { scoped_lock lk(privateViews._mutex()); for( vector<WriteIntent>::iterator i = writes.begin(); i != writes.end(); i++ ) { size_t ofs; MongoMMF *mmf = privateViews._find(i->p, ofs); if( mmf == 0 ) { journalingFailure("view pointer cannot be resolved"); } else { if( mmf->filePath() != lastFilePath ) { lastFilePath = mmf->filePath(); JDbContext c; bb.appendStruct(c); bb.appendStr(lastFilePath); } JEntry e; e.len = i->len; e.fileNo = mmf->fileSuffixNo(); bb.appendStruct(e); bb.appendBuf(i->p, i->len); } } } { JSectFooter f; f.hash = 0; bb.appendStruct(f); } { unsigned L = (bb.len() + 8191) & 0xffffe000; // fill to alignment dassert( L >= (unsigned) bb.len() ); *lenInBlockHeader = L; unsigned padding = L - bb.len(); bb.skip(padding); dassert( bb.len() % 8192 == 0 ); } writes.clear(); alreadyNoted.clear(); return true; }
void* writingPtr(void *x, size_t len) { //log() << "TEMP writing " << x << ' ' << len << endl; void *p = x; DEV p = MongoMMF::switchToPrivateView(x); WriteIntent w(p, len); if( !alreadyNoted.checkAndSet(w) ) { // remember intent. we will journal it in a bit writes.push_back(w); wassert( writes.size() < 2000000 ); assert( writes.size() < 20000000 ); } return p; }
namespace dur { //MongoMMF* pointerToMMF(void *p, size_t& ofs); struct WriteIntent { WriteIntent() : p(0) { } WriteIntent(void *a, unsigned b) : p(a), len(b) { } void *p; // where we will write unsigned len; // up to this len }; /* try to remember things we have already marked for journalling. false negatives are ok if infrequent - we will just log them twice. */ template<int Prime> class Already { enum { N = Prime }; // this should be small the idea is that it fits in the cpu cache easily WriteIntent nodes[N]; 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. @return true if already indicated. */ bool checkAndSet(const WriteIntent& w) { unsigned x = mongoutils::hashPointer(w.p); WriteIntent& nd = nodes[x % N]; if( nd.p != w.p || nd.len < w.len ) { nd = w; return false; } return true; } }; static Already<127> alreadyNoted; /* our record of pending/uncommitted write intents */ static vector<WriteIntent> writes; void* writingPtr(void *x, size_t len) { //log() << "TEMP writing " << x << ' ' << len << endl; void *p = x; DEV p = MongoMMF::switchToPrivateView(x); WriteIntent w(p, len); if( !alreadyNoted.checkAndSet(w) ) { // remember intent. we will journal it in a bit writes.push_back(w); wassert( writes.size() < 2000000 ); assert( writes.size() < 20000000 ); } return p; } /** caller handles locking */ static bool PREPLOGBUFFER(BufBuilder& bb) { if( writes.empty() ) return false; bb.reset(); unsigned *lenInBlockHeader; { // JSectHeader bb.appendStr("\nHH\n", false); lenInBlockHeader = (unsigned *) bb.skip(4); } string lastFilePath; { scoped_lock lk(privateViews._mutex()); for( vector<WriteIntent>::iterator i = writes.begin(); i != writes.end(); i++ ) { size_t ofs; MongoMMF *mmf = privateViews._find(i->p, ofs); if( mmf == 0 ) { journalingFailure("view pointer cannot be resolved"); } else { if( mmf->filePath() != lastFilePath ) { lastFilePath = mmf->filePath(); JDbContext c; bb.appendStruct(c); bb.appendStr(lastFilePath); } JEntry e; e.len = i->len; e.fileNo = mmf->fileSuffixNo(); bb.appendStruct(e); bb.appendBuf(i->p, i->len); } } } { JSectFooter f; f.hash = 0; bb.appendStruct(f); } { unsigned L = (bb.len() + 8191) & 0xffffe000; // fill to alignment dassert( L >= (unsigned) bb.len() ); *lenInBlockHeader = L; unsigned padding = L - bb.len(); bb.skip(padding); dassert( bb.len() % 8192 == 0 ); } writes.clear(); alreadyNoted.clear(); return true; } static void WRITETOJOURNAL(const BufBuilder& bb) { journal(bb); } static void _go(BufBuilder& bb) { PREPLOGBUFFER(bb); // todo: add double buffering so we can be (not even read locked) during WRITETOJOURNAL WRITETOJOURNAL(bb); } static void go(BufBuilder& bb) { { readlocktry lk("", 1000); if( lk.got() ) { _go(bb); return; } } // starvation on read locks could occur. so if read lock acquisition is slow, try to get a // write lock instead. otherwise writes could use too much RAM. writelock lk; _go(bb); } static void durThread() { Client::initThread("dur"); const int HowOftenToGroupCommitMs = 100; BufBuilder bb(1024 * 1024 * 16); // reuse to avoid any heap fragmentation while( 1 ) { try { int millis = HowOftenToGroupCommitMs; { Timer t; journalRotate(); // note we do this part outside of mongomutex millis -= t.millis(); if( millis < 5 || millis > HowOftenToGroupCommitMs ) millis = 5; } sleepmillis(millis); go(bb); } catch(std::exception& e) { log() << "exception in durThread " << e.what() << endl; } } } void startup() { journalMakeDir(); boost::thread t(durThread); } } // namespace dur
void clear() { _alreadyNoted.clear(); _writes.clear(); }