0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 09:06:21 +01:00
mongodb/db/reccache.cpp
2009-03-19 15:38:49 -04:00

350 lines
8.5 KiB
C++

// storage.cpp
#include "stdafx.h"
#include "pdfile.h"
#include "reccache.h"
#include "rec.h"
#include "db.h"
namespace mongo {
RecCache theRecCache(BucketSize);
void writerThread() {
sleepsecs(10);
while( 1 ) {
try {
theRecCache.writeLazily();
}
catch(...) {
log() << "exception in writerThread()" << endl;
sleepsecs(3);
}
}
}
// called on program exit.
void recCacheCloseAll() {
#if defined(_RECSTORE)
theRecCache.closing();
#endif
}
int ndirtywritten;
/* filename format is
<n>-<ns>.idx
*/
BasicRecStore* RecCache::_initStore(string fname) {
assert( strchr(fname.c_str(), '/') == 0 );
assert( strchr(fname.c_str(), '\\') == 0 );
stringstream ss(fname);
int n;
ss >> n;
assert( n >= 0 );
char ch;
ss >> ch;
assert( ch == '-' );
string rest;
ss >> rest;
const char *p = rest.c_str();
const char *q = strstr(p, ".idx");
assert( q );
string ns(p, q-p);
// arbitrary limit. if you are hitting, we should use fewer files and put multiple
// indexes in a single file (which is easy to do)
massert( "too many index files", n < 10000 );
if( stores.size() < (unsigned)n+1 )
stores.resize(n+1);
assert( stores[n] == 0 );
BasicRecStore *rs = new BasicRecStore(n);
path pf(dbpath);
pf /= fname;
string full = pf.string();
rs->init(full.c_str(), recsize);
stores[n] = rs;
storesByNs[ns] = rs;
return rs;
}
BasicRecStore* RecCache::initStore(int n) {
string ns;
{
stringstream ss;
ss << '/' << n << '-';
ns = ss.str();
}
/* this will be slow if there are thousands of files */
path dir(dbpath);
directory_iterator end;
try {
directory_iterator i(dir);
while ( i != end ) {
string s = i->string();
const char *p = strstr(s.c_str(), ns.c_str());
if( p && strstr(p, ".idx") ) {
// found it
path P = *i;
return _initStore(P.leaf());
}
i++;
}
}
catch( DBException & ) {
throw;
}
catch (...) {
string s = string("i/o error looking for .idx file in ") + dbpath;
massert(s, false);
}
stringstream ss;
ss << "index datafile missing? n=" << n;
uasserted(ss.str());
return 0;
}
/* find the filename for a given ns.
format is
<n>-<escaped_ns>.idx
returns filename. found is true if found. If false, a proposed name is returned for (optional) creation
of the file.
*/
string RecCache::findStoreFilename(const char *_ns, bool& found) {
string namefrag;
{
stringstream ss;
ss << '-';
ss << _ns;
assert( strchr(_ns, '$') == 0); // $ not good for filenames
ss << ".idx";
namefrag = ss.str();
}
path dir(dbpath);
directory_iterator end;
int nmax = -1;
try {
directory_iterator i(dir);
while ( i != end ) {
string s = i->leaf();
const char *p = strstr(s.c_str(), namefrag.c_str());
if( p ) {
found = true;
return s;
}
if( strstr(s.c_str(), ".idx") ) {
stringstream ss(s);
int n = -1;
ss >> n;
if( n > nmax )
nmax = n;
}
i++;
}
}
catch (...) {
string s = string("i/o error looking for .idx file in ") + dbpath;
massert(s, false);
}
// DNE. return a name that would work.
stringstream ss;
ss << nmax+1 << namefrag;
found = false;
return ss.str();
}
void RecCache::initStoreByNs(const char *_ns) {
bool found;
string fn = findStoreFilename(_ns, found);
_initStore(fn);
}
inline void RecCache::writeIfDirty(Node *n) {
if( n->dirty ) {
ndirtywritten++;
n->dirty = false;
store(n->loc).update(fileOfs(n->loc), n->data, recsize);
}
}
void RecCache::closing() {
boostlock lk(rcmutex);
(cout << "TEMP: recCacheCloseAll() writing dirty pages...\n").flush();
writeDirty( dirtyl.begin(), true );
for( unsigned i = 0; i < stores.size(); i++ ) {
if( stores[i] ) {
delete stores[i];
}
}
(cout << "TEMP: write dirty done\n").flush();
}
/* note that this is written in order, as much as possible, given that dirtyl is of type set. */
void RecCache::writeDirty( set<DiskLoc>::iterator startAt, bool rawLog ) {
try {
ndirtywritten=0;
for( set<DiskLoc>::iterator i = startAt; i != dirtyl.end(); i++ ) {
map<DiskLoc, Node*>::iterator j = m.find(*i);
if( j != m.end() )
writeIfDirty(j->second);
}
OCCASIONALLY out() << "TEMP: ndirtywritten: " << ndirtywritten << endl;
}
catch(...) {
const char *message = "Problem: bad() in RecCache::writeDirty, file io error\n";
if ( rawLog )
rawOut( message );
else
( log() << message ).flush();
}
dirtyl.clear();
}
void RecCache::writeLazily() {
int sleep = 0;
int k;
{
boostlock lk(rcmutex);
Timer t;
set<DiskLoc>::iterator i = dirtyl.end();
for( k = 0; k < 100; k++ ) {
if( i == dirtyl.begin() ) {
// we're not very far behind
sleep = k < 20 ? 2000 : 1000;
break;
}
i--;
}
writeDirty(i);
if( sleep == 0 ) {
sleep = t.millis() * 4 + 10;
}
}
OCCASIONALLY cout << "writeLazily " << k << " sleep:" << sleep << '\n';
sleepmillis(sleep);
}
// 100k * 8KB = 800MB
const unsigned RECCACHELIMIT = 150000;
inline void RecCache::ejectOld() {
if( nnodes <= RECCACHELIMIT )
return;
boostlock lk(rcmutex);
if( nnodes <= RECCACHELIMIT )
return;
Node *n = oldest;
while( 1 ) {
if( nnodes <= RECCACHELIMIT - 4 ) {
n->older = 0;
oldest = n;
assert( oldest ) ;
break;
}
nnodes--;
assert(n);
Node *nxt = n->newer;
writeIfDirty(n);
m.erase(n->loc);
delete n;
n = nxt;
}
}
void RecCache::dump() {
Node *n = oldest;
Node *last = 0;
while( n ) {
assert( n->older == last );
last = n;
// cout << n << ' ' << n->older << ' ' << n->newer << '\n';
n=n->newer;
}
assert( newest == last );
// cout << endl;
}
void RecCache::closeStore(BasicRecStore *rs) {
for( set<DiskLoc>::iterator i = dirtyl.begin(); i != dirtyl.end(); ) {
DiskLoc k = *i++;
if( k.a() == rs->fileNumber )
dirtyl.erase(k);
}
for( map<DiskLoc,Node*>::iterator i = m.begin(); i != m.end(); ) {
DiskLoc k = i->first;
i++;
if( k.a() == rs->fileNumber )
m.erase(k);
}
for( unsigned i = 0; i < stores.size(); i++ ) {
if( stores[i] == rs ) {
stores[i] = 0;
break;
}
}
delete rs; // closes file
}
void RecCache::drop(const char *_ns) {
// todo: test with a non clean shutdown file
boostlock lk(rcmutex);
char buf[256];
{
const char *ns = _ns;
char *p = buf;
while( 1 ) {
if( *ns == '$' ) *p = '_';
else
*p = *ns;
if( *ns == 0 )
break;
p++; ns++;
}
assert( p - buf < (int) sizeof(buf) );
}
BasicRecStore *&rs = storesByNs[buf];
string fname;
if( rs ) {
fname = rs->filename;
closeStore(rs);
rs = 0;
}
else {
bool found;
fname = findStoreFilename(buf, found);
if( !found ) {
log() << "RecCache::drop: no idx file found for " << _ns << endl;
return;
}
path pf(dbpath);
pf /= fname;
fname = pf.string();
}
try {
boost::filesystem::remove(fname);
}
catch(...) {
log() << "RecCache::drop: exception removing file " << fname << endl;
}
}
void dbunlocking() {
dassert( dbMutexInfo.isLocked() );
theRecCache.ejectOld();
}
}