0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/db/pdfile.cpp

388 lines
10 KiB
C++
Raw Normal View History

2007-10-20 01:35:48 +02:00
// pdfile.cpp
2007-10-30 10:50:14 +01:00
/*
todo:
_ manage deleted records. bucket?
_ use deleted on inserts!
_ quantize allocations
2007-10-30 16:35:17 +01:00
_ table scans must be sequential, not next/prev pointers
2007-10-30 10:50:14 +01:00
*/
2007-10-20 01:35:48 +02:00
#include "stdafx.h"
#include "pdfile.h"
#include "db.h"
#include "../util/mmap.h"
#include "../util/hashtab.h"
DataFileMgr theDataFileMgr;
/* just temporary */
2007-10-30 10:50:14 +01:00
const int ExtentSize = 1 * 1024 * 1024;
2007-10-20 01:35:48 +02:00
/*---------------------------------------------------------------------*/
2007-10-30 10:50:14 +01:00
int bucketSizes[] = {
32, 64, 128, 256, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000,
0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000,
0x400000, 0x800000
};
const int Buckets = 19;
2007-10-30 16:35:17 +01:00
const int MaxBucket = 18;
2007-10-30 10:50:14 +01:00
class NamespaceDetails {
public:
NamespaceDetails() { memset(reserved, 0, sizeof(reserved)); }
DiskLoc firstExtent;
DiskLoc deletedList[Buckets];
char reserved[256];
static int bucket(int n) {
for( int i = 0; i < Buckets; i++ )
if( bucketSizes[i] > n )
return i;
return Buckets-1;
}
2007-10-30 16:35:17 +01:00
void addDeletedRec(DeletedRecord *d, DiskLoc dloc) {
2007-10-30 10:50:14 +01:00
int b = bucket(d->lengthWithHeaders);
DiskLoc& list = deletedList[b];
DiskLoc oldHead = list;
list = dloc;
2007-10-30 16:35:17 +01:00
d->nextDeleted = oldHead;
2007-10-30 10:50:14 +01:00
}
2007-10-30 16:35:17 +01:00
DiskLoc alloc(int lenToAlloc, DiskLoc& extentLoc);
private:
DiskLoc _alloc(int len);
2007-10-30 10:50:14 +01:00
};
2007-10-30 16:35:17 +01:00
DiskLoc NamespaceDetails::alloc(int lenToAlloc, DiskLoc& extentLoc) {
lenToAlloc = (lenToAlloc + 3) & 0xfffffffc;
DiskLoc loc = _alloc(lenToAlloc);
if( loc.isNull() )
return loc;
DeletedRecord *r = loc.drec();
/* note we want to grab from the front so our next pointers on disk tend
to go in a forward direction which is important for performance. */
int regionlen = r->lengthWithHeaders;
extentLoc.set(loc.a(), r->extentOfs);
int left = regionlen - lenToAlloc;
if( left < 24 ) {
// you get the whole thing.
return loc;
}
/* split off some for further use. */
r->lengthWithHeaders = lenToAlloc;
DiskLoc newDelLoc = loc;
newDelLoc.inc(lenToAlloc);
DeletedRecord *newDel = newDelLoc.drec();
newDel->extentOfs = r->extentOfs;
newDel->lengthWithHeaders = left;
newDel->nextDeleted.Null();
addDeletedRec(newDel, newDelLoc);
return loc;
}
/* returned item is out of the deleted list upon return */
DiskLoc NamespaceDetails::_alloc(int len) {
DiskLoc *prev;
DiskLoc *bestprev = 0;
DiskLoc bestmatch;
int bestmatchlen = 0x7fffffff;
int b = bucket(len);
DiskLoc cur = deletedList[b]; prev = &deletedList[b];
int extra = 5; // look for a better fit, a little.
int chain = 0;
while( 1 ) {
if( cur.isNull() ) {
// move to next bucket. if we were doing "extra", just break
if( bestmatchlen < 0x7fffffff )
break;
b++;
if( b > MaxBucket ) {
// out of space. alloc a new extent.
return DiskLoc();
}
cur = deletedList[b]; prev = &deletedList[b];
continue;
}
DeletedRecord *r = cur.drec();
if( r->lengthWithHeaders >= len &&
r->lengthWithHeaders < bestmatchlen ) {
bestmatchlen = r->lengthWithHeaders;
bestmatch = cur;
bestprev = prev;
}
if( bestmatchlen < 0x7fffffff && --extra <= 0 )
break;
if( ++chain > 30 && b < MaxBucket ) {
// too slow, force move to next bucket to grab a big chunk
b++;
chain = 0;
cur.Null();
}
else {
cur = r->nextDeleted; prev = &r->nextDeleted;
}
}
/* unlink ourself from the deleted list */
*bestprev = bestmatch.drec()->nextDeleted;
return bestmatch;
}
2007-10-20 01:35:48 +02:00
class NamespaceIndex {
public:
NamespaceIndex() { }
void init() {
const int LEN = 16 * 1024 * 1024;
2007-10-30 18:35:02 +01:00
void *p = f.map("/data/db/namespace.idx", LEN);
2007-10-30 10:50:14 +01:00
ht = new HashTable<Namespace,NamespaceDetails>(p, LEN, "namespace index");
2007-10-20 01:35:48 +02:00
}
void add(const char *ns, DiskLoc& loc) {
Namespace n(ns);
2007-10-30 10:50:14 +01:00
NamespaceDetails details;
details.firstExtent = loc;
ht->put(n, details);
2007-10-20 01:35:48 +02:00
}
2007-10-30 10:50:14 +01:00
NamespaceDetails* details(const char *ns) {
2007-10-20 01:35:48 +02:00
Namespace n(ns);
2007-10-30 10:50:14 +01:00
return ht->get(n);
}
bool find(const char *ns, DiskLoc& loc) {
NamespaceDetails *l = details(ns);
2007-10-20 01:35:48 +02:00
if( l ) {
2007-10-30 10:50:14 +01:00
loc = l->firstExtent;
2007-10-20 01:35:48 +02:00
return true;
}
return false;
}
private:
MemoryMappedFile f;
2007-10-30 10:50:14 +01:00
HashTable<Namespace,NamespaceDetails> *ht;
2007-10-20 01:35:48 +02:00
} namespaceIndex;
/*---------------------------------------------------------------------*/
void PhysicalDataFile::open(const char *filename, int length) {
header = (PDFHeader *) mmf.map(filename, length);
assert(header);
header->init(length);
}
2007-10-30 10:50:14 +01:00
/* prev - previous extent for this namespace. null=this is the first one. */
2007-10-30 16:35:17 +01:00
Extent* PhysicalDataFile::newExtent(const char *ns) {
DiskLoc loc;
2007-10-20 01:35:48 +02:00
int left = header->unusedLength - ExtentSize;
if( left < 0 ) {
cout << "ERROR: newExtent: no more room for extents. write more code" << endl;
assert(false);
exit(2);
}
int offset = header->unused.getOfs();
header->unused.setOfs( offset + ExtentSize );
header->unusedLength -= ExtentSize;
loc.setOfs(offset);
Extent *e = _getExtent(loc);
2007-10-30 16:35:17 +01:00
DiskLoc emptyLoc = e->init(ns, ExtentSize, offset);
DiskLoc oldExtentLoc;
if( namespaceIndex.find(ns, oldExtentLoc) ) {
Extent *old = oldExtentLoc.ext();
assert( old->xprev.isNull() );
old->xprev = loc;
e->xnext = oldExtentLoc;
namespaceIndex.details(ns)->firstExtent = loc;
2007-10-30 10:50:14 +01:00
}
2007-10-30 16:35:17 +01:00
else {
namespaceIndex.add(ns, loc);
}
namespaceIndex.details(ns)->addDeletedRec(emptyLoc.drec(), emptyLoc);
2007-10-20 01:35:48 +02:00
return e;
}
/*---------------------------------------------------------------------*/
/* assumes already zeroed -- insufficient for block 'reuse' perhaps */
2007-10-30 16:35:17 +01:00
DiskLoc Extent::init(const char *nsname, int _length, int _offset) {
2007-10-20 01:35:48 +02:00
magic = 0x41424344;
myLoc.setOfs(_offset);
2007-10-30 10:50:14 +01:00
xnext.Null(); xprev.Null();
2007-10-20 01:35:48 +02:00
ns = nsname;
length = _length;
firstRecord.Null(); lastRecord.Null();
2007-10-30 16:35:17 +01:00
DiskLoc emptyLoc = myLoc;
emptyLoc.inc( (extentData-(char*)this) );
2007-10-20 01:35:48 +02:00
2007-10-30 16:35:17 +01:00
DeletedRecord *empty1 = (DeletedRecord *) extentData;
DeletedRecord *empty = (DeletedRecord *) getRecord(emptyLoc);
2007-10-20 01:35:48 +02:00
assert( empty == empty1 );
empty->lengthWithHeaders = _length - (extentData - (char *) this);
2007-10-30 16:35:17 +01:00
empty->extentOfs = myLoc.getOfs();
return emptyLoc;
2007-10-20 01:35:48 +02:00
}
2007-10-30 16:35:17 +01:00
/*
2007-10-20 01:35:48 +02:00
Record* Extent::newRecord(int len) {
if( firstEmptyRegion.isNull() )
return 0;
assert(len > 0);
int newRecSize = len + Record::HeaderSize;
DiskLoc newRecordLoc = firstEmptyRegion;
Record *r = getRecord(newRecordLoc);
int left = r->netLength() - len;
if( left < 0 ) {
2007-10-30 16:35:17 +01:00
//
2007-10-20 01:35:48 +02:00
firstEmptyRegion.Null();
return 0;
}
2007-10-30 10:50:14 +01:00
DiskLoc nextEmpty = r->next.getNextEmpty(firstEmptyRegion);
2007-10-20 01:35:48 +02:00
r->lengthWithHeaders = newRecSize;
2007-10-30 10:50:14 +01:00
r->next.markAsFirstOrLastInExtent(this); // we're now last in the extent
2007-10-20 01:35:48 +02:00
if( !lastRecord.isNull() ) {
2007-10-30 10:50:14 +01:00
assert(getRecord(lastRecord)->next.lastInExtent()); // it was the last one
getRecord(lastRecord)->next.set(newRecordLoc); // until now
r->prev.set(lastRecord);
2007-10-20 01:35:48 +02:00
}
2007-10-30 10:50:14 +01:00
else {
r->prev.markAsFirstOrLastInExtent(this); // we are the first in the extent
assert( firstRecord.isNull() );
2007-10-20 01:35:48 +02:00
firstRecord = newRecordLoc;
2007-10-30 10:50:14 +01:00
}
lastRecord = newRecordLoc;
2007-10-20 01:35:48 +02:00
if( left < Record::HeaderSize + 32 ) {
firstEmptyRegion.Null();
}
else {
firstEmptyRegion.inc(newRecSize);
Record *empty = getRecord(firstEmptyRegion);
2007-10-30 10:50:14 +01:00
empty->next.set(nextEmpty); // not for empty records, unless in-use records, next and prev can be null.
empty->prev.Null();
2007-10-20 01:35:48 +02:00
empty->lengthWithHeaders = left;
}
return r;
}
2007-10-30 16:35:17 +01:00
*/
2007-10-20 01:35:48 +02:00
/*---------------------------------------------------------------------*/
Cursor DataFileMgr::findAll(const char *ns) {
DiskLoc loc;
bool found = namespaceIndex.find(ns, loc);
if( !found ) {
cout << "info: findAll() namespace does not exist: " << ns << endl;
return Cursor(DiskLoc());
}
Extent *e = temp.getExtent(loc);
return Cursor( e->firstRecord );
}
2007-10-30 16:35:17 +01:00
void DataFileMgr::deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl)
{
2007-10-30 10:50:14 +01:00
/* remove ourself from the record next/prev chain */
2007-10-30 16:35:17 +01:00
{
if( todelete->prevOfs != DiskLoc::NullOfs )
todelete->getPrev(dl).rec()->nextOfs = todelete->nextOfs;
if( todelete->nextOfs != DiskLoc::NullOfs )
todelete->getNext(dl).rec()->prevOfs = todelete->prevOfs;
}
2007-10-30 10:50:14 +01:00
/* remove ourself from extent pointers */
2007-10-30 16:35:17 +01:00
{
Extent *e = todelete->myExtent(dl);
if( e->firstRecord == dl )
e->firstRecord.setOfs(todelete->nextOfs);
if( e->lastRecord == dl )
e->lastRecord.setOfs(todelete->prevOfs);
2007-10-30 10:50:14 +01:00
}
2007-10-30 16:35:17 +01:00
{
NamespaceDetails* d = namespaceIndex.details(ns);
d->addDeletedRec((DeletedRecord*)todelete, dl);
}
2007-10-30 10:50:14 +01:00
}
/** Note: as written so far, if the object shrinks a lot, we don't free up space. */
void DataFileMgr::update(
const char *ns,
Record *toupdate, const DiskLoc& dl,
const char *buf, int len)
{
if( toupdate->netLength() < len ) {
cout << "temp: update: moving record to a larger location " << ns << endl;
// doesn't fit.
deleteRecord(ns, toupdate, dl);
insert(ns, buf, len);
return;
}
memcpy(toupdate->data, buf, len);
}
2007-10-28 17:37:24 +01:00
void DataFileMgr::insert(const char *ns, const void *buf, int len) {
2007-10-30 16:35:17 +01:00
NamespaceDetails *d = namespaceIndex.details(ns);
if( d == 0 ) {
2007-10-20 01:35:48 +02:00
cout << "New namespace: " << ns << endl;
2007-10-30 16:35:17 +01:00
temp.newExtent(ns);
d = namespaceIndex.details(ns);
2007-10-20 01:35:48 +02:00
}
2007-10-30 16:35:17 +01:00
DiskLoc extentLoc;
int lenWHdr = len + Record::HeaderSize;
DiskLoc loc = d->alloc(lenWHdr, extentLoc);
if( loc.isNull() ) {
// out of space
cout << "allocating new extent for " << ns << endl;
temp.newExtent(ns);
loc = d->alloc(lenWHdr, extentLoc);
if( loc.isNull() ) {
cout << "ERROR: out of space in datafile. write more code." << endl;
assert(false);
return;
}
}
Record *r = loc.rec();
assert( r->lengthWithHeaders >= lenWHdr );
2007-10-20 01:35:48 +02:00
memcpy(r->data, buf, len);
2007-10-30 16:35:17 +01:00
Extent *e = r->myExtent(loc);
if( e->lastRecord.isNull() ) {
e->firstRecord = e->lastRecord = loc;
r->prevOfs = r->nextOfs = DiskLoc::NullOfs;
}
else {
Record *oldlast = e->lastRecord.rec();
r->prevOfs = e->lastRecord.getOfs();
r->nextOfs = DiskLoc::NullOfs;
e->lastRecord = loc;
}
2007-10-20 01:35:48 +02:00
}
void DataFileMgr::init() {
2007-10-30 18:35:02 +01:00
temp.open("/data/db/temp.dat", 64 * 1024 * 1024);
2007-10-20 01:35:48 +02:00
}
void pdfileInit() {
namespaceIndex.init();
theDataFileMgr.init();
}