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

SERVER-100 checkpoint btree cursor can take multiple ranges

This commit is contained in:
Aaron Staple 2009-08-11 16:42:27 -04:00
parent d22866abff
commit 714de2af68
3 changed files with 156 additions and 13 deletions

View File

@ -215,15 +215,17 @@ namespace mongo {
};
class BtreeCursor : public Cursor {
friend class BtreeBucket;
NamespaceDetails *d;
int idxNo;
BSONObj startKey;
BSONObj endKey;
bool endKeyInclusive_;
bool multikey; // note this must be updated every getmore batch in case someone added a multikey...
public:
BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int direction );
// a BoundList contains intervals specified by inclusive start
// and end bounds. The intervals should be nonoverlapping and occur in
// the specified direction of traversal. For example, given a simple index {i:1}
// and direction +1, one valid BoundList is: (1, 2); (4, 6). The same BoundList
// would be valid for index {i:-1} with direction -1.
typedef vector< pair< BSONObj, BSONObj > > BoundList;
BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails& _id, const vector< pair< BSONObj, BSONObj > > &_bounds, int _direction );
virtual bool ok() {
return !bucket.isNull();
}
@ -290,13 +292,14 @@ namespace mongo {
virtual string toString() {
string s = string("BtreeCursor ") + indexDetails.indexName();
if ( direction < 0 ) s += " reverse";
if ( bounds_.size() > 1 ) s += " multi";
return s;
}
BSONObj prettyKey( const BSONObj &key ) const {
return key.replaceFieldNames( indexDetails.keyPattern() ).clientReadable();
}
virtual BSONObj prettyStartKey() const {
return prettyKey( startKey );
}
@ -315,6 +318,20 @@ namespace mongo {
/* Check if the current key is beyond endKey. */
void checkEnd();
// selective audits on construction
void audit();
// init start / end keys with a new range
void init();
friend class BtreeBucket;
NamespaceDetails *d;
int idxNo;
BSONObj startKey;
BSONObj endKey;
bool endKeyInclusive_;
bool multikey; // note this must be updated every getmore batch in case someone added a multikey...
const IndexDetails& indexDetails;
BSONObj order;
DiskLoc bucket;
@ -322,6 +339,8 @@ namespace mongo {
int direction; // 1=fwd,-1=reverse
BSONObj keyAtKeyOfs; // so we can tell if things moved around on us between the query and the getMore call
DiskLoc locAtKeyOfs;
BoundList bounds_;
unsigned boundIndex_;
};
#pragma pack()

View File

@ -34,14 +34,37 @@ namespace mongo {
startKey( _startKey ),
endKey( _endKey ),
endKeyInclusive_( endKeyInclusive ),
multikey( d->isMultikey( idxNo ) ),
indexDetails( _id ),
order( _id.keyPattern() ),
direction( _direction )
direction( _direction ),
boundIndex_()
{
dassert( d->idxNo((IndexDetails&) indexDetails) == idxNo );
multikey = d->isMultikey(idxNo);
audit();
init();
}
BtreeCursor::BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails& _id, const vector< pair< BSONObj, BSONObj > > &_bounds, int _direction )
:
d(_d), idxNo(_idxNo),
endKeyInclusive_( true ),
multikey( d->isMultikey( idxNo ) ),
indexDetails( _id ),
order( _id.keyPattern() ),
direction( _direction ),
bounds_( _bounds ),
boundIndex_()
{
assert( !bounds_.empty() );
startKey = bounds_[ 0 ].first;
endKey = bounds_[ 0 ].second;
audit();
init();
}
void BtreeCursor::audit() {
dassert( d->idxNo((IndexDetails&) indexDetails) == idxNo );
bool found;
if ( otherTraceLevel >= 12 ) {
if ( otherTraceLevel >= 200 ) {
out() << "::BtreeCursor() qtl>200. validating entire index." << endl;
@ -52,7 +75,10 @@ namespace mongo {
indexDetails.head.btree()->dump();
}
}
}
void BtreeCursor::init() {
bool found;
bucket = indexDetails.head.btree()->
locate(indexDetails, indexDetails.head, startKey, order, keyOfs, found, direction > 0 ? minDiskLoc : maxDiskLoc, direction);
@ -104,6 +130,11 @@ namespace mongo {
bucket = bucket.btree()->advance(bucket, keyOfs, direction, "BtreeCursor::advance");
skipUnusedKeys();
checkEnd();
while( !ok() && ++boundIndex_ < bounds_.size() ) {
startKey = bounds_[ boundIndex_ ].first;
endKey = bounds_[ boundIndex_ ].second;
init();
}
return !bucket.isNull();
}

View File

@ -18,6 +18,8 @@
*/
#include "../db/clientcursor.h"
#include "../db/instance.h"
#include "../db/btree.h"
#include "dbtests.h"
@ -103,12 +105,103 @@ namespace CursorTests {
};
} // namespace IdSetTests
namespace BtreeCursorTests {
class MultiRange {
public:
void run() {
dblock lk;
const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRange";
{
DBDirectClient c;
for( int i = 0; i < 10; ++i )
c.insert( ns, BSON( "a" << i ) );
ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
}
BtreeCursor::BoundList b;
b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 1 ), BSON( "" << 2 ) ) );
b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 4 ), BSON( "" << 6 ) ) );
setClient( ns );
BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->indexes[ 1 ], b, 1 );
ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() );
double expected[] = { 1, 2, 4, 5, 6 };
for( int i = 0; i < 5; ++i ) {
ASSERT( c.ok() );
ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
c.advance();
}
ASSERT( !c.ok() );
}
};
class MultiRangeGap {
public:
void run() {
dblock lk;
const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRangeGap";
{
DBDirectClient c;
for( int i = 0; i < 10; ++i )
c.insert( ns, BSON( "a" << i ) );
for( int i = 100; i < 110; ++i )
c.insert( ns, BSON( "a" << i ) );
ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
}
BtreeCursor::BoundList b;
b.push_back( pair< BSONObj, BSONObj >( BSON( "" << -50 ), BSON( "" << 2 ) ) );
b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 40 ), BSON( "" << 60 ) ) );
b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 109 ), BSON( "" << 200 ) ) );
setClient( ns );
BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->indexes[ 1 ], b, 1 );
ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() );
double expected[] = { 0, 1, 2, 109 };
for( int i = 0; i < 4; ++i ) {
ASSERT( c.ok() );
ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
c.advance();
}
ASSERT( !c.ok() );
}
};
class MultiRangeReverse {
public:
void run() {
dblock lk;
const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRangeReverse";
{
DBDirectClient c;
for( int i = 0; i < 10; ++i )
c.insert( ns, BSON( "a" << i ) );
ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) );
}
BtreeCursor::BoundList b;
b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 6 ), BSON( "" << 4 ) ) );
b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 2 ), BSON( "" << 1 ) ) );
setClient( ns );
BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->indexes[ 1 ], b, -1 );
ASSERT_EQUALS( "BtreeCursor a_1 reverse multi", c.toString() );
double expected[] = { 6, 5, 4, 2, 1 };
for( int i = 0; i < 5; ++i ) {
ASSERT( c.ok() );
ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() );
c.advance();
}
ASSERT( !c.ok() );
}
};
} // namespace MultiBtreeCursorTests
class All : public Suite {
class All : public ::Suite {
public:
All() {
add< IdSetTests::BasicSize >();
add< IdSetTests::Upgrade >();
add< BtreeCursorTests::MultiRange >();
add< BtreeCursorTests::MultiRangeGap >();
add< BtreeCursorTests::MultiRangeReverse >();
}
};
} // namespace CursorTests