0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/s/shardkey.cpp
Mathias Stearn 244b162111 Use intrusive_ptr rather than shared_ptr for BSONObj
Advantages:
* Owned BSONObj now uses 1 heap allocation rather than 3
* sizeof(BSONObj) is now 16 rather than 24 (on 64 bit)
* perftest bson shows it to be much faster

Change in BSONObj constructor API for owned case to make sure old code gets
updated to allocate room for the ref count. Anyone using BSONObjBuilder doesn't
need to worry about this change.
2011-04-03 20:50:45 -04:00

271 lines
9.2 KiB
C++

// shardkey.cpp
/**
* Copyright (C) 2008 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/>.
*/
#include "pch.h"
#include "chunk.h"
#include "../db/jsobj.h"
#include "../util/unittest.h"
#include "../util/timer.h"
namespace mongo {
ShardKeyPattern::ShardKeyPattern( BSONObj p ) : pattern( p.getOwned() ) {
pattern.getFieldNames(patternfields);
BSONObjBuilder min;
BSONObjBuilder max;
BSONObjIterator it(p);
while (it.more()) {
BSONElement e (it.next());
min.appendMinKey(e.fieldName());
max.appendMaxKey(e.fieldName());
}
gMin = min.obj();
gMax = max.obj();
}
int ShardKeyPattern::compare( const BSONObj& lObject , const BSONObj& rObject ) const {
BSONObj L = extractKey(lObject);
uassert( 10198 , "left object doesn't have full shard key", L.nFields() == (int)patternfields.size());
BSONObj R = extractKey(rObject);
uassert( 10199 , "right object doesn't have full shard key", R.nFields() == (int)patternfields.size());
return L.woCompare(R);
}
bool ShardKeyPattern::hasShardKey( const BSONObj& obj ) const {
/* this is written s.t. if obj has lots of fields, if the shard key fields are early,
it is fast. so a bit more work to try to be semi-fast.
*/
for(set<string>::const_iterator it = patternfields.begin(); it != patternfields.end(); ++it) {
if(obj.getFieldDotted(it->c_str()).eoo())
return false;
}
return true;
}
bool ShardKeyPattern::isPrefixOf( const BSONObj& otherPattern ) const {
BSONObjIterator a( pattern );
BSONObjIterator b( otherPattern );
while ( a.more() && b.more() ) {
BSONElement x = a.next();
BSONElement y = b.next();
if ( strcmp( x.fieldName() , y.fieldName() ) )
return false;
}
return ! a.more();
}
string ShardKeyPattern::toString() const {
return pattern.toString();
}
BSONObj ShardKeyPattern::moveToFront(const BSONObj& obj) const {
vector<const char*> keysToMove;
keysToMove.push_back("_id");
BSONForEach(e, pattern) {
if (strchr(e.fieldName(), '.') == NULL)
keysToMove.push_back(e.fieldName());
}
if (keysToMove.size() == 1) {
return obj;
}
else {
BufBuilder buf (obj.objsize());
buf.appendNum((unsigned)0); // refcount
buf.appendNum(obj.objsize());
vector<pair<const char*, size_t> > copies;
pair<const char*, size_t> toCopy ((const char*)NULL, 0); // C++ NULL isn't a pointer type yet
BSONForEach(e, obj) {
bool moveToFront = false;
for (vector<const char*>::const_iterator it(keysToMove.begin()), end(keysToMove.end()); it!=end; ++it) {
if (strcmp(e.fieldName(), *it) == 0) {
moveToFront = true;
break;
}
}
if (moveToFront) {
buf.appendBuf(e.fieldName()-1, e.size());
if (toCopy.first) {
copies.push_back(toCopy);
toCopy.first = NULL;
}
}
else {
if (!toCopy.first) {
toCopy.first = e.fieldName()-1;
toCopy.second = e.size();
}
else {
toCopy.second += e.size();
}
}
}
for (vector<pair<const char*, size_t> >::const_iterator it(copies.begin()), end(copies.end()); it!=end; ++it) {
buf.appendBuf(it->first, it->second);
}
if (toCopy.first) {
buf.appendBuf(toCopy.first, toCopy.second);
}
buf.appendChar('\0');
BSONObj out ((BSONObj::Holder*)buf.buf());
buf.decouple();
return out;
}
}
/* things to test for compound :
\ middle (deprecating?)
*/
class ShardKeyUnitTest : public UnitTest {
public:
void testIsPrefixOf() {
{
ShardKeyPattern k( BSON( "x" << 1 ) );
assert( ! k.isPrefixOf( BSON( "a" << 1 ) ) );
assert( k.isPrefixOf( BSON( "x" << 1 ) ) );
assert( k.isPrefixOf( BSON( "x" << 1 << "a" << 1 ) ) );
assert( ! k.isPrefixOf( BSON( "a" << 1 << "x" << 1 ) ) );
}
{
ShardKeyPattern k( BSON( "x" << 1 << "y" << 1 ) );
assert( ! k.isPrefixOf( BSON( "x" << 1 ) ) );
assert( ! k.isPrefixOf( BSON( "x" << 1 << "z" << 1 ) ) );
assert( k.isPrefixOf( BSON( "x" << 1 << "y" << 1 ) ) );
assert( k.isPrefixOf( BSON( "x" << 1 << "y" << 1 << "z" << 1 ) ) );
}
}
void hasshardkeytest() {
BSONObj x = fromjson("{ zid : \"abcdefg\", num: 1.0, name: \"eliot\" }");
ShardKeyPattern k( BSON( "num" << 1 ) );
assert( k.hasShardKey(x) );
assert( !k.hasShardKey( fromjson("{foo:'a'}") ) );
// try compound key
{
ShardKeyPattern k( fromjson("{a:1,b:-1,c:1}") );
assert( k.hasShardKey( fromjson("{foo:'a',a:'b',c:'z',b:9,k:99}") ) );
assert( !k.hasShardKey( fromjson("{foo:'a',a:'b',c:'z',bb:9,k:99}") ) );
assert( !k.hasShardKey( fromjson("{k:99}") ) );
}
}
void extractkeytest() {
ShardKeyPattern k( fromjson("{a:1,'sub.b':-1,'sub.c':1}") );
BSONObj x = fromjson("{a:1,'sub.b':2,'sub.c':3}");
assert( k.extractKey( fromjson("{a:1,sub:{b:2,c:3}}") ).woEqual(x) );
assert( k.extractKey( fromjson("{sub:{b:2,c:3},a:1}") ).woEqual(x) );
}
void moveToFrontTest() {
ShardKeyPattern sk (BSON("a" << 1 << "b" << 1));
BSONObj ret;
ret = sk.moveToFront(BSON("z" << 1 << "_id" << 1 << "y" << 1 << "a" << 1 << "x" << 1 << "b" << 1 << "w" << 1));
assert(ret.woEqual(BSON("_id" << 1 << "a" << 1 << "b" << 1 << "z" << 1 << "y" << 1 << "x" << 1 << "w" << 1)));
ret = sk.moveToFront(BSON("_id" << 1 << "a" << 1 << "b" << 1 << "z" << 1 << "y" << 1 << "x" << 1 << "w" << 1));
assert(ret.woEqual(BSON("_id" << 1 << "a" << 1 << "b" << 1 << "z" << 1 << "y" << 1 << "x" << 1 << "w" << 1)));
ret = sk.moveToFront(BSON("z" << 1 << "y" << 1 << "a" << 1 << "b" << 1 << "Z" << 1 << "Y" << 1));
assert(ret.woEqual(BSON("a" << 1 << "b" << 1 << "z" << 1 << "y" << 1 << "Z" << 1 << "Y" << 1)));
}
void moveToFrontBenchmark(int numFields) {
BSONObjBuilder bb;
bb.append("_id", 1);
for (int i=0; i < numFields; i++)
bb.append(BSONObjBuilder::numStr(i), 1);
bb.append("key", 1);
BSONObj o = bb.obj();
ShardKeyPattern sk (BSON("key" << 1));
Timer t;
const int iterations = 100*1000;
for (int i=0; i< iterations; i++) {
sk.moveToFront(o);
}
const double secs = t.micros() / 1000000.0;
const double ops_per_sec = iterations / secs;
cout << "moveToFront (" << numFields << " fields) secs: " << secs << " ops_per_sec: " << ops_per_sec << endl;
}
void run() {
extractkeytest();
ShardKeyPattern k( BSON( "key" << 1 ) );
BSONObj min = k.globalMin();
// cout << min.jsonString(TenGen) << endl;
BSONObj max = k.globalMax();
BSONObj k1 = BSON( "key" << 5 );
assert( k.compare( min , max ) < 0 );
assert( k.compare( min , k1 ) < 0 );
assert( k.compare( max , min ) > 0 );
assert( k.compare( min , min ) == 0 );
hasshardkeytest();
assert( k.hasShardKey( k1 ) );
assert( ! k.hasShardKey( BSON( "key2" << 1 ) ) );
BSONObj a = k1;
BSONObj b = BSON( "key" << 999 );
assert( k.compare(a,b) < 0 );
testIsPrefixOf();
// add middle multitype tests
moveToFrontTest();
if (0) { // toggle to run benchmark
moveToFrontBenchmark(0);
moveToFrontBenchmark(10);
moveToFrontBenchmark(100);
}
log(1) << "shardKeyTest passed" << endl;
}
} shardKeyTest;
} // namespace mongo