// perftest.cpp : Run db performance tests.
//
/**
* Copyright (C) 2009 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 .
*/
#include "stdafx.h"
#include "../../client/dbclient.h"
#include "../../db/instance.h"
#include "../../db/query.h"
#include "../../db/queryoptimizer.h"
#include "../../util/file_allocator.h"
#include "../framework.h"
#include
namespace mongo {
extern string dbpath;
} // namespace mongo
using namespace mongo;
using namespace mongo::regression;
DBClientBase *client_;
// Each test runs with a separate db, so no test does any of the startup
// (ie allocation) work for another test.
template< class T >
string testDb( T *t = 0 ) {
string name = mongo::regression::demangleName( typeid( T ) );
// Make filesystem safe.
for( string::iterator i = name.begin(); i != name.end(); ++i )
if ( *i == ':' )
*i = '_';
return name;
}
template< class T >
string testNs( T *t ) {
stringstream ss;
ss << testDb( t ) << ".perftest";
return ss.str();
}
template
class Runner {
public:
void run() {
T test;
string name = testDb( &test );
boost::posix_time::ptime start = boost::posix_time::microsec_clock::universal_time();
test.run();
boost::posix_time::ptime end = boost::posix_time::microsec_clock::universal_time();
long long micro = ( end - start ).total_microseconds();
cout << "{'" << name << "': "
<< micro / 1000000
<< "."
<< setw( 6 ) << setfill( '0' ) << micro % 1000000
<< "}" << endl;
}
~Runner() {
theFileAllocator().waitUntilFinished();
client_->dropDatabase( testDb< T >().c_str() );
}
};
class RunnerSuite : public Suite {
public:
RunnerSuite( string name ) : Suite( name ){}
protected:
template< class T >
void add() {
Suite::add< Runner< T > >();
}
};
namespace Insert {
class IdIndex {
public:
void run() {
string ns = testNs( this );
for( int i = 0; i < 100000; ++i ) {
client_->insert( ns.c_str(), BSON( "_id" << i ) );
}
}
};
class TwoIndex {
public:
TwoIndex() : ns_( testNs( this ) ) {
client_->ensureIndex( ns_, BSON( "_id" << 1 ), "my_id" );
}
void run() {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i ) );
}
string ns_;
};
class TenIndex {
public:
TenIndex() : ns_( testNs( this ) ) {
const char *names = "aaaaaaaaa";
for( int i = 0; i < 9; ++i ) {
client_->resetIndexCache();
client_->ensureIndex( ns_.c_str(), BSON( "_id" << 1 ), false, names + i );
}
}
void run() {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i ) );
}
string ns_;
};
class Capped {
public:
Capped() : ns_( testNs( this ) ) {
client_->createCollection( ns_.c_str(), 100000, true );
}
void run() {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i ) );
}
string ns_;
};
class OneIndexReverse {
public:
OneIndexReverse() : ns_( testNs( this ) ) {
client_->ensureIndex( ns_, BSON( "_id" << 1 ) );
}
void run() {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << ( 100000 - 1 - i ) ) );
}
string ns_;
};
class OneIndexHighLow {
public:
OneIndexHighLow() : ns_( testNs( this ) ) {
client_->ensureIndex( ns_, BSON( "_id" << 1 ) );
}
void run() {
for( int i = 0; i < 100000; ++i ) {
int j = 50000 + ( ( i % 2 == 0 ) ? 1 : -1 ) * ( i / 2 + 1 );
client_->insert( ns_.c_str(), BSON( "_id" << j ) );
}
}
string ns_;
};
class All : public RunnerSuite {
public:
All() : RunnerSuite( "insert" ){}
void setupTests(){
add< IdIndex >();
add< TwoIndex >();
add< TenIndex >();
add< Capped >();
add< OneIndexReverse >();
add< OneIndexHighLow >();
}
} all;
} // namespace Insert
namespace Update {
class Smaller {
public:
Smaller() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i << "b" << 2 ) );
}
void run() {
for( int i = 0; i < 100000; ++i )
client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "_id" << i ) );
}
string ns_;
};
class Bigger {
public:
Bigger() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i ) );
}
void run() {
for( int i = 0; i < 100000; ++i )
client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "_id" << i << "b" << 2 ) );
}
string ns_;
};
class Inc {
public:
Inc() : ns_( testNs( this ) ) {
for( int i = 0; i < 10000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i << "i" << 0 ) );
}
void run() {
for( int j = 0; j < 10; ++j )
for( int i = 0; i < 10000; ++i )
client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "$inc" << BSON( "i" << 1 ) ) );
}
string ns_;
};
class Set {
public:
Set() : ns_( testNs( this ) ) {
for( int i = 0; i < 10000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i << "i" << 0 ) );
}
void run() {
for( int j = 1; j < 11; ++j )
for( int i = 0; i < 10000; ++i )
client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "$set" << BSON( "i" << j ) ) );
}
string ns_;
};
class SetGrow {
public:
SetGrow() : ns_( testNs( this ) ) {
for( int i = 0; i < 10000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i << "i" << "" ) );
}
void run() {
for( int j = 9; j > -1; --j )
for( int i = 0; i < 10000; ++i )
client_->update( ns_.c_str(), QUERY( "_id" << i ), BSON( "$set" << BSON( "i" << "aaaaaaaaaa"[j] ) ) );
}
string ns_;
};
class All : public RunnerSuite {
public:
All() : RunnerSuite( "update" ){}
void setupTests(){
add< Smaller >();
add< Bigger >();
add< Inc >();
add< Set >();
add< SetGrow >();
}
} all;
} // namespace Update
namespace BSON {
const char *sample =
"{\"one\":2, \"two\":5, \"three\": {},"
"\"four\": { \"five\": { \"six\" : 11 } },"
"\"seven\": [ \"a\", \"bb\", \"ccc\", 5 ],"
"\"eight\": Dbref( \"rrr\", \"01234567890123456789aaaa\" ),"
"\"_id\": ObjectId( \"deadbeefdeadbeefdeadbeef\" ),"
"\"nine\": { \"$binary\": \"abc=\", \"$type\": \"02\" },"
"\"ten\": Date( 44 ), \"eleven\": /foooooo/i }";
const char *shopwikiSample =
"{ '_id' : '289780-80f85380b5c1d4a0ad75d1217673a4a2' , 'site_id' : 289780 , 'title'"
": 'Jubilee - Margaret Walker' , 'image_url' : 'http://www.heartlanddigsandfinds.c"
"om/store/graphics/Product_Graphics/Product_8679.jpg' , 'url' : 'http://www.heartla"
"nddigsandfinds.com/store/store_product_detail.cfm?Product_ID=8679&Category_ID=2&Su"
"b_Category_ID=910' , 'url_hash' : 3450626119933116345 , 'last_update' : null , '"
"features' : { '$imagePrefetchDate' : '2008Aug30 22:39' , '$image.color.rgb' : '5a7"
"574' , 'Price' : '$10.99' , 'Description' : 'Author--s 1st Novel. A Houghton Miffl"
"in Literary Fellowship Award novel by the esteemed poet and novelist who has demon"
"strated a lifelong commitment to the heritage of black culture. An acclaimed story"
"of Vyry, a negro slave during the 19th Century, facing the biggest challenge of h"
"er lifetime - that of gaining her freedom, fighting for all the things she had nev"
"er known before. The author, great-granddaughter of Vyry, reveals what the Civil W"
"ar in America meant to the Negroes. Slavery W' , '$priceHistory-1' : '2008Dec03 $1"
"0.99' , 'Brand' : 'Walker' , '$brands_in_title' : 'Walker' , '--path' : '//HTML[1]"
"/BODY[1]/TABLE[1]/TR[1]/TD[1]/P[1]/TABLE[1]/TR[1]/TD[1]/TABLE[1]/TR[2]/TD[2]/TABLE"
"[1]/TR[1]/TD[1]/P[1]/TABLE[1]/TR[1]' , '~location' : 'en_US' , '$crawled' : '2009J"
"an11 03:22' , '$priceHistory-2' : '2008Nov15 $10.99' , '$priceHistory-0' : '2008De"
"c24 $10.99'}}";
class Parse {
public:
void run() {
for( int i = 0; i < 10000; ++i )
fromjson( sample );
}
};
class ShopwikiParse {
public:
void run() {
for( int i = 0; i < 10000; ++i )
fromjson( shopwikiSample );
}
};
class Json {
public:
Json() : o_( fromjson( sample ) ) {}
void run() {
for( int i = 0; i < 10000; ++i )
o_.jsonString();
}
BSONObj o_;
};
class ShopwikiJson {
public:
ShopwikiJson() : o_( fromjson( shopwikiSample ) ) {}
void run() {
for( int i = 0; i < 10000; ++i )
o_.jsonString();
}
BSONObj o_;
};
class All : public RunnerSuite {
public:
All() : RunnerSuite( "bson" ){}
void setupTests(){
add< Parse >();
add< ShopwikiParse >();
add< Json >();
add< ShopwikiJson >();
}
} all;
} // namespace BSON
namespace Index {
class Int {
public:
Int() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "a" << i ) );
}
void run() {
client_->ensureIndex( ns_, BSON( "a" << 1 ) );
}
string ns_;
};
class ObjectId {
public:
ObjectId() : ns_( testNs( this ) ) {
OID id;
for( int i = 0; i < 100000; ++i ) {
id.init();
client_->insert( ns_.c_str(), BSON( "a" << id ) );
}
}
void run() {
client_->ensureIndex( ns_, BSON( "a" << 1 ) );
}
string ns_;
};
class String {
public:
String() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i ) {
stringstream ss;
ss << i;
client_->insert( ns_.c_str(), BSON( "a" << ss.str() ) );
}
}
void run() {
client_->ensureIndex( ns_, BSON( "a" << 1 ) );
}
string ns_;
};
class Object {
public:
Object() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i ) {
client_->insert( ns_.c_str(), BSON( "a" << BSON( "a" << i ) ) );
}
}
void run() {
client_->ensureIndex( ns_, BSON( "a" << 1 ) );
}
string ns_;
};
class All : public RunnerSuite {
public:
All() : RunnerSuite( "index" ){}
void setupTests(){
add< Int >();
add< ObjectId >();
add< String >();
add< Object >();
}
} all;
} // namespace Index
namespace QueryTests {
class NoMatch {
public:
NoMatch() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i ) );
}
void run() {
client_->findOne( ns_.c_str(), QUERY( "_id" << 100000 ) );
}
string ns_;
};
class NoMatchIndex {
public:
NoMatchIndex() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i ) );
}
void run() {
client_->findOne( ns_.c_str(),
QUERY( "a" << "b" ).hint( BSON( "_id" << 1 ) ) );
}
string ns_;
};
class NoMatchLong {
public:
NoMatchLong() : ns_( testNs( this ) ) {
const char *names = "aaaaaaaaaa";
for( int i = 0; i < 100000; ++i ) {
BSONObjBuilder b;
for( int j = 0; j < 10; ++j )
b << ( names + j ) << i;
client_->insert( ns_.c_str(), b.obj() );
}
}
void run() {
client_->findOne( ns_.c_str(), QUERY( "a" << 100000 ) );
}
string ns_;
};
class SortOrdered {
public:
SortOrdered() : ns_( testNs( this ) ) {
for( int i = 0; i < 50000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << i ) );
}
void run() {
auto_ptr< DBClientCursor > c =
client_->query( ns_.c_str(), Query( BSONObj() ).sort( BSON( "_id" << 1 ) ) );
int i = 0;
for( ; c->more(); c->nextSafe(), ++i );
ASSERT_EQUALS( 50000, i );
}
string ns_;
};
class SortReverse {
public:
SortReverse() : ns_( testNs( this ) ) {
for( int i = 0; i < 50000; ++i )
client_->insert( ns_.c_str(), BSON( "_id" << ( 50000 - 1 - i ) ) );
}
void run() {
auto_ptr< DBClientCursor > c =
client_->query( ns_.c_str(), Query( BSONObj() ).sort( BSON( "_id" << 1 ) ) );
int i = 0;
for( ; c->more(); c->nextSafe(), ++i );
ASSERT_EQUALS( 50000, i );
}
string ns_;
};
class GetMore {
public:
GetMore() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "a" << i ) );
c_ = client_->query( ns_.c_str(), Query() );
}
void run() {
int i = 0;
for( ; c_->more(); c_->nextSafe(), ++i );
ASSERT_EQUALS( 100000, i );
}
string ns_;
auto_ptr< DBClientCursor > c_;
};
class GetMoreIndex {
public:
GetMoreIndex() : ns_( testNs( this ) ) {
for( int i = 0; i < 100000; ++i )
client_->insert( ns_.c_str(), BSON( "a" << i ) );
client_->ensureIndex( ns_, BSON( "a" << 1 ) );
c_ = client_->query( ns_.c_str(), QUERY( "a" << GT << -1 ).hint( BSON( "a" << 1 ) ) );
}
void run() {
int i = 0;
for( ; c_->more(); c_->nextSafe(), ++i );
ASSERT_EQUALS( 100000, i );
}
string ns_;
auto_ptr< DBClientCursor > c_;
};
class GetMoreKeyMatchHelps {
public:
GetMoreKeyMatchHelps() : ns_( testNs( this ) ) {
for( int i = 0; i < 1000000; ++i )
client_->insert( ns_.c_str(), BSON( "a" << i << "b" << i % 10 << "c" << "d" ) );
client_->ensureIndex( ns_, BSON( "a" << 1 << "b" << 1 ) );
c_ = client_->query( ns_.c_str(), QUERY( "a" << GT << -1 << "b" << 0 ).hint( BSON( "a" << 1 << "b" << 1 ) ) );
}
void run() {
int i = 0;
for( ; c_->more(); c_->nextSafe(), ++i );
ASSERT_EQUALS( 100000, i );
}
string ns_;
auto_ptr< DBClientCursor > c_;
};
class All : public RunnerSuite {
public:
All() : RunnerSuite( "query" ){}
void setupTests(){
add< NoMatch >();
add< NoMatchIndex >();
add< NoMatchLong >();
add< SortOrdered >();
add< SortReverse >();
add< GetMore >();
add< GetMoreIndex >();
add< GetMoreKeyMatchHelps >();
}
} all;
} // namespace QueryTests
namespace Count {
class Count {
public:
Count() : ns_( testNs( this ) ) {
BSONObj obj = BSON( "a" << 1 );
for( int i = 0; i < 100000; ++i )
client_->insert( ns_, obj );
}
void run() {
ASSERT_EQUALS( 100000U, client_->count( ns_, BSON( "a" << 1 ) ) );
}
string ns_;
};
class CountIndex {
public:
CountIndex() : ns_( testNs( this ) ) {
BSONObj obj = BSON( "a" << 1 );
for( int i = 0; i < 100000; ++i )
client_->insert( ns_, obj );
client_->ensureIndex( ns_, obj );
}
void run() {
// 'simple' match does not work for numbers
ASSERT_EQUALS( 100000U, client_->count( ns_, BSON( "a" << 1 ) ) );
}
string ns_;
};
class CountSimpleIndex {
public:
CountSimpleIndex() : ns_( testNs( this ) ) {
BSONObj obj = BSON( "a" << "b" );
for( int i = 0; i < 100000; ++i )
client_->insert( ns_, obj );
client_->ensureIndex( ns_, obj );
}
void run() {
ASSERT_EQUALS( 100000U, client_->count( ns_, BSON( "a" << "b" ) ) );
}
string ns_;
};
class All : public RunnerSuite {
public:
All() : RunnerSuite( "count" ){}
void setupTests(){
add< Count >();
add< CountIndex >();
add< CountSimpleIndex >();
}
} all;
} // namespace Count
namespace Plan {
class Hint {
public:
Hint() : ns_( testNs( this ) ) {
const char *names = "aaaaaaaaa";
for( int i = 0; i < 9; ++i ) {
client_->resetIndexCache();
client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
}
lk_.reset( new dblock );
setClient( ns_.c_str() );
hint_ = BSON( "hint" << BSON( "a" << 1 ) );
hintElt_ = hint_.firstElement();
}
void run() {
for( int i = 0; i < 10000; ++i )
QueryPlanSet s( ns_.c_str(), BSONObj(), BSONObj(), &hintElt_ );
}
string ns_;
auto_ptr< dblock > lk_;
BSONObj hint_;
BSONElement hintElt_;
};
class Sort {
public:
Sort() : ns_( testNs( this ) ) {
const char *names = "aaaaaaaaaa";
for( int i = 0; i < 10; ++i ) {
client_->resetIndexCache();
client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
}
lk_.reset( new dblock );
setClient( ns_.c_str() );
}
void run() {
for( int i = 0; i < 10000; ++i )
QueryPlanSet s( ns_.c_str(), BSONObj(), BSON( "a" << 1 ) );
}
string ns_;
auto_ptr< dblock > lk_;
};
class Query {
public:
Query() : ns_( testNs( this ) ) {
const char *names = "aaaaaaaaaa";
for( int i = 0; i < 10; ++i ) {
client_->resetIndexCache();
client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
}
lk_.reset( new dblock );
setClient( ns_.c_str() );
}
void run() {
for( int i = 0; i < 10000; ++i )
QueryPlanSet s( ns_.c_str(), BSON( "a" << 1 ), BSONObj() );
}
string ns_;
auto_ptr< dblock > lk_;
};
class All : public RunnerSuite {
public:
All() : RunnerSuite("plan" ){}
void setupTests(){
add< Hint >();
add< Sort >();
add< Query >();
}
} all;
} // namespace Plan
int main( int argc, char **argv ) {
printGitVersion();
printSysInfo();
logLevel = -1;
boost::filesystem::path p( "/data/db/perftest" );
if ( boost::filesystem::exists( p ) )
boost::filesystem::remove_all( p );
boost::filesystem::create_directory( p );
string dbpathString = p.native_directory_string();
dbpath = dbpathString.c_str();
theFileAllocator().start();
client_ = new DBDirectClient();
//return Suite::run( argc, argv ); // MIKE FIX
return 0;
}