Compare commits

...

55 Commits

Author SHA1 Message Date
Dan Pasette
3e52c1a73c BUMP 2.4.2 2013-04-16 15:21:23 -04:00
Eric Milkie
0ec9eca507 post 2.4.2-rc0 2013-04-11 09:14:52 -04:00
Dan Pasette
72acf70c11 BUMP 2.4.2-rc0 2013-04-10 15:37:09 -04:00
Greg Studer
c6ec08e021 SERVER-9312 buildbot sharding_migrateBigObject.js longer timeout for slower machines migrating 2013-04-10 15:30:58 -04:00
Alberto Lerner
ab76660a75 SERVER-7271 Improve error reporting when migrate commit fails. 2013-04-09 16:02:19 -04:00
Alberto Lerner
6edf4a7f08 SERVER-9125 Prevent cursor time out in the case of very slow config servers 2013-04-09 12:51:21 -04:00
Alberto Lerner
9f073b6cae SERVER-9125 Fixed batch size computation if first doc is large. 2013-04-09 12:51:20 -04:00
Eric Milkie
cde1ad8f45 SERVER-9286 lowering nested depth
This avoids a stack overflow on OS X; full fix in SERVER-9286
2013-04-09 09:45:41 -04:00
Alberto Lerner
a3dae84ade SERVER-9125 Fix memory ownership during large collection copy 2013-04-08 19:38:14 -04:00
Mathias Stearn
1a62390256 SERVER-9267 Make readonly v8 objects use same getters as regular lazy objects
Also fixes SERVER-9251
2013-04-05 22:23:47 -04:00
Mathias Stearn
219986d5ca Speed up conversion of v8::Arrays to BSON
Two major improvements:
* Use BSONObjBuilder::numStr() to convert index to string (has global
     cache of strings for 0-99)
* Use indexed rather than named iteration over the v8::Array
2013-04-05 22:23:39 -04:00
Mathias Stearn
aefcffa124 Use indexed rather than named Set() when converting to v8::Array
The meat of this change is in the Array case of mongoToV8Element.
mongoToV8() was removed since this was the only caller.

Conflicts:
	src/mongo/scripting/engine_v8.cpp
2013-04-05 22:21:47 -04:00
Mathias Stearn
ee21ceb8d4 SERVER-9230: fix issue causing persistent v8 objects not to be tracked 2013-04-03 21:55:46 -04:00
Hari Khalsa
9a846d8688 SERVER-9228 don't return dups when s2cursor yields, delegate to underlying btreecursor 2013-04-03 21:52:53 -04:00
Eric Milkie
04707f7b39 SERVER-9229 wait for async deletes to finish before balancer stops, to avoid test failure 2013-04-03 15:15:38 -04:00
Dan Pasette
81212176da SERVER-7271 - Fix compile error with ScopedDbConnection 2013-04-02 13:20:05 -04:00
Ben Becker
822840b180 SERVER-9213: remove v8 ResourceConstraints 2013-04-02 10:19:03 -07:00
Spencer T Brody
64ccdfa2f6 SERVER-9054 Update db.help() message for addUser 2013-04-02 11:31:49 -04:00
Alberto Lerner
3bc9328524 SERVER-9125 Copy collections faster in the config upgrade procedure 2013-04-02 10:25:32 -04:00
Alberto Lerner
4a472d8df3 SERVER-7271 Do not exit if a transient config server error aborts a migration.
Conflicts:

	src/mongo/client/distlock.cpp
2013-04-02 10:25:07 -04:00
Randolph Tan
0502e6aada SERVER-9174 Race condition on read_pref_rs_client.js after reconfig
Replaced js test with cpp unit test that does not rely on reconfig.
2013-04-01 19:17:36 -04:00
Ben Becker
f1017ee044 SERVER-9066: avoid duplicate JS field names 2013-04-01 14:43:56 -07:00
Ben Becker
9e05b382ca Reverting SERVER-9186 due to test failures
Revert "SERVER-9186: avoid storing properties on js objects in the getter interceptor"

This reverts commit 337b300277.
2013-03-30 07:41:53 -07:00
Ben Becker
f00104e92f SERVER-9124: Avoid raw pointers for SM's nativeHelper 2013-03-29 17:23:18 -07:00
Ben Becker
e4890bf85c SERVER-9185: add v8 GC epligoue stats 2013-03-29 16:06:30 -07:00
Ben Becker
337b300277 SERVER-9186: avoid storing properties on js objects in the getter interceptor 2013-03-29 16:06:15 -07:00
Jason Rassi
3bf409d4e5 SERVER-9182 Allow restores of dumps containing edited .metadata.json files 2013-03-29 18:48:39 -04:00
Scott Hernandez
198c577fe4 SERVER-9053: ttl: don't delete on non-primaries 2013-03-29 17:32:42 -04:00
Andrew Morrow
85fc27444f SERVER-9047 Don't limit line length when reading numa_maps 2013-03-29 14:49:17 -04:00
Shaun Verch
9ba71622c9 SERVER-9082 Accept any valid value in id field of dbref 2013-03-29 11:38:07 -04:00
Shaun Verch
ffe9de8df3 SERVER-8819 Framework for testing round trip and test roundtrip of DBRef and DBPointer 2013-03-29 11:37:59 -04:00
Shaun Verch
2e87ed15f0 SERVER-8819 Add to falsy values test and do some cleanup in jstests.cpp 2013-03-29 11:37:48 -04:00
Greg Studer
6d7ac05ea2 SERVER-9139 test cursor cleanup in mongos 2013-03-29 10:34:21 -04:00
Greg Studer
bca711f26f SERVER-9139 clean up unsharded cursors in CursorCache 2013-03-29 10:33:56 -04:00
Dan Pasette
9412afa6f3 SERVER-9003 - Display replica set chaining topology in rs.status()
Include syncingTo in heartbeat response and display this info for each
replica set member in the replSetGetStatus command.
2013-03-28 19:35:01 -04:00
Andy Schwerin
7f45f0fe14 SERVER-8983 Include AuthenticationFailed error code in failed MONGODB-CR authentications. 2013-03-28 19:11:38 -04:00
Eric Milkie
4f26c3137e SERVER-9085 fix units in tsToSeconds calculation 2013-03-28 19:11:06 -04:00
Andy Schwerin
6f677a8150 SERVER-9111 Do not try to find user documents in $CLUSTER or $SERVER pseudodatabases. 2013-03-28 19:10:31 -04:00
Jason Rassi
719d6d8207 SERVER-8999 Read "indexPrefix" correctly in FTSCommand::_run() 2013-03-28 19:09:39 -04:00
Andy Schwerin
fde9ff094d SERVER-9014 Synchronize access to CmdGetNonce::_random. 2013-03-28 19:08:19 -04:00
Scott Hernandez
27b3840145 SERVER-9027: allow dbref shard keys 2013-03-28 18:42:05 -04:00
Hari Khalsa
033f58e7d5 SERVER-9062 don't rely on drem to normalize longitude -- explicitly reject OOB values 2013-03-28 18:41:26 -04:00
Ben Becker
f9289144fc SERVER-9088: ensure printjson() doesn't attempt to access DB global 2013-03-28 18:39:34 -04:00
Ben Becker
7ec1528ae3 SERVER-8799: handle symbol BSON type as string in v8 2013-03-28 18:36:12 -04:00
Randolph Tan
182955c65b SERVER-8720 Memory leak in DBClientReplicaSet::slaveConn 2013-03-27 17:18:39 -04:00
Spencer T Brody
d54170cb2b SERVER-9029 Add more informative log message when building unique index on system.users fails 2013-03-25 18:00:39 -04:00
Scott Hernandez
ab32409b04 SERVER-8984: gle c++ client helper should check for command failure 2013-03-25 17:38:07 -04:00
Eliot Horowitz
a16001b741 prep 2.4.2-pre- 2013-03-22 15:56:32 -04:00
Eliot Horowitz
8301275677 SERVER-9087: fix initial sync ignoring updates during index creation 2013-03-22 15:56:28 -04:00
Spencer T Brody
4ffa043511 SERVER-9061 Fix mongostat segfault when using auth with multiple hosts 2013-03-21 18:02:02 -04:00
Spencer T Brody
656008683a SERVER-9050 GLE with w:1 on config servers shouldn't report "norepl" error 2013-03-21 18:01:42 -04:00
Greg Studer
1b8a702436 SERVER-6991 allow releasing sharded conn back to pool after read operation 2013-03-19 16:40:59 -04:00
Greg Studer
1dc86f438b SERVER-6991 better stats for sharded connections and sharded conn pool 2013-03-19 16:40:52 -04:00
Dan Pasette
b74d1bb85d post 2.4.0 2013-03-18 10:29:02 -04:00
Ernie Hershey
bafb7708c6 update logic for including new man pages to >=2.4.0, not >2.4.0 2013-03-17 23:32:26 +00:00
61 changed files with 1684 additions and 794 deletions

View File

@@ -924,9 +924,9 @@ fi
%{_mandir}/man1/mongostat.1*
# FIXME: uncomment when mongosniff is back in the package
#%{_mandir}/man1/mongosniff.1*
#@@VERSION>2.4.0@@%{_mandir}/man1/mongotop.1*
#@@VERSION>2.4.0@@%{_mandir}/man1/mongoperf.1*
#@@VERSION>2.4.0@@%{_mandir}/man1/mongooplog.1*
#@@VERSION>=2.4.0@@%{_mandir}/man1/mongotop.1*
#@@VERSION>=2.4.0@@%{_mandir}/man1/mongoperf.1*
#@@VERSION>=2.4.0@@%{_mandir}/man1/mongooplog.1*
%files server
%defattr(-,root,root,-)

View File

@@ -3,7 +3,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = MongoDB
PROJECT_NUMBER = 2.4.0
PROJECT_NUMBER = 2.4.2
OUTPUT_DIRECTORY = docs/doxygen
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English

View File

@@ -18,3 +18,6 @@ assert.eq( [ 1 ], queryIDS( t, "foo" , { x : 1 } ) );
res = t.runCommand( "text", { search : "foo" , filter : { x : 1 } } );
assert( res.results[0].score > 0, tojson( res ) )
// repeat search with "language" specified, SERVER-8999
res = t.runCommand( "text", { search : "foo" , filter : { x : 1 } , language : "english" } );
assert( res.results[0].score > 0, tojson( res ) )

View File

@@ -5,20 +5,20 @@ roundworldpoint = { "type" : "Point", "coordinates": [ 180, 0 ] }
// Opposite the equator
roundworld = { "type" : "Polygon",
"coordinates" : [ [ [179,1], [181,1], [181,-1], [179,-1], [179,1]]]}
"coordinates" : [ [ [179,1], [-179,1], [-179,-1], [179,-1], [179,1]]]}
t.insert({geo : roundworld})
roundworld2 = { "type" : "Polygon",
"coordinates" : [ [ [179,1], [179,-1], [181,-1], [181,1], [179,1]]]}
"coordinates" : [ [ [179,1], [179,-1], [-179,-1], [-179,1], [179,1]]]}
t.insert({geo : roundworld2})
// North pole
santapoint = { "type" : "Point", "coordinates": [ 180, 90 ] }
santa = { "type" : "Polygon",
"coordinates" : [ [ [179,89], [179,90], [181,90], [181,89], [179,89]]]}
"coordinates" : [ [ [179,89], [179,90], [-179,90], [-179,89], [179,89]]]}
t.insert({geo : santa})
santa2 = { "type" : "Polygon",
"coordinates" : [ [ [179,89], [181,89], [181,90], [179,90], [179,89]]]}
"coordinates" : [ [ [179,89], [-179,89], [-179,90], [179,90], [179,89]]]}
t.insert({geo : santa2})
// South pole

View File

@@ -59,15 +59,25 @@ function uniformPoints(origin, count, minDist, maxDist){
var pointLat = asin((sin(lat) * cos(distance)) + (cos(lat) * sin(distance) * cos(angle)));
var pointDLng = atan2(sin(angle) * sin(distance) * cos(lat), cos(distance) - sin(lat) * sin(pointLat));
var pointLng = ((lng - pointDLng + PI) % 2*PI) - PI;
// Latitude must be [-90, 90]
var newLat = lat + pointLat;
if (newLat > 90) newLat -= 180;
if (newLat < -90) newLat += 180;
// Longitude must be [-180, 180]
var newLng = lng + pointLng;
if (newLng > 180) newLng -= 360;
if (newLng < -180) newLng += 360;
var newPoint = {
geo: {
type: "Point",
coordinates: [lng + pointLng, lat + pointLat]
//coordinates: [lng + pointLng, lat + pointLat]
coordinates: [newLng, newLat]
}
};
if(lat + pointLat > 90.0){
continue;
}
points.push(newPoint);
}
for(i=0; i < points.length; i++){

View File

@@ -14,7 +14,7 @@ t = db.objNestTest;
t.drop();
t.ensureIndex({a:1});
nestedObj = makeNestObj(500);
nestedObj = makeNestObj(300);
t.insert( { tst : "test1", a : nestedObj }, true );
t.insert( { tst : "test2", a : nestedObj }, true );

View File

@@ -0,0 +1,62 @@
//
// Tests cleanup of sharded and unsharded cursors
//
var st = new ShardingTest({ shards : 2, mongos : 1, other : { separateConfig : true } });
st.stopBalancer();
var mongos = st.s0;
var admin = mongos.getDB( "admin" );
var config = mongos.getDB( "config" );
var shards = config.shards.find().toArray();
var coll = mongos.getCollection( "foo.bar" );
var collUnsharded = mongos.getCollection( "foo.baz" );
// Shard collection
printjson(admin.runCommand({ enableSharding : coll.getDB() + "" }));
printjson(admin.runCommand({ movePrimary : coll.getDB() + "", to : shards[0]._id }));
printjson(admin.runCommand({ shardCollection : coll + "", key : { _id : 1 } }));
printjson(admin.runCommand({ split : coll + "", middle : { _id : 0 } }));
printjson(admin.runCommand({ moveChunk : coll + "", find : { _id : 0 }, to : shards[1]._id }));
jsTest.log("Collection set up...");
st.printShardingStatus(true);
jsTest.log("Insert enough data to overwhelm a query batch.");
for (var i = -150; i < 150; i++) {
coll.insert({ _id : i });
collUnsharded.insert({ _id : i });
}
assert.eq(null, coll.getDB().getLastError());
jsTest.log("Open a cursor to a sharded and unsharded collection.");
var shardedCursor = coll.find();
assert.neq(null, shardedCursor.next());
var unshardedCursor = collUnsharded.find();
assert.neq(null, unshardedCursor.next());
jsTest.log("Check whether the cursor is registered in the cursor info.");
var cursorInfo = admin.runCommand({ cursorInfo : true });
printjson(cursorInfo);
assert.eq(cursorInfo.sharded, 1);
assert.eq(cursorInfo.refs, 1);
jsTest.log("End the cursors.");
shardedCursor.itcount();
unshardedCursor.itcount();
var cursorInfo = admin.runCommand({ cursorInfo : true });
printjson(cursorInfo);
assert.eq(cursorInfo.sharded, 0);
assert.eq(cursorInfo.refs, 0);
jsTest.log("DONE!");
st.stop();

View File

@@ -10,6 +10,14 @@ function writeToConfigTest(){
var gleObj = confDB.runCommand({ getLastError: 1, w: 'majority' });
assert( gleObj.ok );
assert.eq("norepl", gleObj.err);
// w:1 should still work
confDB.settings.update({ _id: 'balancer' }, { $set: { stopped: true }});
var gleObj = confDB.runCommand({ getLastError: 1, w: 1 });
assert(gleObj.ok);
assert.eq(null, gleObj.err);
st.stop();
}

View File

@@ -1,6 +1,6 @@
s = new ShardingTest( "migrateBig" , 2 , 0 , 1 , { chunksize : 1 } );
s.config.settings.update( { _id: "balancer" }, { $set : { stopped: true } } , true );
s.config.settings.update( { _id: "balancer" }, { $set : { stopped : true, _waitForDelete : true } } , true );
s.adminCommand( { enablesharding : "test" } );
s.adminCommand( { shardcollection : "test.foo" , key : { x : 1 } } );

View File

@@ -1,220 +0,0 @@
/**
* Testing read preference on DBClientReplicaSets, specifically on the auto-retry
* and automatic failover selection
*/
// NOTE: this test is skipped when running smoke.py with --auth because of SERVER-6972
function basicTest() {
var replTest = new ReplSetTest({ name: 'basic', nodes: 2, useHostName: true });
replTest.startSet({ oplogSize: 1 });
replTest.initiate();
replTest.awaitSecondaryNodes();
var PRI_HOST = replTest.getPrimary().host;
var SEC_HOST = replTest.getSecondary().host;
var replConn = new Mongo(replTest.getURL());
var coll = replConn.getDB('test').user;
var dest = coll.find().readPref('primary').explain().server;
assert.eq(PRI_HOST, dest);
// Create brand new connection to make sure that the last cached is not used
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
dest = coll.find().readPref('secondary').explain().server;
assert.eq(SEC_HOST, dest);
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
dest = coll.find().readPref('primaryPreferred').explain().server;
assert.eq(PRI_HOST, dest);
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
dest = coll.find().readPref('secondaryPreferred').explain().server;
assert.eq(SEC_HOST, dest);
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
// just make sure that it doesn't throw
coll.find().readPref('nearest').explain();
replTest.stopSet();
}
function noPriNoSecTest() {
var replTest = new ReplSetTest({ name: 'noPriNoSec', useHostName: true,
nodes: [{}, { arbiter: true }, { arbiter: true }]});
replTest.startSet({ oplogSize: 1 });
replTest.initiate();
replTest.awaitSecondaryNodes();
var replConn = new Mongo(replTest.getURL());
var coll = replConn.getDB('test').user;
replTest.stop(0);
assert.throws(function() {
coll.find().readPref('primary').explain();
});
// Make sure that it still fails even when trying to refresh
assert.throws(function() {
coll.find().readPref('primary').explain();
});
// Don't need to create new connection because failed connections
// would never be reused, and also becasue the js Mongo contructor
// will throw when it can't connect to a primary
assert.throws(function() {
coll.find().readPref('secondary').explain();
});
assert.throws(function() {
coll.find().readPref('secondary').explain();
});
assert.throws(function() {
coll.find().readPref('primaryPreferred').explain();
});
assert.throws(function() {
coll.find().readPref('primaryPreferred').explain();
});
assert.throws(function() {
coll.find().readPref('secondaryPreferred').explain();
});
assert.throws(function() {
coll.find().readPref('secondaryPreferred').explain();
});
assert.throws(function() {
coll.find().readPref('neareset').explain();
});
assert.throws(function() {
coll.find().readPref('nearest').explain();
});
replTest.stopSet();
}
function priOkNoSecTest() {
var replTest = new ReplSetTest({ name: 'priOkNoSec', useHostName: true,
nodes: [{}, { arbiter: true }, {}]});
replTest.startSet({ oplogSize: 1 });
replTest.initiate();
replTest.awaitSecondaryNodes();
var replConn = new Mongo(replTest.getURL());
var coll = replConn.getDB('test').user;
replTest.stop(2);
var PRI_HOST = replTest.getPrimary().host;
var dest = coll.find().readPref('primary').explain().server;
assert.eq(PRI_HOST, dest);
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
dest = coll.find().readPref('primaryPreferred').explain().server;
assert.eq(PRI_HOST, dest);
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
assert.throws(function() {
coll.find().readPref('secondary').explain();
});
assert.throws(function() {
coll.find().readPref('secondary').explain();
});
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
dest = coll.find().readPref('secondaryPreferred').explain().server;
assert.eq(PRI_HOST, dest);
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
dest = coll.find().readPref('nearest').explain().server;
assert.eq(PRI_HOST, dest);
replTest.stopSet();
}
function noPriSecOkTest() {
var replTest = new ReplSetTest({ name: 'noPriSecOk', useHostName: true,
nodes: [{ }, { arbiter: true }, { }]});
replTest.startSet({ oplogSize: 1 });
replTest.initiate();
replTest.awaitSecondaryNodes();
var priConn = replTest.getPrimary();
var conf = priConn.getDB('local').system.replset.findOne();
conf.version++;
conf.members[0].priority = 99;
conf.members[2].priority = 0;
var SEC_HOST = replTest.nodes[2].host;
try {
priConn.getDB('admin').runCommand({ replSetReconfig: conf });
} catch (x) {
print('Exception from reconfig: ' + x);
}
var replConn = new Mongo(replTest.getURL());
var coll = replConn.getDB('test').user;
replTest.stop(0);
assert.throws(function() {
coll.find().readPref('primary').explain();
});
// Make sure that it still fails even when trying to refresh
assert.throws(function() {
coll.find().readPref('primary').explain();
});
replConn = new Mongo(replTest.getURL());
coll = replConn.getDB('test').user;
var dest = coll.find().readPref('primaryPreferred').explain().server;
assert.eq(SEC_HOST, dest);
replTest.start(0, {}, true);
replTest.awaitSecondaryNodes();
replConn = new Mongo(replTest.getURL());
replTest.stop(0);
coll = replConn.getDB('test').user;
dest = coll.find().readPref('secondary').explain().server;
assert.eq(SEC_HOST, dest);
replTest.start(0, {}, true);
replTest.awaitSecondaryNodes();
replConn = new Mongo(replTest.getURL());
replTest.stop(0);
coll = replConn.getDB('test').user;
dest = coll.find().readPref('secondaryPreferred').explain().server;
assert.eq(SEC_HOST, dest);
replTest.start(0, {}, true);
replTest.awaitSecondaryNodes();
replConn = new Mongo(replTest.getURL());
replTest.stop(0);
coll = replConn.getDB('test').user;
dest = coll.find().readPref('nearest').explain().server;
assert.eq(SEC_HOST, dest);
replTest.stopSet();
}
basicTest();
noPriNoSecTest();
priOkNoSecTest();
noPriSecOkTest();

View File

@@ -65,7 +65,7 @@ assert.soon(
return res.length > 1 && Math.abs( res[0].nChunks - res[1].nChunks ) <= 3;
} ,
"never migrated" , 180000 , 1000 );
"never migrated" , 10 * 60 * 1000 , 1000 );
stopMongod( 30000 );
stopMongod( 29999 );

View File

@@ -0,0 +1,52 @@
/** Test TTL docs are not deleted from secondaries directly
*/
var rt = new ReplSetTest( { name : "ttl_repl" , nodes: 2 } );
// setup set
var nodes = rt.startSet();
rt.initiate();
var master = rt.getMaster();
rt.awaitSecondaryNodes();
var slave1 = rt.getSecondary();
// shortcuts
var masterdb = master.getDB( 'd' );
var slave1db = slave1.getDB( 'd' );
var mastercol = masterdb[ 'c' ];
var slave1col = slave1db[ 'c' ];
// create TTL index, wait for TTL monitor to kick in, then check things
mastercol.ensureIndex( { x : 1 } , { expireAfterSeconds : 10 } );
rt.awaitReplication();
//increase logging
slave1col.getDB().adminCommand({setParameter:1, logLevel:1});
//insert old doc (10 minutes old) directly on secondary using godinsert
slave1col.runCommand("godinsert",
{obj: {_id: new Date(), x: new Date( (new Date()).getTime() - 600000 ) } })
assert.eq(1, slave1col.count(), "missing inserted doc" );
sleep(70*1000) //wait for 70seconds
assert.eq(1, slave1col.count(), "ttl deleted my doc!" );
// looking for this error : "Assertion: 13312:replSet error : logOp() but not primary"
// indicating that the secondary tried to delete the doc, but shouldn't be writing
var errorString = "13312";
var foundError = false;
var globalLogLines = slave1col.getDB().adminCommand({getLog:"global"}).log
for (i in globalLogLines) {
var line = globalLogLines[i];
if (line.match( errorString )) {
foundError = true;
errorString = line; // replace error string with what we found.
break;
}
}
assert.eq(false, foundError, "found error in this line: " + errorString);
// finish up
rt.stopSet();

View File

@@ -500,11 +500,19 @@ namespace mongo {
return getLastErrorString( info );
}
string DBClientWithCommands::getLastErrorString( const BSONObj& info ) {
BSONElement e = info["err"];
if( e.eoo() ) return "";
if( e.type() == Object ) return e.toString();
return e.str();
string DBClientWithCommands::getLastErrorString(const BSONObj& info) {
if (info["ok"].trueValue()) {
BSONElement e = info["err"];
if (e.eoo()) return "";
if (e.type() == Object) return e.toString();
return e.str();
} else {
// command failure
BSONElement e = info["errmsg"];
if (e.eoo()) return "";
if (e.type() == Object) return "getLastError command failed: " + e.toString();
return "getLastError command failed: " + e.str();
}
}
const BSONObj getpreverrorcmdobj = fromjson("{getpreverror:1}");

View File

@@ -244,19 +244,18 @@ namespace mongo {
uassert(16385, "tags for read preference should be an array",
tagsElem.type() == mongo::Array);
std::auto_ptr<TagSet> tags(new TagSet(BSONArray(tagsElem.Obj())));
if (pref == mongo::ReadPreference_PrimaryOnly && !tags->isExhausted()) {
TagSet tags(BSONArray(tagsElem.Obj().getOwned()));
if (pref == mongo::ReadPreference_PrimaryOnly && !tags.isExhausted()) {
uassert(16384, "Only empty tags are allowed with primary read preference",
tags->getCurrentTag().isEmpty());
tags.getCurrentTag().isEmpty());
}
return new ReadPreferenceSetting(pref, tags.release());
return new ReadPreferenceSetting(pref, tags);
}
}
BSONArrayBuilder arrayBuilder;
arrayBuilder.append(BSONObj());
return new ReadPreferenceSetting(pref, new TagSet(arrayBuilder.arr()));
TagSet tags(BSON_ARRAY(BSONObj()));
return new ReadPreferenceSetting(pref, tags);
}
/**
@@ -1538,8 +1537,8 @@ namespace mongo {
DBClientConnection& DBClientReplicaSet::slaveConn() {
BSONArray emptyArray(BSON_ARRAY(BSONObj()));
TagSet tags(emptyArray);
shared_ptr<ReadPreferenceSetting> readPref(new ReadPreferenceSetting(
ReadPreference_SecondaryPreferred, new TagSet(emptyArray)));
shared_ptr<ReadPreferenceSetting> readPref(
new ReadPreferenceSetting(ReadPreference_SecondaryPreferred, tags));
DBClientConnection* conn = selectNodeUsingTags(readPref);
uassert( 16369, str::stream() << "No good nodes available for set: "
@@ -1743,7 +1742,7 @@ namespace mongo {
ReplicaSetMonitorPtr monitor = _getMonitor();
bool isPrimarySelected = false;
_lastSlaveOkHost = monitor->selectAndCheckNode(readPref->pref, readPref->tags,
_lastSlaveOkHost = monitor->selectAndCheckNode(readPref->pref, &readPref->tags,
&isPrimarySelected);
if ( _lastSlaveOkHost.empty() ){
@@ -2011,6 +2010,13 @@ namespace mongo {
TagSet::TagSet() : _isExhausted(true), _tagIterator(_tags) {
}
TagSet::TagSet(const TagSet& other) :
_isExhausted(false),
_tags(other._tags.getOwned()),
_tagIterator(_tags) {
next();
}
TagSet::TagSet(const BSONArray& tags) :
_isExhausted(false),
_tags(tags.getOwned()),
@@ -2042,10 +2048,6 @@ namespace mongo {
return new BSONObjIterator(_tags);
}
TagSet* TagSet::clone() const {
return new TagSet(BSONArray(_tags.copy()));
}
bool TagSet::equals(const TagSet& other) const {
return _tags.equal(other._tags);
}

View File

@@ -617,13 +617,19 @@ namespace mongo {
* A simple object for representing the list of tags. The initial state will
* have a valid current tag as long as the list is not empty.
*/
class TagSet : public boost::noncopyable { // because of BSONArrayIteratorSorted
class TagSet {
public:
/**
* Creates an empty tag list that is initially exhausted.
*/
TagSet();
/**
* Creates a copy of the given TagSet. The new copy will have the
* iterator pointing at the initial position.
*/
explicit TagSet(const TagSet& other);
/**
* Creates a tag set object that lazily iterates over the tag list.
*
@@ -660,12 +666,6 @@ namespace mongo {
*/
BSONObjIterator* getIterator() const;
/**
* Create a new copy of this tag set and wuth the iterator pointing at the
* head.
*/
TagSet* clone() const;
/**
* @returns true if the other TagSet has the same tag set specification with
* this tag set, disregarding where the current iterator is pointing to.
@@ -673,6 +673,12 @@ namespace mongo {
bool equals(const TagSet& other) const;
private:
/**
* This is purposely undefined as the semantics for assignment can be
* confusing. This is because BSONArrayIteratorSorted shouldn't be
* copied (because of how it manages internal buffer).
*/
TagSet& operator=(const TagSet& other);
BSONObj _currentTag;
bool _isExhausted;
@@ -683,25 +689,21 @@ namespace mongo {
struct ReadPreferenceSetting {
/**
* @param tag cannot be NULL.
* @parm pref the read preference mode.
* @param tag the tag set. Note that this object will have the
* tag set will have this in a reset state (meaning, this
* object's copy of tag will have the iterator in the initial
* position).
*/
ReadPreferenceSetting(ReadPreference pref, TagSet* tag):
pref(pref), tags(tag->clone()) {
}
~ReadPreferenceSetting() {
delete tags;
ReadPreferenceSetting(ReadPreference pref, const TagSet& tag):
pref(pref), tags(tag) {
}
inline bool equals(const ReadPreferenceSetting& other) const {
return pref == other.pref && tags->equals(*other.tags);
return pref == other.pref && tags.equals(other.tags);
}
const ReadPreference pref;
/**
* Note: This object owns this memory.
*/
TagSet* tags;
TagSet tags;
};
}

View File

@@ -15,7 +15,8 @@
*/
/**
* This file contains tests for DBClientReplicaSet.
* This file contains tests for DBClientReplicaSet. The tests mocks the servers
* the DBClientReplicaSet talks to, so the tests only covers the client side logic.
*/
#include "mongo/bson/bson_field.h"
@@ -25,35 +26,13 @@
#include "mongo/dbtests/mock/mock_conn_registry.h"
#include "mongo/dbtests/mock/mock_replica_set.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/assert_util.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
using std::auto_ptr;
using std::map;
using std::make_pair;
using std::pair;
using std::string;
using std::vector;
using boost::scoped_ptr;
using mongo::BSONField;
using mongo::BSONObj;
using mongo::BSONArray;
using mongo::BSONElement;
using mongo::ConnectionString;
using mongo::DBClientCursor;
using mongo::DBClientReplicaSet;
using mongo::HostAndPort;
using mongo::MockReplicaSet;
using mongo::Query;
using mongo::ReadPreference;
using mongo::ReplicaSetMonitor;
using mongo::ScopedDbConnection;
using mongo::TagSet;
namespace mongo {
// Symbols defined to build the binary correctly.
CmdLine cmdLine;
@@ -73,16 +52,373 @@ namespace mongo {
}
}
namespace mongo_test {
namespace {
using boost::scoped_ptr;
using std::auto_ptr;
using std::map;
using std::make_pair;
using std::pair;
using std::string;
using std::vector;
using mongo::AssertionException;
using mongo::BSONArray;
using mongo::BSONElement;
using mongo::BSONField;
using mongo::BSONObj;
using mongo::ConnectionString;
using mongo::DBClientCursor;
using mongo::DBClientReplicaSet;
using mongo::HostAndPort;
using mongo::HostField;
using mongo::IdentityNS;
using mongo::MockReplicaSet;
using mongo::Query;
using mongo::ReadPreference;
using mongo::ReplicaSetMonitor;
using mongo::ScopedDbConnection;
using mongo::TagSet;
/**
* Basic fixture with one primary and one secondary.
*/
class BasicRS: public mongo::unittest::Test {
protected:
void setUp() {
_replSet.reset(new MockReplicaSet("test", 2));
ConnectionString::setConnectionHook(
mongo::MockConnRegistry::get()->getConnStrHook());
}
void tearDown() {
ReplicaSetMonitor::remove(_replSet->getSetName(), true);
_replSet.reset();
// TODO: remove this after we remove replSetGetStatus from ReplicaSetMonitor.
mongo::ScopedDbConnection::clearPool();
}
MockReplicaSet* getReplSet() {
return _replSet.get();
}
private:
boost::scoped_ptr<MockReplicaSet> _replSet;
};
TEST_F(BasicRS, ReadFromPrimary) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_PrimaryOnly, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getPrimary(), doc[HostField.name()].str());
}
TEST_F(BasicRS, SecondaryOnly) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_SecondaryOnly, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getSecondaries().front(), doc[HostField.name()].str());
}
TEST_F(BasicRS, PrimaryPreferred) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_PrimaryPreferred, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getPrimary(), doc[HostField.name()].str());
}
TEST_F(BasicRS, SecondaryPreferred) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_SecondaryPreferred, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getSecondaries().front(), doc[HostField.name()].str());
}
/**
* Setup for 2 member replica set will all of the nodes down.
*/
class AllNodesDown: public mongo::unittest::Test {
protected:
void setUp() {
_replSet.reset(new MockReplicaSet("test", 2));
ConnectionString::setConnectionHook(
mongo::MockConnRegistry::get()->getConnStrHook());
vector<HostAndPort> hostList(_replSet->getHosts());
for (vector<HostAndPort>::const_iterator iter = hostList.begin();
iter != hostList.end(); ++iter) {
_replSet->kill(iter->toString(true));
}
}
void tearDown() {
ReplicaSetMonitor::remove(_replSet->getSetName(), true);
_replSet.reset();
// TODO: remove this after we remove replSetGetStatus from ReplicaSetMonitor.
mongo::ScopedDbConnection::clearPool();
}
MockReplicaSet* getReplSet() {
return _replSet.get();
}
private:
boost::scoped_ptr<MockReplicaSet> _replSet;
};
TEST_F(AllNodesDown, ReadFromPrimary) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_PrimaryOnly, BSONArray());
ASSERT_THROWS(replConn.query(IdentityNS, query), AssertionException);
}
TEST_F(AllNodesDown, SecondaryOnly) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_SecondaryOnly, BSONArray());
ASSERT_THROWS(replConn.query(IdentityNS, query), AssertionException);
}
TEST_F(AllNodesDown, PrimaryPreferred) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_PrimaryPreferred, BSONArray());
ASSERT_THROWS(replConn.query(IdentityNS, query), AssertionException);
}
TEST_F(AllNodesDown, SecondaryPreferred) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_SecondaryPreferred, BSONArray());
ASSERT_THROWS(replConn.query(IdentityNS, query), AssertionException);
}
TEST_F(AllNodesDown, Nearest) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_Nearest, BSONArray());
ASSERT_THROWS(replConn.query(IdentityNS, query), AssertionException);
}
/**
* Setup for 2 member replica set with the primary down.
*/
class PrimaryDown: public mongo::unittest::Test {
protected:
void setUp() {
_replSet.reset(new MockReplicaSet("test", 2));
ConnectionString::setConnectionHook(
mongo::MockConnRegistry::get()->getConnStrHook());
_replSet->kill(_replSet->getPrimary());
}
void tearDown() {
ReplicaSetMonitor::remove(_replSet->getSetName(), true);
_replSet.reset();
// TODO: remove this after we remove replSetGetStatus from ReplicaSetMonitor.
mongo::ScopedDbConnection::clearPool();
}
MockReplicaSet* getReplSet() {
return _replSet.get();
}
private:
boost::scoped_ptr<MockReplicaSet> _replSet;
};
TEST_F(PrimaryDown, ReadFromPrimary) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_PrimaryOnly, BSONArray());
ASSERT_THROWS(replConn.query(IdentityNS, query), AssertionException);
}
TEST_F(PrimaryDown, SecondaryOnly) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_SecondaryOnly, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getSecondaries().front(), doc[HostField.name()].str());
}
TEST_F(PrimaryDown, PrimaryPreferred) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_PrimaryPreferred, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getSecondaries().front(), doc[HostField.name()].str());
}
TEST_F(PrimaryDown, SecondaryPreferred) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_SecondaryPreferred, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getSecondaries().front(), doc[HostField.name()].str());
}
TEST_F(PrimaryDown, Nearest) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_Nearest, BSONArray());
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getSecondaries().front(), doc[HostField.name()].str());
}
/**
* Setup for 2 member replica set with the secondary down.
*/
class SecondaryDown: public mongo::unittest::Test {
protected:
void setUp() {
_replSet.reset(new MockReplicaSet("test", 2));
ConnectionString::setConnectionHook(
mongo::MockConnRegistry::get()->getConnStrHook());
_replSet->kill(_replSet->getSecondaries().front());
}
void tearDown() {
ReplicaSetMonitor::remove(_replSet->getSetName(), true);
_replSet.reset();
// TODO: remove this after we remove replSetGetStatus from ReplicaSetMonitor.
mongo::ScopedDbConnection::clearPool();
}
MockReplicaSet* getReplSet() {
return _replSet.get();
}
private:
boost::scoped_ptr<MockReplicaSet> _replSet;
};
TEST_F(SecondaryDown, ReadFromPrimary) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_PrimaryOnly, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getPrimary(), doc[HostField.name()].str());
}
TEST_F(SecondaryDown, SecondaryOnly) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_SecondaryOnly, BSONArray());
ASSERT_THROWS(replConn.query(IdentityNS, query), AssertionException);
}
TEST_F(SecondaryDown, PrimaryPreferred) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_PrimaryPreferred, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getPrimary(), doc[HostField.name()].str());
}
TEST_F(SecondaryDown, SecondaryPreferred) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_SecondaryPreferred, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getPrimary(), doc[HostField.name()].str());
}
TEST_F(SecondaryDown, Nearest) {
MockReplicaSet* replSet = getReplSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts());
Query query;
query.readPref(mongo::ReadPreference_Nearest, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
ASSERT_EQUALS(replSet->getPrimary(), doc[HostField.name()].str());
}
/**
* Warning: Tests running this fixture cannot be run in parallel with other tests
* that uses ConnectionString::setConnectionHook
*/
class TaggedFiveMemberRS: public mongo::unittest::Test {
protected:
static const string IdentityNS;
static const BSONField<string> HostField;
void setUp() {
_replSet.reset(new MockReplicaSet("test", 5));
_originalConnectionHook = ConnectionString::getConnectionHook();
@@ -153,6 +489,8 @@ namespace mongo_test {
ConnectionString::setConnectionHook(_originalConnectionHook);
ReplicaSetMonitor::remove(_replSet->getSetName(), true);
_replSet.reset();
// TODO: remove this after we remove replSetGetStatus from ReplicaSetMonitor.
mongo::ScopedDbConnection::clearPool();
}
@@ -165,9 +503,6 @@ namespace mongo_test {
boost::scoped_ptr<MockReplicaSet> _replSet;
};
const string TaggedFiveMemberRS::IdentityNS("local.me");
const BSONField<string> TaggedFiveMemberRS::HostField("host", "bad");
TEST_F(TaggedFiveMemberRS, ConnShouldPinIfSameSettings) {
MockReplicaSet* replSet = getReplSet();
vector<HostAndPort> seedList;
@@ -179,6 +514,8 @@ namespace mongo_test {
{
Query query;
query.readPref(mongo::ReadPreference_PrimaryPreferred, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
dest = doc[HostField.name()].str();
@@ -205,6 +542,8 @@ namespace mongo_test {
{
Query query;
query.readPref(mongo::ReadPreference_SecondaryPreferred, BSONArray());
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
dest = doc[HostField.name()].str();
@@ -233,6 +572,8 @@ namespace mongo_test {
Query query;
query.readPref(mongo::ReadPreference_SecondaryPreferred,
BSON_ARRAY(BSON("dc" << "sf")));
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = replConn.query(IdentityNS, query);
BSONObj doc = cursor->next();
dest = doc[HostField.name()].str();
@@ -261,6 +602,8 @@ namespace mongo_test {
string dest;
mongo::DBClientConnection& secConn = replConn.slaveConn();
// Note: IdentityNS contains the name of the server.
auto_ptr<DBClientCursor> cursor = secConn.query(IdentityNS, Query());
BSONObj doc = cursor->next();
dest = doc[HostField.name()].str();

View File

@@ -492,12 +492,58 @@ namespace mongo {
return true;
}
bool DistributedLock::isLockHeld( double timeout, string* errMsg ) {
scoped_ptr<ScopedDbConnection> connPtr(
ScopedDbConnection::getInternalScopedDbConnection( _conn.toString(), timeout ) );
ScopedDbConnection& conn = *connPtr;
BSONObj lockObj;
try {
lockObj = conn->findOne( LocksType::ConfigNS,
BSON( LocksType::name(_name) ) ).getOwned();
}
catch ( DBException& e ) {
*errMsg = str::stream() << "error checking whether lock " << _name << " is held "
<< causedBy( e );
return false;
}
conn.done();
if ( lockObj.isEmpty() ) {
*errMsg = str::stream() << "no lock for " << _name << " exists in the locks collection";
return false;
}
if ( lockObj[LocksType::state()].numberInt() < 2 ) {
*errMsg = str::stream() << "lock " << _name << " current state is not held ("
<< lockObj[LocksType::state()].numberInt() << ")";
return false;
}
if ( lockObj[LocksType::process()].String() != _processId ) {
*errMsg = str::stream() << "lock " << _name << " is currently being held by "
<< "another process ("
<< lockObj[LocksType::process()].String() << ")";
return false;
}
if ( distLockPinger.willUnlockOID( lockObj[LocksType::lockID()].OID() ) ) {
*errMsg = str::stream() << "lock " << _name << " is not held and is currently being "
<< "scheduled for lazy unlock by "
<< lockObj[LocksType::lockID()].OID();
return false;
}
return true;
}
// Semantics of this method are basically that if the lock cannot be acquired, returns false, can be retried.
// If the lock should not be tried again (some unexpected error) a LockException is thrown.
// If we are only trying to re-enter a currently held lock, reenter should be true.
// Note: reenter doesn't actually make this lock re-entrant in the normal sense, since it can still only
// be unlocked once, instead it is used to verify that the lock is already held.
bool DistributedLock::lock_try( const string& why , bool reenter, BSONObj * other ) {
bool DistributedLock::lock_try( const string& why , bool reenter, BSONObj * other, double timeout ) {
// TODO: Start pinging only when we actually get the lock?
// If we don't have a thread pinger, make sure we shouldn't have one
@@ -520,7 +566,7 @@ namespace mongo {
other = &dummyOther;
scoped_ptr<ScopedDbConnection> connPtr(
ScopedDbConnection::getInternalScopedDbConnection( _conn.toString() ) );
ScopedDbConnection::getInternalScopedDbConnection( _conn.toString(), timeout ) );
ScopedDbConnection& conn = *connPtr;
BSONObjBuilder queryBuilder;

View File

@@ -123,7 +123,15 @@ namespace mongo {
* details if not
* @return true if it managed to grab the lock
*/
bool lock_try( const string& why , bool reenter = false, BSONObj * other = 0 );
bool lock_try( const string& why , bool reenter = false, BSONObj * other = 0, double timeout = 0.0 );
/**
* Returns true if we currently believe we hold this lock and it was possible to
* confirm that, within 'timeout' seconds, if provided, with the config servers. If the
* lock is not held or if we failed to contact the config servers within the timeout,
* returns false.
*/
bool isLockHeld( double timeout, string* errMsg );
/**
* Releases a previously taken lock.
@@ -223,9 +231,9 @@ namespace mongo {
return *this;
}
dist_lock_try( DistributedLock * lock , const std::string& why )
dist_lock_try( DistributedLock * lock , const std::string& why, double timeout = 0.0 )
: _lock(lock), _why(why) {
_got = _lock->lock_try( why , false , &_other );
_got = _lock->lock_try( why , false , &_other, timeout );
}
~dist_lock_try() {
@@ -235,16 +243,23 @@ namespace mongo {
}
}
bool reestablish(){
return retry();
}
/**
* Returns false if the lock is known _not_ to be held, otherwise asks the underlying
* lock to issue a 'isLockHeld' call and returns whatever that calls does.
*/
bool isLockHeld( double timeout, string* errMsg) {
if ( !_lock ) {
*errMsg = "Lock is not currently set up";
return false;
}
bool retry() {
verify( _lock );
verify( _got );
verify( ! _other.isEmpty() );
if ( !_got ) {
*errMsg = str::stream() << "Lock " << _lock->_name << " is currently held by "
<< _other;
return false;
}
return _got = _lock->lock_try( _why , true, &_other );
return _lock->isLockHeld( timeout, errMsg );
}
bool got() const { return _got; }

View File

@@ -181,7 +181,7 @@ namespace mongo {
if ( lockType > 0 ) { // write $cmd
string errmsg;
if ( ! prepare( errmsg ) )
throw UserException( 13104 , (string)"SyncClusterConnection::findOne prepare failed: " + errmsg );
throw UserException( PrepareConfigsFailedCode , (string)"SyncClusterConnection::findOne prepare failed: " + errmsg );
vector<BSONObj> all;
for ( size_t i=0; i<_conns.size(); i++ ) {
@@ -336,7 +336,31 @@ namespace mongo {
return;
}
uassert( 10023 , "SyncClusterConnection bulk insert not implemented" , 0);
for (vector<BSONObj>::const_iterator it = v.begin(); it != v.end(); ++it ) {
BSONObj obj = *it;
if ( obj["_id"].type() == EOO ) {
string assertMsg = "SyncClusterConnection::insert (batched) obj misses an _id: ";
uasserted( 16743, assertMsg + obj.jsonString() );
}
}
// fsync all connections before starting the batch.
string errmsg;
if ( ! prepare( errmsg ) ) {
string assertMsg = "SyncClusterConnection::insert (batched) prepare failed: ";
throw UserException( 16744, assertMsg + errmsg );
}
// We still want one getlasterror per document, even if they're batched.
for ( size_t i=0; i<_conns.size(); i++ ) {
for ( vector<BSONObj>::const_iterator it = v.begin(); it != v.end(); ++it ) {
_conns[i]->insert( ns, *it, flags );
_conns[i]->getLastErrorDetailed();
}
}
// We issue a final getlasterror, but this time with an fsync.
_checkLast();
}
void SyncClusterConnection::remove( const string &ns , Query query, int flags ) {

View File

@@ -30,9 +30,12 @@ namespace mongo {
const PrincipalName& principalName,
BSONObj* result) {
if (dbname == StringData("$external", StringData::LiteralTag())) {
if (dbname == StringData("$external", StringData::LiteralTag()) ||
dbname == AuthorizationManager::SERVER_RESOURCE_NAME ||
dbname == AuthorizationManager::CLUSTER_RESOURCE_NAME) {
return Status(ErrorCodes::UserNotFound,
"No privilege documents stored in the $external user source.");
mongoutils::str::stream() << "No privilege documents stored in the " <<
dbname << " user source.");
}
if (!NamespaceString::validDBName(dbname)) {

View File

@@ -23,6 +23,7 @@
#include "mongo/db/index_update.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_details.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/log.h"
namespace mongo {
@@ -90,10 +91,21 @@ namespace {
void createSystemIndexes(const NamespaceString& ns) {
if (ns.coll == "system.users") {
Helpers::ensureIndex(ns.ns().c_str(),
extendedSystemUsersKeyPattern,
true, // unique
extendedSystemUsersIndexName.c_str());
try {
Helpers::ensureIndex(ns.ns().c_str(),
extendedSystemUsersKeyPattern,
true, // unique
extendedSystemUsersIndexName.c_str());
} catch (const DBException& e) {
if (e.getCode() == ASSERT_ID_DUPKEY) {
log() << "Duplicate key exception while trying to build unique index on " <<
ns << ". You most likely have user documents with duplicate \"user\" "
"fields. To resolve this, start up with a version of MongoDB prior to "
"2.4, drop the duplicate user documents, then start up again with the "
"current version." << endl;
}
throw;
}
}
}

View File

@@ -389,6 +389,7 @@ namespace mongo {
mayInterrupt( opts.mayBeInterrupted );
dbtempreleaseif r( opts.mayYield );
#if 0
// fetch index info
auto_ptr<DBClientCursor> cur = _conn->query(idxns.c_str(), BSONObj(), 0, 0, 0,
opts.slaveOk ? QueryOption_SlaveOk : 0 );
@@ -415,7 +416,7 @@ namespace mongo {
_sortersForNS[idxEntry["ns"].String()].insert(make_pair(idxEntry["name"].String(),
details));
}
#endif
// just using exhaust for collection copying right now
// todo: if snapshot (bool param to this func) is true, we need to snapshot this query?

View File

@@ -16,10 +16,12 @@
#include "mongo/db/commands/authentication_commands.h"
#include <boost/scoped_ptr.hpp>
#include <string>
#include <vector>
#include "mongo/base/status.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_manager.h"
@@ -29,6 +31,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/jsobj.h"
#include "mongo/platform/random.h"
#include "mongo/util/concurrency/mutex.h"
#include "mongo/util/md5.hpp"
namespace mongo {
@@ -53,8 +56,10 @@ namespace mongo {
class CmdGetNonce : public Command {
public:
CmdGetNonce() : Command("getnonce") {
_random = SecureRandom::create();
CmdGetNonce() :
Command("getnonce"),
_randMutex("getnonce"),
_random(SecureRandom::create()) {
}
virtual bool requiresAuth() { return false; }
@@ -68,7 +73,7 @@ namespace mongo {
const BSONObj& cmdObj,
std::vector<Privilege>* out) {} // No auth required
bool run(const string&, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
nonce64 n = _random->nextInt64();
nonce64 n = getNextNonce();
stringstream ss;
ss << hex << n;
result.append("nonce", ss.str() );
@@ -77,7 +82,14 @@ namespace mongo {
return true;
}
SecureRandom* _random;
private:
nonce64 getNextNonce() {
SimpleMutex::scoped_lock lk(_randMutex);
return _random->nextInt64();
}
SimpleMutex _randMutex; // Synchronizes accesses to _random.
boost::scoped_ptr<SecureRandom> _random;
} cmdGetNonce;
bool CmdAuthenticate::run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
@@ -92,6 +104,7 @@ namespace mongo {
if (dbname != StringData("local", StringData::LiteralTag()) ||
user != internalSecurity.user) {
errmsg = _nonceAuthenticateCommandsDisabledMessage;
result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
return false;
}
}
@@ -105,6 +118,7 @@ namespace mongo {
<< endl;
errmsg = "auth fails";
sleepmillis(10);
result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
return false;
}
@@ -132,6 +146,7 @@ namespace mongo {
log() << "auth: bad nonce received or getnonce not called. could be a driver bug or a security attack. db:" << dbname << endl;
errmsg = "auth fails";
sleepmillis(30);
result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
return false;
}
}
@@ -143,6 +158,7 @@ namespace mongo {
if (!status.isOK()) {
log() << status.reason() << std::endl;
errmsg = "auth fails";
result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
return false;
}
pwd = userObj["pwd"].String();
@@ -163,6 +179,7 @@ namespace mongo {
if ( key != computed ) {
log() << "auth: key mismatch " << user << ", ns:" << dbname << endl;
errmsg = "auth fails";
result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
return false;
}

View File

@@ -187,7 +187,9 @@ namespace mongo {
BSONElement e = cmdObj["w"];
if ( e.ok() ) {
if ( cmdLine.configsvr ) {
if ( cmdLine.configsvr && (!e.isNumber() || e.numberInt() > 1) ) {
// w:1 on config servers should still work, but anything greater than that
// should not.
result.append( "wnote", "can't use w on config servers" );
result.append( "err", "norepl" );
return true;

View File

@@ -89,14 +89,14 @@ namespace mongo {
const IndexDetails& id = d->idx( idxMatches[0] );
BSONObj indexPrefix;
FTSIndex* ftsIndex = static_cast<FTSIndex*>(id.getSpec().getType());
if ( language == "" ) {
FTSIndex* ftsIndex = static_cast<FTSIndex*>(id.getSpec().getType());
language = ftsIndex->getFtsSpec().defaultLanguage();
Status s = ftsIndex->getFtsSpec().getIndexPrefix( filter, &indexPrefix );
if ( !s.isOK() ) {
errmsg = s.toString();
return false;
}
}
Status s = ftsIndex->getFtsSpec().getIndexPrefix( filter, &indexPrefix );
if ( !s.isOK() ) {
errmsg = s.toString();
return false;
}

View File

@@ -68,7 +68,9 @@ namespace mongo {
}
// ...where the latitude is valid
double lat = thisCoord[1].Number();
double lng = thisCoord[0].Number();
if (lat < -90 || lat > 90) { return false; }
if (lng < -180 || lng > 180) { return false; }
}
return true;
}
@@ -101,7 +103,8 @@ namespace mongo {
if (coordinates.size() != 2) { return false; }
if (!coordinates[0].isNumber() || !coordinates[1].isNumber()) { return false; }
double lat = coordinates[1].Number();
return lat >= -90 && lat <= 90;
double lng = coordinates[0].Number();
return lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
}
void GeoParser::parseGeoJSONPoint(const BSONObj& obj, S2Cell* out) {

View File

@@ -54,6 +54,10 @@ namespace {
// Make sure lat is in range
ASSERT_TRUE(GeoParser::isPoint(fromjson("{'type':'Point', 'coordinates': [0, 90.0]}")));
ASSERT_TRUE(GeoParser::isPoint(fromjson("{'type':'Point', 'coordinates': [0, -90.0]}")));
ASSERT_TRUE(GeoParser::isPoint(fromjson("{'type':'Point', 'coordinates': [180, 90.0]}")));
ASSERT_TRUE(GeoParser::isPoint(fromjson("{'type':'Point', 'coordinates': [-180, -90.0]}")));
ASSERT_FALSE(GeoParser::isPoint(fromjson("{'type':'Point', 'coordinates': [180.01, 90.0]}")));
ASSERT_FALSE(GeoParser::isPoint(fromjson("{'type':'Point', 'coordinates': [-180.01, -90.0]}")));
ASSERT_FALSE(GeoParser::isPoint(fromjson("{'type':'Point', 'coordinates': [0, 90.1]}")));
ASSERT_FALSE(GeoParser::isPoint(fromjson("{'type':'Point', 'coordinates': [0, -90.1]}")));
}
@@ -63,6 +67,10 @@ namespace {
fromjson("{'type':'LineString', 'coordinates':[[1,2], [3,4]]}")));
ASSERT_TRUE(GeoParser::isLineString(
fromjson("{'type':'LineString', 'coordinates':[[0,-90], [0,90]]}")));
ASSERT_TRUE(GeoParser::isLineString(
fromjson("{'type':'LineString', 'coordinates':[[180,-90], [-180,90]]}")));
ASSERT_FALSE(GeoParser::isLineString(
fromjson("{'type':'LineString', 'coordinates':[[180.1,-90], [-180.1,90]]}")));
ASSERT_FALSE(GeoParser::isLineString(
fromjson("{'type':'LineString', 'coordinates':[[0,-91], [0,90]]}")));
ASSERT_FALSE(GeoParser::isLineString(
@@ -82,6 +90,13 @@ namespace {
TEST(GeoParser, isValidPolygon) {
ASSERT_TRUE(GeoParser::isPolygon(
fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[5,0],[5,5],[0,5],[0,0]] ]}")));
// No out of bounds points
ASSERT_FALSE(GeoParser::isPolygon(
fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[5,0],[5,91],[0,5],[0,0]] ]}")));
ASSERT_TRUE(GeoParser::isPolygon(
fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[180,0],[5,5],[0,5],[0,0]] ]}")));
ASSERT_FALSE(GeoParser::isPolygon(
fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[181,0],[5,5],[0,5],[0,0]] ]}")));
// And one with a hole.
ASSERT_TRUE(GeoParser::isPolygon(
fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[5,0],[5,5],[0,5],[0,0]],"

View File

@@ -97,6 +97,12 @@ namespace mongo {
BSONObj S2Cursor::currKey() const { return _btreeCursor->currKey(); }
DiskLoc S2Cursor::refLoc() { return DiskLoc(); }
long long S2Cursor::nscanned() { return _nscanned; }
bool S2Cursor::getsetdup(DiskLoc loc) { return _btreeCursor->getsetdup(loc); }
void S2Cursor::aboutToDeleteBucket(const DiskLoc& b) {
if (NULL != _btreeCursor) {
_btreeCursor->aboutToDeleteBucket(b);
}
}
// This is the actual search.
bool S2Cursor::advance() {

View File

@@ -39,7 +39,8 @@ namespace mongo {
virtual bool isMultiKey() const { return true; }
virtual bool autoDedup() const { return false; }
virtual bool modifiedKeys() const { return true; }
virtual bool getsetdup(DiskLoc loc) { return false; }
virtual bool getsetdup(DiskLoc loc);
virtual void aboutToDeleteBucket(const DiskLoc& b);
virtual string toString() { return "S2Cursor"; }
BSONObj indexKeyPattern() { return _keyPattern; }
virtual bool ok();

View File

@@ -465,6 +465,9 @@ namespace mongo {
}
Status JParse::dbRefObject(const StringData& fieldName, BSONObjBuilder& builder) {
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
if (!accept(COLON)) {
return parseError("Expecting ':'");
}
@@ -474,6 +477,8 @@ namespace mongo {
if (ret != Status::OK()) {
return ret;
}
subBuilder.append("$ref", ns);
if (!accept(COMMA)) {
return parseError("Expecting ','");
}
@@ -484,42 +489,12 @@ namespace mongo {
if (!accept(COLON)) {
return parseError("Expecting ':'");
}
if (accept("ObjectId")) {
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
subBuilder.append("$ref", ns);
objectId("$id", subBuilder);
subBuilder.done();
}
else if (accept(LBRACE)) {
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
subBuilder.append("$ref", ns);
if (!acceptField("$oid")) {
return parseError("Expected field name: \"$oid\"");
}
objectIdObject("$id", subBuilder);
subBuilder.done();
if (!accept(RBRACE)) {
return parseError("Expecting '}'");
}
}
else {
std::string id;
id.reserve(ID_RESERVE_SIZE);
Status ret = quotedString(&id);
if (ret != Status::OK()) {
return ret;
}
if (id.size() != 24) {
return parseError("Expecting 24 hex digits: " + id);
}
if (!isHexString(id)) {
return parseError("Expecting hex digits: " + id);
}
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
subBuilder.append("$ref", ns);
subBuilder.append("$id", OID(id));
subBuilder.done();
Status valueRet = value("$id", subBuilder);
if (valueRet != Status::OK()) {
return valueRet;
}
subBuilder.done();
return Status::OK();
}
@@ -661,6 +636,8 @@ namespace mongo {
}
Status JParse::dbRef(const StringData& fieldName, BSONObjBuilder& builder) {
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
if (!accept(LPAREN)) {
return parseError("Expecting '('");
}
@@ -670,27 +647,21 @@ namespace mongo {
if (refRet != Status::OK()) {
return refRet;
}
subBuilder.append("$ref", ns);
if (!accept(COMMA)) {
return parseError("Expecting ','");
}
std::string id;
id.reserve(ID_RESERVE_SIZE);
Status idRet = quotedString(&id);
if (idRet != Status::OK()) {
return idRet;
}
if (id.size() != 24) {
return parseError("Expecting 24 hex digits: " + id);
}
if (!isHexString(id)) {
return parseError("Expecting hex digits: " + id);
Status valueRet = value("$id", subBuilder);
if (valueRet != Status::OK()) {
return valueRet;
}
if (!accept(RPAREN)) {
return parseError("Expecting ')'");
}
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
subBuilder.append("$ref", ns);
subBuilder.append("$id", OID(id));
subBuilder.done();
return Status::OK();
}

View File

@@ -446,6 +446,11 @@ namespace mongo {
bb.append("authenticated", false);
}
string syncingTo = m->hbinfo().syncingTo;
if (!syncingTo.empty()) {
bb.append("syncingTo", syncingTo);
}
v.push_back(bb.obj());
m = m->next();
}

View File

@@ -122,6 +122,11 @@ namespace mongo {
result.append("hbmsg", theReplSet->hbmsg());
result.append("time", (long long) time(0));
result.appendDate("opTime", theReplSet->lastOpTimeWritten.asDate());
const Member *syncTarget = replset::BackgroundSync::get()->getSyncTarget();
if (syncTarget) {
result.append("syncingTo", syncTarget->fullName());
}
int v = theReplSet->config().version;
result.append("v", v);
if( v > cmdObj["v"].Int() )
@@ -396,6 +401,10 @@ namespace mongo {
}
mem.health = 1.0;
mem.lastHeartbeatMsg = info["hbmsg"].String();
if (info.hasElement("syncingTo")) {
mem.syncingTo = info["syncingTo"].String();
}
if( info.hasElement("opTime") )
mem.opTime = info["opTime"].Date();

View File

@@ -84,6 +84,7 @@ namespace mongo {
// This is the last time we got a heartbeat request from a given member.
time_t lastHeartbeatRecv;
DiagStr lastHeartbeatMsg;
DiagStr syncingTo;
OpTime opTime;
int skew;
bool authIssue;

View File

@@ -50,6 +50,9 @@ namespace mongo {
void doTTLForDB( const string& dbName ) {
//check isMaster before becoming god
bool isMaster = isMasterNs( dbName.c_str() );
Client::GodScope god;
vector<BSONObj> indexes;
@@ -100,7 +103,7 @@ namespace mongo {
nsd->syncUserFlags( ns );
}
// only do deletes if on master
if ( ! isMasterNs( dbName.c_str() ) ) {
if ( ! isMaster ) {
continue;
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (C) 2013 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 "mongo/dbtests/dbtests.h"
#include "mongo/util/assert_util.h"
using mongo::MsgAssertionException;
/**
* Test getLastError client handling
*/
namespace {
DBDirectClient _client;
static const char* const _ns = "unittests.gle";
/**
* Verify that when the command fails we get back an error message.
*/
class GetLastErrorCommandFailure {
public:
void run() {
_client.insert(_ns, BSON( "test" << "test"));
// Cannot mix fsync + j, will make command fail
string gleString = _client.getLastError(true, true, 10, 10);
ASSERT_NOT_EQUALS(gleString, "");
}
};
/**
* Verify that the write succeeds
*/
class GetLastErrorClean {
public:
void run() {
_client.insert(_ns, BSON( "test" << "test"));
// Make sure there was no error
string gleString = _client.getLastError();
ASSERT_EQUALS(gleString, "");
}
};
/**
* Verify that the write succeed first, then error on dup
*/
class GetLastErrorFromDup {
public:
void run() {
_client.insert(_ns, BSON( "_id" << 1));
// Make sure there was no error
string gleString = _client.getLastError();
ASSERT_EQUALS(gleString, "");
//insert dup
_client.insert(_ns, BSON( "_id" << 1));
// Make sure there was an error
gleString = _client.getLastError();
ASSERT_NOT_EQUALS(gleString, "");
}
};
class All : public Suite {
public:
All() : Suite( "gle" ) {
}
void setupTests() {
add< GetLastErrorClean >();
add< GetLastErrorCommandFailure >();
add< GetLastErrorFromDup >();
}
} myall;
}

View File

@@ -860,11 +860,9 @@ namespace JsonTests {
class DBRefConstructor : public Base {
virtual BSONObj bson() const {
BSONObjBuilder b;
OID o;
memset( &o, 0, 12 );
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", o);
subBuilder.append("$id", "000000000000000000000000");
subBuilder.done();
return b.obj();
}
@@ -877,11 +875,9 @@ namespace JsonTests {
class DBRefConstructorCapitals : public Base {
virtual BSONObj bson() const {
BSONObjBuilder b;
OID o;
memset( &o, 0, 12 );
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", o);
subBuilder.append("$id", "000000000000000000000000");
subBuilder.done();
return b.obj();
}
@@ -890,14 +886,40 @@ namespace JsonTests {
}
};
class DBRefObjectIDString : public Base {
class DBRefConstructorNumber : public Base {
virtual BSONObj bson() const {
BSONObjBuilder b;
OID o;
memset( &o, 0, 12 );
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", o);
subBuilder.append("$id", 1);
subBuilder.done();
return b.obj();
}
virtual string json() const {
return "{ \"a\" : Dbref( \"ns\", 1 ) }";
}
};
class DBRefNumberId : public Base {
virtual BSONObj bson() const {
BSONObjBuilder b;
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", 1);
subBuilder.done();
return b.obj();
}
virtual string json() const {
return "{ \"a\" : { \"$ref\" : \"ns\", \"$id\" : 1 } }";
}
};
class DBRefStringId : public Base {
virtual BSONObj bson() const {
BSONObjBuilder b;
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", "000000000000000000000000");
subBuilder.done();
return b.obj();
}
@@ -1605,7 +1627,8 @@ namespace JsonTests {
add< FromJsonTests::Utf8TooShort >();
add< FromJsonTests::DBRefConstructor >();
add< FromJsonTests::DBRefConstructorCapitals >();
add< FromJsonTests::DBRefObjectIDString >();
add< FromJsonTests::DBRefNumberId >();
add< FromJsonTests::DBRefStringId >();
add< FromJsonTests::DBRefObjectIDObject >();
add< FromJsonTests::DBRefObjectIDConstructor >();
add< FromJsonTests::Oid >();

View File

@@ -18,29 +18,23 @@
*/
#include "pch.h"
#include "../db/instance.h"
#include "mongo/db/instance.h"
#include "mongo/scripting/engine.h"
#include "mongo/util/timer.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/db/json.h"
#include "../pch.h"
#include "../scripting/engine.h"
#include "../util/timer.h"
#include "dbtests.h"
namespace mongo {
bool dbEval(const string& dbName , BSONObj& cmd, BSONObjBuilder& result, string& errmsg);
} // namespace mongo
namespace JSTests {
class Fundamental {
class BuiltinTests {
public:
void run() {
// By calling JavaJSImpl() inside run(), we ensure the unit test framework's
// signal handlers are pre-installed from JNI's perspective. This allows
// JNI to catch signals generated within the JVM and forward other signals
// as appropriate.
ScriptEngine::setup();
// Run any tests included with the scripting engine
globalScriptEngine->runTest();
}
};
@@ -48,7 +42,7 @@ namespace JSTests {
class BasicScope {
public:
void run() {
auto_ptr<Scope> s;
scoped_ptr<Scope> s;
s.reset( globalScriptEngine->newScope() );
s->setNumber( "x" , 5 );
@@ -63,18 +57,15 @@ namespace JSTests {
s->setBoolean( "b" , true );
ASSERT( s->getBoolean( "b" ) );
if ( 0 ) {
s->setBoolean( "b" , false );
ASSERT( ! s->getBoolean( "b" ) );
}
s->setBoolean( "b" , false );
ASSERT( ! s->getBoolean( "b" ) );
}
};
class ResetScope {
public:
void run() {
// Not worrying about this for now SERVER-446.
/*
/* Currently reset does not clear data in v8 or spidermonkey scopes. See SECURITY-10
auto_ptr<Scope> s;
s.reset( globalScriptEngine->newScope() );
@@ -90,15 +81,23 @@ namespace JSTests {
class FalseTests {
public:
void run() {
Scope * s = globalScriptEngine->newScope();
// Test falsy javascript values
scoped_ptr<Scope> s;
s.reset( globalScriptEngine->newScope() );
ASSERT( ! s->getBoolean( "x" ) );
ASSERT( ! s->getBoolean( "notSet" ) );
s->setString( "z" , "" );
ASSERT( ! s->getBoolean( "z" ) );
s->setString( "emptyString" , "" );
ASSERT( ! s->getBoolean( "emptyString" ) );
s->setNumber( "notANumberVal" , std::numeric_limits<double>::quiet_NaN());
ASSERT( ! s->getBoolean( "notANumberVal" ) );
delete s ;
s->setElement( "nullVal" , BSONObjBuilder().appendNull("null").obj().getField("null") );
ASSERT( ! s->getBoolean( "nullVal" ) );
s->setNumber( "zeroVal" , 0 );
ASSERT( ! s->getBoolean( "zeroVal" ) );
}
};
@@ -453,6 +452,22 @@ namespace JSTests {
ASSERT_EQUALS( Array, out.firstElement().type() );
}
// symbol
{
// test mutable object with symbol type
BSONObjBuilder builder;
builder.appendSymbol("sym", "value");
BSONObj in = builder.done();
s->setObject( "x", in, false );
BSONObj out = s->getObject( "x" );
ASSERT_EQUALS( Symbol, out.firstElement().type() );
// readonly
s->setObject( "x", in, true );
out = s->getObject( "x" );
ASSERT_EQUALS( Symbol, out.firstElement().type() );
}
delete s;
}
};
@@ -940,70 +955,194 @@ namespace JSTests {
}
};
class DBRefTest {
public:
DBRefTest() {
_a = "unittest.dbref.a";
_b = "unittest.dbref.b";
reset();
}
~DBRefTest() {
//reset();
}
namespace RoundTripTests {
void run() {
// Inherit from this class to test round tripping of JSON objects
class TestRoundTrip {
public:
virtual ~TestRoundTrip() {}
void run() {
client.insert( _a , BSON( "a" << "17" ) );
// Insert in Javascript -> Find using DBDirectClient
{
BSONObj fromA = client.findOne( _a , BSONObj() );
verify( fromA.valid() );
//cout << "Froma : " << fromA << endl;
BSONObjBuilder b;
b.append( "b" , 18 );
b.appendDBRef( "c" , "dbref.a" , fromA["_id"].__oid() );
client.insert( _b , b.obj() );
// Drop the collection
client.dropCollection( "unittest.testroundtrip" );
// Insert in Javascript
stringstream jsInsert;
jsInsert << "db.testroundtrip.insert(" << jsonIn() << ")";
ASSERT_TRUE( client.eval( "unittest" , jsInsert.str() ) );
// Find using DBDirectClient
BSONObj excludeIdProjection = BSON( "_id" << 0 );
BSONObj directFind = client.findOne( "unittest.testroundtrip",
"",
&excludeIdProjection);
bsonEquals( bson(), directFind );
// Insert using DBDirectClient -> Find in Javascript
// Drop the collection
client.dropCollection( "unittest.testroundtrip" );
// Insert using DBDirectClient
client.insert( "unittest.testroundtrip" , bson() );
// Find in Javascript
stringstream jsFind;
jsFind << "dbref = db.testroundtrip.findOne( { } , { _id : 0 } )\n"
<< "assert.eq(dbref, " << jsonOut() << ")";
ASSERT_TRUE( client.eval( "unittest" , jsFind.str() ) );
}
protected:
// Methods that must be defined by child classes
virtual BSONObj bson() const = 0;
virtual string json() const = 0;
// This can be overriden if a different meaning of equality besides woCompare is needed
virtual void bsonEquals( const BSONObj &expected, const BSONObj &actual ) {
if ( expected.woCompare( actual ) ) {
out() << "want:" << expected.jsonString() << " size: " << expected.objsize() << endl;
out() << "got :" << actual.jsonString() << " size: " << actual.objsize() << endl;
out() << expected.hexDump() << endl;
out() << actual.hexDump() << endl;
}
ASSERT( !expected.woCompare( actual ) );
}
ASSERT( client.eval( "unittest" , "x = db.dbref.b.findOne(); assert.eq( 17 , x.c.fetch().a , 'ref working' );" ) );
// This can be overriden if the JSON representation is altered on the round trip
virtual string jsonIn() const {
return json();
}
virtual string jsonOut() const {
return json();
}
};
// BSON DBRef <=> JS DBPointer
ASSERT( client.eval( "unittest", "x = db.dbref.b.findOne(); db.dbref.b.drop(); x.c = new DBPointer( x.c.ns, x.c.id ); db.dbref.b.insert( x );" ) );
ASSERT_EQUALS( DBRef, client.findOne( "unittest.dbref.b", "" )[ "c" ].type() );
class DBRefTest : public TestRoundTrip {
virtual BSONObj bson() const {
BSONObjBuilder b;
OID o;
memset( &o, 0, 12 );
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", o);
subBuilder.done();
return b.obj();
}
virtual string json() const {
return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
}
// BSON Object <=> JS DBRef
ASSERT( client.eval( "unittest", "x = db.dbref.b.findOne(); db.dbref.b.drop(); x.c = new DBRef( x.c.ns, x.c.id ); db.dbref.b.insert( x );" ) );
ASSERT_EQUALS( Object, client.findOne( "unittest.dbref.b", "" )[ "c" ].type() );
ASSERT_EQUALS( string( "dbref.a" ), client.findOne( "unittest.dbref.b", "" )[ "c" ].embeddedObject().getStringField( "$ref" ) );
}
// A "fetch" function is added to the DBRef object when it is inserted using the
// constructor, so we need to compare the fields individually
virtual void bsonEquals( const BSONObj &expected, const BSONObj &actual ) {
ASSERT_EQUALS( expected["a"].type() , actual["a"].type() );
ASSERT_EQUALS( expected["a"]["$id"].OID() , actual["a"]["$id"].OID() );
ASSERT_EQUALS( expected["a"]["$ref"].String() , actual["a"]["$ref"].String() );
}
};
void reset() {
client.dropCollection( _a );
client.dropCollection( _b );
}
class DBPointerTest : public TestRoundTrip {
virtual BSONObj bson() const {
BSONObjBuilder b;
OID o;
memset( &o, 0, 12 );
b.appendDBRef( "a" , "ns" , o );
return b.obj();
}
virtual string json() const {
return "{ \"a\" : DBPointer( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
}
};
const char * _a;
const char * _b;
};
class InformalDBRefTest : public TestRoundTrip {
virtual BSONObj bson() const {
BSONObjBuilder b;
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", "000000000000000000000000");
subBuilder.done();
return b.obj();
}
class InformalDBRef {
public:
void run() {
client.insert( ns(), BSON( "i" << 1 ) );
BSONObj obj = client.findOne( ns(), BSONObj() );
client.remove( ns(), BSONObj() );
client.insert( ns(), BSON( "r" << BSON( "$ref" << "jstests.informaldbref" << "$id" << obj["_id"].__oid() << "foo" << "bar" ) ) );
obj = client.findOne( ns(), BSONObj() );
ASSERT_EQUALS( "bar", obj[ "r" ].embeddedObject()[ "foo" ].str() );
// Don't need to return anything because we are overriding both jsonOut and jsonIn
virtual string json() const { return ""; }
ASSERT( client.eval( "unittest", "x = db.jstests.informaldbref.findOne(); y = { r:x.r }; db.jstests.informaldbref.drop(); y.r[ \"a\" ] = \"b\"; db.jstests.informaldbref.save( y );" ) );
obj = client.findOne( ns(), BSONObj() );
ASSERT_EQUALS( "bar", obj[ "r" ].embeddedObject()[ "foo" ].str() );
ASSERT_EQUALS( "b", obj[ "r" ].embeddedObject()[ "a" ].str() );
}
private:
static const char *ns() { return "unittest.jstests.informaldbref"; }
};
// Need to override these because the JSON doesn't actually round trip.
// An object with "$ref" and "$id" fields is handled specially and different on the way out.
virtual string jsonOut() const {
return "{ \"a\" : DBRef( \"ns\", \"000000000000000000000000\" ) }";
}
virtual string jsonIn() const {
stringstream ss;
ss << "{ \"a\" : { \"$ref\" : \"ns\" , " <<
"\"$id\" : \"000000000000000000000000\" } }";
return ss.str();
}
};
class InformalDBRefOIDTest : public TestRoundTrip {
virtual BSONObj bson() const {
BSONObjBuilder b;
OID o;
memset( &o, 0, 12 );
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", o);
subBuilder.done();
return b.obj();
}
// Don't need to return anything because we are overriding both jsonOut and jsonIn
virtual string json() const { return ""; }
// Need to override these because the JSON doesn't actually round trip.
// An object with "$ref" and "$id" fields is handled specially and different on the way out.
virtual string jsonOut() const {
return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
}
virtual string jsonIn() const {
stringstream ss;
ss << "{ \"a\" : { \"$ref\" : \"ns\" , " <<
"\"$id\" : ObjectId( \"000000000000000000000000\" ) } }";
return ss.str();
}
};
class InformalDBRefExtraFieldTest : public TestRoundTrip {
virtual BSONObj bson() const {
BSONObjBuilder b;
OID o;
memset( &o, 0, 12 );
BSONObjBuilder subBuilder(b.subobjStart("a"));
subBuilder.append("$ref", "ns");
subBuilder.append("$id", o);
subBuilder.append("otherfield", "value");
subBuilder.done();
return b.obj();
}
// Don't need to return anything because we are overriding both jsonOut and jsonIn
virtual string json() const { return ""; }
// Need to override these because the JSON doesn't actually round trip.
// An object with "$ref" and "$id" fields is handled specially and different on the way out.
virtual string jsonOut() const {
return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
}
virtual string jsonIn() const {
stringstream ss;
ss << "{ \"a\" : { \"$ref\" : \"ns\" , " <<
"\"$id\" : ObjectId( \"000000000000000000000000\" ) , " <<
"\"otherfield\" : \"value\" } }";
return ss.str();
}
};
} // namespace RoundTripTests
class BinDataType {
public:
@@ -1094,7 +1233,7 @@ namespace JSTests {
Timer t;
double n = 0;
for ( ; n < 100000; n++ ) {
for ( ; n < 10000 ; n++ ) {
s->invoke( f , &empty, &start );
ASSERT_EQUALS( 11 , s->getNumber( "__returnValue" ) );
}
@@ -1173,10 +1312,12 @@ namespace JSTests {
class All : public Suite {
public:
All() : Suite( "js" ) {
// Initialize the Javascript interpreter
ScriptEngine::setup();
}
void setupTests() {
add< Fundamental >();
add< BuiltinTests >();
add< BasicScope >();
add< ResetScope >();
add< FalseTests >();
@@ -1202,8 +1343,6 @@ namespace JSTests {
add< WeirdObjects >();
add< CodeTests >();
add< DBRefTest >();
add< InformalDBRef >();
add< BinDataType >();
add< VarTests >();
@@ -1216,6 +1355,12 @@ namespace JSTests {
add< ScopeOut >();
add< InvalidStoredJS >();
add< RoundTripTests::DBRefTest >();
add< RoundTripTests::DBPointerTest >();
add< RoundTripTests::InformalDBRefTest >();
add< RoundTripTests::InformalDBRefOIDTest >();
add< RoundTripTests::InformalDBRefExtraFieldTest >();
}
} myall;

View File

@@ -24,6 +24,7 @@ using std::string;
using std::vector;
namespace mongo {
MockRemoteDBServer::CircularBSONIterator::CircularBSONIterator(
const vector<BSONObj>& replyVector) {
for (std::vector<mongo::BSONObj>::const_iterator iter = replyVector.begin();
@@ -54,6 +55,7 @@ namespace mongo {
_cmdCount(0),
_queryCount(0),
_instanceID(0) {
insert(IdentityNS, BSON(HostField(hostAndPort)), 0);
}
MockRemoteDBServer::~MockRemoteDBServer() {

View File

@@ -19,12 +19,17 @@
#include <string>
#include <vector>
#include "mongo/bson/bson_field.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/client/dbclientinterface.h"
#include "mongo/platform/unordered_map.h"
#include "mongo/util/concurrency/spin_lock.h"
namespace mongo {
const std::string IdentityNS("local.me");
const BSONField<string> HostField("host");
/**
* A very simple mock that acts like a database server. Every object keeps track of its own
* InstanceID, which initially starts at zero and increments every time it is restarted.
@@ -51,6 +56,9 @@ namespace mongo {
* 1. hostAndPort of this server should start with $.
* 2. No other instance has the same hostAndPort as this.
*
* This server will also contain the hostAndPort inside the IdentityNS
* collection. This is convenient for testing query routing.
*
* @param hostAndPort the host name with port for this server.
*
* @see MockConnRegistry
@@ -70,7 +78,7 @@ namespace mongo {
/**
* Shuts down this server. Any operations on this server with an InstanceID
* less than or equal to the current one will throw a mongo::SocketException.
* To bring the server up again, use the #reboot method.
* To bring the server up again, use the reboot method.
*/
void shutdown();

View File

@@ -1414,6 +1414,33 @@ namespace mongo_test {
ASSERT_EQUALS("b", lastHost.host());
}
TEST(TagSet, CopyConstructor) {
TagSet* copy;
{
BSONArrayBuilder builder;
builder.append(BSON("dc" << "nyc"));
builder.append(BSON("priority" << "1"));
TagSet original(builder.arr());
original.next();
copy = new TagSet(original);
}
ASSERT_FALSE(copy->isExhausted());
ASSERT(copy->getCurrentTag().equal(BSON("dc" << "nyc")));
copy->next();
ASSERT_FALSE(copy->isExhausted());
ASSERT(copy->getCurrentTag().equal(BSON("priority" << "1")));
copy->next();
ASSERT(copy->isExhausted());
delete copy;
}
TEST(TagSet, NearestMultiTagsNoMatch) {
vector<ReplicaSetMonitor::Node> nodes =
NodeSetFixtures::getThreeMemberWithTags();

View File

@@ -19,6 +19,7 @@
#include "mongo/client/connpool.h"
#include "mongo/db/namespacestring.h"
#include "mongo/s/cluster_client_internal.h"
#include "mongo/util/timer.h"
namespace mongo {
@@ -249,18 +250,62 @@ namespace mongo {
return e.toStatus("could not create indexes in new collection");
}
// Copy data over
try {
ScopedDbConnection& conn = *connPtr;
scoped_ptr<DBClientCursor> cursor(_safeCursor(conn->query(fromNS, BSONObj())));
//
// Copy data over in batches. A batch size here is way smaller than the maximum size of
// a bsonobj. We want to copy efficiently but we don't need to maximize the object size
// here.
//
Timer t;
int64_t docCount = 0;
const int32_t maxBatchSize = BSONObjMaxUserSize / 16;
try {
log() << "About to copy " << fromNS << " to " << toNS << endl;
// Lower the query's batchSize so that we incur in getMore()'s more frequently.
// The rationale here is that, if for some reason the config server is extremely
// slow, we wouldn't time this cursor out.
ScopedDbConnection& conn = *connPtr;
scoped_ptr<DBClientCursor> cursor(_safeCursor(conn->query(fromNS,
BSONObj(),
0 /* nToReturn */,
0 /* nToSkip */,
NULL /* fieldsToReturn */,
0 /* queryOptions */,
1024 /* batchSize */)));
vector<BSONObj> insertBatch;
int32_t insertSize = 0;
while (cursor->more()) {
BSONObj next = cursor->nextSafe();
BSONObj next = cursor->nextSafe().getOwned();
++docCount;
conn->insert(toNS, next);
insertBatch.push_back(next);
insertSize += next.objsize();
if (insertSize > maxBatchSize ) {
conn->insert(toNS, insertBatch);
_checkGLE(conn);
insertBatch.clear();
insertSize = 0;
}
if (t.seconds() >= 10) {
t.reset();
log() << "Copied " << docCount << " documents so far from "
<< fromNS << " to " << toNS << endl;
}
}
if (!insertBatch.empty()) {
conn->insert(toNS, insertBatch);
_checkGLE(conn);
}
log() << "Finished copying " << docCount << " documents from "
<< fromNS << " to " << toNS << endl;
}
catch (const DBException& e) {
return e.toStatus("could not copy data into new collection");

View File

@@ -203,6 +203,13 @@ namespace mongo {
_cursors.erase( id );
}
void CursorCache::removeRef( long long id ) {
verify( id );
scoped_lock lk( _mutex );
_refs.erase( id );
_refsNS.erase( id );
}
void CursorCache::storeRef(const std::string& server, long long id, const std::string& ns) {
LOG(_myLogLevel) << "CursorCache::storeRef server: " << server << " id: " << id << endl;
verify( id );

View File

@@ -105,6 +105,7 @@ namespace mongo {
void remove( long long id );
void storeRef(const std::string& server, long long id, const std::string& ns);
void removeRef( long long id );
/** @return the server for id or "" */
string getRef( long long id ) const ;

View File

@@ -58,6 +58,7 @@
#include "mongo/s/d_logic.h"
#include "mongo/s/shard.h"
#include "mongo/s/type_chunk.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/elapsed_tracker.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/queue.h"
@@ -999,7 +1000,7 @@ namespace mongo {
dist_lock_try dlk;
try{
dlk = dist_lock_try( &lockSetup , (string)"migrate-" + min.toString() );
dlk = dist_lock_try( &lockSetup , (string)"migrate-" + min.toString(), 30.0 /*timeout*/ );
}
catch( LockException& e ){
errmsg = str::stream() << "error locking distributed lock for migration " << "migrate-" << min.toString() << causedBy( e );
@@ -1214,6 +1215,22 @@ namespace mongo {
timing.done(4);
// 5.
// Before we get into the critical section of the migration, let's double check
// that the config servers are reachable and the lock is in place.
log() << "About to check if it is safe to enter critical section";
string lockHeldMsg;
bool lockHeld = dlk.isLockHeld( 30.0 /* timeout */, &lockHeldMsg );
if ( !lockHeld ) {
errmsg = str::stream() << "not entering migrate critical section because "
<< lockHeldMsg;
warning() << errmsg << endl;
return false;
}
log() << "About to enter migrate critical section";
{
// 5.a
// we're under the collection lock here, so no other migrate can change maxVersion or ShardChunkManager state
@@ -1386,6 +1403,7 @@ namespace mongo {
BSONObj cmd = cmdBuilder.obj();
LOG(7) << "moveChunk update: " << cmd << migrateLog;
int exceptionCode = OkCode;
bool ok = false;
BSONObj cmdResult;
try {
@@ -1399,12 +1417,39 @@ namespace mongo {
catch ( DBException& e ) {
warning() << e << migrateLog;
ok = false;
exceptionCode = e.getCode();
BSONObjBuilder b;
e.getInfo().append( b );
cmdResult = b.obj();
errmsg = cmdResult.toString();
}
if ( ! ok ) {
if ( exceptionCode == PrepareConfigsFailedCode ) {
// In the process of issuing the migrate commit, the SyncClusterConnection
// checks that the config servers are reachable. If they are not, we are
// sure that the applyOps command was not sent to any of the configs, so we
// can safely back out of the migration here, by resetting the shard
// version that we bumped up to in the donateChunk() call above.
log() << "About to acquire moveChunk global lock to reset shard version from "
<< "failed migration" << endl;
{
Lock::GlobalWrite lk;
// Revert the chunk manager back to the state before "forgetting"
// about the chunk.
shardingState.undoDonateChunk( ns , min , max , startingVersion );
}
log() << "Shard version successfully reset to clean up failed migration" << endl;
errmsg = "Failed to send migrate commit to configs because " + errmsg;
return false;
}
else if ( ! ok || exceptionCode != OkCode ) {
// this could be a blip in the connectivity
// wait out a few seconds and check if the commit request made it
@@ -1433,6 +1478,7 @@ namespace mongo {
if ( checkVersion.isEquivalentTo( nextVersion ) ) {
log() << "moveChunk commit confirmed" << migrateLog;
errmsg.clear();
}
else {

View File

@@ -107,6 +107,12 @@ namespace mongo {
try {
r.init();
r.process();
// Release connections after non-write op
if ( ShardConnection::releaseConnectionsAfterResponse && r.expectResponse() ) {
LOG(2) << "release thread local connections back to pool" << endl;
ShardConnection::releaseMyConnections();
}
}
catch ( AssertionException & e ) {
LOG( e.isUserAssertion() ? 1 : 0 ) << "AssertionException while processing op type : " << m.operation() << " to : " << r.getns() << causedBy(e) << endl;

View File

@@ -290,9 +290,17 @@ namespace mongo {
*/
bool runCommand( const string& db , const BSONObj& cmd , BSONObj& res );
static bool releaseConnectionsAfterResponse;
/** checks all of my thread local connections for the version of this ns */
static void checkMyConnectionVersions( const string & ns );
/**
* Returns all the current sharded connections to the pool.
* Note: This is *dangerous* if we have GLE state.
*/
static void releaseMyConnections();
/**
* Clears all connections in the sharded pool, including connections in the
* thread local storage pool of the current thread.

View File

@@ -21,6 +21,8 @@
#include <set>
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
#include "mongo/db/server_parameters.h"
#include "mongo/s/config.h"
#include "mongo/s/request.h"
#include "mongo/s/shard.h"
@@ -33,6 +35,70 @@ namespace mongo {
DBConnectionPool shardConnectionPool;
class ClientConnections;
/**
* Class which tracks ClientConnections (the client connection pool) for each incoming
* connection, allowing stats access.
*/
class ActiveClientConnections {
public:
ActiveClientConnections() : _mutex( "ActiveClientConnections" ) {
}
void add( const ClientConnections* cc ) {
scoped_lock lock( _mutex );
_clientConnections.insert( cc );
}
void remove( const ClientConnections* cc ) {
scoped_lock lock( _mutex );
_clientConnections.erase( cc );
}
// Implemented after ClientConnections
void appendInfo( BSONObjBuilder& b );
private:
mongo::mutex _mutex;
set<const ClientConnections*> _clientConnections;
} activeClientConnections;
/**
* Command to allow access to the sharded conn pool information in mongos.
* TODO: Refactor with other connection pooling changes
*/
class ShardedPoolStats : public Command {
public:
ShardedPoolStats() : Command( "shardConnPoolStats" ) {}
virtual void help( stringstream &help ) const { help << "stats about the shard connection pool"; }
virtual LockType locktype() const { return NONE; }
virtual bool slaveOk() const { return true; }
// Same privs as connPoolStats
virtual void addRequiredPrivileges( const std::string& dbname,
const BSONObj& cmdObj,
std::vector<Privilege>* out )
{
ActionSet actions;
actions.addAction( ActionType::connPoolStats );
out->push_back( Privilege( AuthorizationManager::SERVER_RESOURCE_NAME, actions ) );
}
virtual bool run ( const string&, mongo::BSONObj&, int, std::string&, mongo::BSONObjBuilder& result, bool ) {
// Base pool info
shardConnectionPool.appendInfo( result );
// Thread connection info
activeClientConnections.appendInfo( result );
return true;
}
} shardedPoolStatsCmd;
/**
* holds all the actual db connections for a client to various servers
* 1 per thread, so doesn't have to be thread safe
@@ -42,14 +108,39 @@ namespace mongo {
struct Status : boost::noncopyable {
Status() : created(0), avail(0) {}
// May be read concurrently, but only written from
// this thread.
long long created;
DBClientBase* avail;
};
// Gets or creates the status object for the host
Status* _getStatus( const string& addr ) {
scoped_spinlock lock( _lock );
Status* &temp = _hosts[addr];
if ( ! temp )
temp = new Status();
return temp;
}
ClientConnections() {}
ClientConnections() {
// Start tracking client connections
activeClientConnections.add( this );
}
~ClientConnections() {
// Stop tracking these client connections
activeClientConnections.remove( this );
releaseAll( true );
}
void releaseAll( bool fromDestructor = false ) {
// Don't need spinlock protection because if not in the destructor, we don't
// modify _hosts, and if in the destructor we are not accessible to external
// threads.
for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
string addr = i->first;
Status* ss = i->second;
@@ -65,17 +156,15 @@ namespace mongo {
release( addr , ss->avail );
ss->avail = 0;
}
delete ss;
if ( fromDestructor ) delete ss;
}
_hosts.clear();
if ( fromDestructor ) _hosts.clear();
}
DBClientBase * get( const string& addr , const string& ns ) {
_check( ns );
Status* &s = _hosts[addr];
if ( ! s )
s = new Status();
Status* s = _getStatus( addr );
auto_ptr<DBClientBase> c; // Handles cleanup if there's an exception thrown
if ( s->avail ) {
@@ -83,8 +172,8 @@ namespace mongo {
s->avail = 0;
shardConnectionPool.onHandedOut( c.get() ); // May throw an exception
} else {
s->created++;
c.reset( shardConnectionPool.get( addr ) );
s->created++; // After, so failed creation doesn't get counted
}
return c.release();
}
@@ -152,18 +241,19 @@ namespace mongo {
Shard& shard = all[i];
try {
string sconnString = shard.getConnString();
Status* &s = _hosts[sconnString];
Status* s = _getStatus( sconnString );
if ( ! s ){
s = new Status();
if( ! s->avail ) {
s->avail = shardConnectionPool.get( sconnString );
s->created++; // After, so failed creation doesn't get counted
}
if( ! s->avail )
s->avail = shardConnectionPool.get( sconnString );
versionManager.checkShardVersionCB( s->avail, ns, false, 1 );
} catch(...) {
LOGATMOST(2) << "exception in checkAllVersions shard:" << shard.getName() << endl;
}
catch ( const std::exception& e ) {
warning() << "problem while initially checking shard versions on"
<< " " << shard.getName() << causedBy(e) << endl;
throw;
}
}
@@ -174,12 +264,47 @@ namespace mongo {
}
void _check( const string& ns ) {
if ( ns.size() == 0 || _seenNS.count( ns ) )
return;
_seenNS.insert( ns );
{
// We want to report ns stats too
scoped_spinlock lock( _lock );
if ( ns.size() == 0 || _seenNS.count( ns ) )
return;
_seenNS.insert( ns );
}
checkVersions( ns );
}
/**
* Appends info about the client connection pool to a BOBuilder
* Safe to call with activeClientConnections lock
*/
void appendInfo( BSONObjBuilder& b ) const {
scoped_spinlock lock( _lock );
BSONArrayBuilder hostsArrB( b.subarrayStart( "hosts" ) );
for ( HostMap::const_iterator i = _hosts.begin(); i != _hosts.end(); ++i ) {
BSONObjBuilder bb( hostsArrB.subobjStart() );
bb.append( "host", i->first );
bb.append( "created", i->second->created );
bb.appendBool( "avail", static_cast<bool>( i->second->avail ) );
bb.done();
}
hostsArrB.done();
BSONArrayBuilder nsArrB( b.subarrayStart( "seenNS" ) );
for ( set<string>::const_iterator i = _seenNS.begin(); i != _seenNS.end(); ++i ) {
nsArrB.append(*i);
}
nsArrB.done();
}
// Protects only the creation of new entries in the _hosts and _seenNS map
// from external threads. Reading _hosts / _seenNS in this thread doesn't
// need protection.
mutable SpinLock _lock;
typedef map<string,Status*,DBConnectionPool::serverNameCompare> HostMap;
HostMap _hosts;
set<string> _seenNS;
@@ -213,6 +338,27 @@ namespace mongo {
thread_specific_ptr<ClientConnections> ClientConnections::_perThread;
/**
* Appends info about all active client shard connections to a BOBuilder
*/
void ActiveClientConnections::appendInfo( BSONObjBuilder& b ) {
BSONArrayBuilder arr( 64 * 1024 ); // There may be quite a few threads
{
scoped_lock lock( _mutex );
for ( set<const ClientConnections*>::const_iterator i = _clientConnections.begin();
i != _clientConnections.end(); ++i )
{
BSONObjBuilder bb( arr.subobjStart() );
(*i)->appendInfo( bb );
bb.done();
}
}
b.appendArray( "threads", arr.obj() );
}
ShardConnection::ShardConnection( const Shard * s , const string& ns, ChunkManagerPtr manager )
: _addr( s->getConnString() ) , _ns( ns ), _manager( manager ) {
_init();
@@ -323,6 +469,20 @@ namespace mongo {
}
}
bool ShardConnection::releaseConnectionsAfterResponse( false );
ExportedServerParameter<bool> ReleaseConnectionsAfterResponse(
ServerParameterSet::getGlobal(),
"releaseConnectionsAfterResponse",
&ShardConnection::releaseConnectionsAfterResponse,
true,
true
);
void ShardConnection::releaseMyConnections() {
ClientConnections::threadInstance()->releaseAll();
}
void ShardConnection::clearPool() {
shardConnectionPool.clear();
ClientConnections::threadInstance()->clearPool();

View File

@@ -49,8 +49,10 @@ namespace mongo {
for(set<string>::const_iterator it = patternfields.begin(); it != patternfields.end(); ++it) {
BSONElement e = obj.getFieldDotted(it->c_str());
if(e.eoo() || e.type() == Array || (e.type() == Object && e.embeddedObject().firstElementFieldName()[0] == '$')) {
// cant use getGtLtOp here as it returns Equality for unknown $ops and we want to reject them
if( e.eoo() ||
e.type() == Array ||
(e.type() == Object && !e.embeddedObject().okForStorage())) {
// Don't allow anything for a shard key we can't store -- like $gt/$lt ops
return false;
}
}
@@ -143,16 +145,26 @@ namespace mongo {
public:
void hasshardkeytest() {
BSONObj x = fromjson("{ zid : \"abcdefg\", num: 1.0, name: \"eliot\" }");
ShardKeyPattern k( BSON( "num" << 1 ) );
BSONObj x = fromjson("{ zid : \"abcdefg\", num: 1.0, name: \"eliot\" }");
verify( k.hasShardKey(x) );
verify( !k.hasShardKey( fromjson("{foo:'a'}") ) );
verify( !k.hasShardKey( fromjson("{x: {$gt: 1}}") ) );
verify( !k.hasShardKey( fromjson("{num: {$gt: 1}}") ) );
BSONObj obj = BSON( "num" << BSON( "$ref" << "coll" << "$id" << 1));
verify( k.hasShardKey(obj));
// try compound key
{
ShardKeyPattern k( fromjson("{a:1,b:-1,c:1}") );
verify( k.hasShardKey( fromjson("{foo:'a',a:'b',c:'z',b:9,k:99}") ) );
BSONObj obj = BSON( "foo" << "a" <<
"a" << BSON("$ref" << "coll" << "$id" << 1) <<
"c" << 1 << "b" << 9 << "k" << 99 );
verify( k.hasShardKey( obj ) );
verify( !k.hasShardKey( fromjson("{foo:'a',a:[1,2],c:'z',b:9,k:99}") ) );
verify( !k.hasShardKey( fromjson("{foo:'a',a:{$gt:1},c:'z',b:9,k:99}") ) );
verify( !k.hasShardKey( fromjson("{foo:'a',a:'b',c:'z',bb:9,k:99}") ) );
verify( !k.hasShardKey( fromjson("{k:99}") ) );
}
@@ -162,7 +174,16 @@ namespace mongo {
ShardKeyPattern k( fromjson("{'a.b':1}") );
verify( k.hasShardKey( fromjson("{a:{b:1,c:1},d:1}") ) );
verify( k.hasShardKey( fromjson("{'a.b':1}") ) );
BSONObj obj = BSON( "c" << "a" <<
"a" << BSON("$ref" << "coll" << "$id" << 1) );
verify( !k.hasShardKey( obj ) );
obj = BSON( "c" << "a" <<
"a" << BSON( "b" << BSON("$ref" << "coll" << "$id" << 1) <<
"c" << 1));
verify( k.hasShardKey( obj ) );
verify( !k.hasShardKey( fromjson("{'a.c':1}") ) );
verify( !k.hasShardKey( fromjson("{'a':[{b:1}, {c:1}]}") ) );
verify( !k.hasShardKey( fromjson("{a:{b:[1,2]},d:1}") ) );
verify( !k.hasShardKey( fromjson("{a:{c:1},d:1}") ) );
verify( !k.hasShardKey( fromjson("{a:1}") ) );
verify( !k.hasShardKey( fromjson("{b:1}") ) );

View File

@@ -212,8 +212,14 @@ namespace mongo {
Message response;
bool ok = conn->get()->callRead( r.m() , response);
uassert( 10204 , "dbgrid: getmore: error calling db", ok);
r.reply( response , "" /*conn->getServerAddress() */ );
bool hasMore = (response.singleData()->getCursor() != 0);
if ( !hasMore ) {
cursorCache.removeRef( id );
}
r.reply( response , "" /*conn->getServerAddress() */ );
conn->done();
return;
}

View File

@@ -1071,11 +1071,25 @@ namespace spidermonkey {
JSBool native_helper( JSContext *cx , JSObject *obj , uintN argc, jsval *argv , jsval *rval ) {
try {
Convertor c(cx);
NativeFunction func = reinterpret_cast<NativeFunction>(
static_cast<long long>( c.getNumber( obj , "x" ) ) );
void* data = reinterpret_cast<void*>(
static_cast<long long>( c.getNumber( obj , "y" ) ) );
verify( func );
// get function pointer from JS caller's argument property 'x'
massert(16740, "nativeHelper argument requires object with 'x' property",
c.hasProperty(obj, "x"));
FunctionMap::iterator funcIter = currentScope->_functionMap.find(c.getNumber(obj, "x"));
massert(16742, "JavaScript function not in map",
funcIter != currentScope->_functionMap.end());
NativeFunction func = funcIter->second;
verify(func);
// get data pointer from JS caller's argument property 'y'
void* data = NULL;
if (c.hasProperty(obj, "y")) {
ArgumentMap::iterator argIter =
currentScope->_argumentMap.find(c.getNumber(obj, "y"));
massert(16741, "nativeHelper 'y' parameter must be in the argumentMap",
argIter != currentScope->_argumentMap.end());
data = argIter->second;
}
BSONObj a;
if ( argc > 0 ) {
@@ -1725,12 +1739,16 @@ namespace spidermonkey {
smlock;
string name = field;
jsval v;
v = _convertor->toval( static_cast<double>( reinterpret_cast<long long>(func) ) );
uint32_t funcId = _functionMap.size();
_functionMap.insert(make_pair(funcId, func));
v = _convertor->toval(static_cast<double>(funcId));
_convertor->setProperty( _global, (name + "_").c_str(), v );
stringstream code;
if (data) {
v = _convertor->toval( static_cast<double>( reinterpret_cast<long long>(data) ) );
uint32_t argsId = _argumentMap.size();
_argumentMap.insert(make_pair(argsId, data));
v = _convertor->toval(static_cast<double>(argsId));
_convertor->setProperty( _global, (name + "_data_").c_str(), v );
code << field << "_" << " = { x : " << field << "_ , y: " << field << "_data_ }; ";
} else {

View File

@@ -47,6 +47,9 @@ namespace spidermonkey {
using std::string;
typedef std::map<uint32_t, NativeFunction> FunctionMap;
typedef std::map<uint32_t, void*> ArgumentMap;
string trim( string s );
class BSONFieldIterator;
@@ -295,6 +298,11 @@ namespace spidermonkey {
JSContext *SavedContext() const { return _context; }
// map from internal function id to function pointer
FunctionMap _functionMap;
// map from internal function argument id to function pointer
ArgumentMap _argumentMap;
private:
void _postCreateHacks();

View File

@@ -17,6 +17,7 @@
#include "mongo/scripting/engine_v8.h"
#include "mongo/platform/unordered_set.h"
#include "mongo/scripting/v8_db.h"
#include "mongo/scripting/v8_utils.h"
#include "mongo/util/base64.h"
@@ -52,10 +53,15 @@ namespace mongo {
return (BSONHolder*)ptr;
}
static v8::Handle<v8::Object> unwrapObject(const v8::Handle<v8::Object>& obj) {
return obj->GetInternalField(1).As<v8::Object>();
}
v8::Persistent<v8::Object> V8Scope::wrapBSONObject(v8::Local<v8::Object> obj,
BSONHolder* data) {
data->_scope = this;
obj->SetInternalField(0, v8::External::New(data));
obj->SetInternalField(0, v8::External::New(data)); // Holder
obj->SetInternalField(1, v8::Object::New()); // Object
v8::Persistent<v8::Object> p = v8::Persistent<v8::Object>::New(obj);
bsonHolderTracker.track(p, data);
return p;
@@ -66,9 +72,10 @@ namespace mongo {
v8::HandleScope handle_scope;
v8::Handle<v8::Value> val;
try {
if (info.This()->HasRealNamedProperty(name)) {
// value already cached
return handle_scope.Close(info.This()->GetRealNamedProperty(name));
v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
if (realObject->HasOwnProperty(name)) {
// value already cached or added
return handle_scope.Close(realObject->Get(name));
}
string key = toSTLString(name);
@@ -83,8 +90,12 @@ namespace mongo {
v8::Local<v8::External> scp = v8::External::Cast(*info.Data());
V8Scope* scope = (V8Scope*)(scp->Value());
val = scope->mongoToV8Element(elmt, false);
info.This()->ForceSet(name, val, v8::DontEnum);
val = scope->mongoToV8Element(elmt, holder->_readOnly);
if (obj.objsize() > 128 || val->IsObject()) {
// Only cache if expected to help (large BSON) or is required due to js semantics
realObject->Set(name, val);
}
if (elmt.type() == mongo::Object || elmt.type() == mongo::Array) {
// if accessing a subobject, it may get modified and base obj would not know
@@ -103,6 +114,9 @@ namespace mongo {
static v8::Handle<v8::Value> namedGetRO(v8::Local<v8::String> name,
const v8::AccessorInfo &info) {
return namedGet(name, info);
// Rest of function is unused but left in to ease backporting of SERVER-9267
v8::HandleScope handle_scope;
v8::Handle<v8::Value> val;
string key = toSTLString(name);
@@ -130,25 +144,25 @@ namespace mongo {
string key = toSTLString(name);
BSONHolder* holder = unwrapHolder(info.Holder());
holder->_removed.erase(key);
holder->_extra.push_back(key);
holder->_modified = true;
// set into JS object
return v8::Handle<v8::Value>();
v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
realObject->Set(name, value_obj);
return value_obj;
}
static v8::Handle<v8::Array> namedEnumerator(const v8::AccessorInfo &info) {
v8::HandleScope handle_scope;
BSONHolder* holder = unwrapHolder(info.Holder());
BSONObj obj = holder->_obj;
v8::Handle<v8::Array> arr = v8::Handle<v8::Array>(v8::Array::New(obj.nFields()));
int i = 0;
v8::Handle<v8::Array> out = v8::Array::New();
int outIndex = 0;
v8::Local<v8::External> scp = v8::External::Cast(*info.Data());
V8Scope* scope = (V8Scope*)(scp->Value());
set<string> added;
unordered_set<string> added;
// note here that if keys are parseable number, v8 will access them using index
for (BSONObjIterator it(obj); it.more(); ++i) {
for (BSONObjIterator it(obj); it.more();) {
const BSONElement& f = it.next();
string sname = f.fieldName();
if (holder->_removed.count(sname))
@@ -156,17 +170,21 @@ namespace mongo {
v8::Handle<v8::String> name = scope->v8StringData(sname);
added.insert(sname);
arr->Set(i, name);
out->Set(outIndex++, name);
}
for (list<string>::iterator it = holder->_extra.begin();
it != holder->_extra.end(); it++) {
string sname = *it;
v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
v8::Handle<v8::Array> fields = realObject->GetOwnPropertyNames();
const int len = fields->Length();
for (int field=0; field < len; field++) {
v8::Handle<v8::String> name = fields->Get(field).As<v8::String>();
string sname = toSTLString(name);
if (added.count(sname))
continue;
arr->Set(i++, scope->v8StringData(sname));
out->Set(outIndex++, name);
}
return handle_scope.Close(arr);
return handle_scope.Close(out);
}
v8::Handle<v8::Boolean> namedDelete(v8::Local<v8::String> name, const v8::AccessorInfo& info) {
@@ -174,26 +192,25 @@ namespace mongo {
string key = toSTLString(name);
BSONHolder* holder = unwrapHolder(info.Holder());
holder->_removed.insert(key);
holder->_extra.remove(key);
holder->_modified = true;
// also delete in JS obj
return handle_scope.Close(v8::Handle<v8::Boolean>());
v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
realObject->Delete(name);
return v8::True();
}
static v8::Handle<v8::Value> indexedGet(uint32_t index, const v8::AccessorInfo &info) {
v8::HandleScope handle_scope;
v8::Handle<v8::Value> val;
try {
v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
if (realObject->Has(index)) {
// value already cached or added
return handle_scope.Close(realObject->Get(index));
}
string key = str::stream() << index;
v8::Local<v8::External> scp = v8::External::Cast(*info.Data());
V8Scope* scope = (V8Scope*)(scp->Value());
v8::Handle<v8::String> name = scope->v8StringData(key);
if (info.This()->HasRealIndexedProperty(index)) {
// value already cached
return handle_scope.Close(info.This()->GetRealNamedProperty(name));
}
BSONHolder* holder = unwrapHolder(info.Holder());
if (holder->_removed.count(key))
@@ -203,8 +220,8 @@ namespace mongo {
BSONElement elmt = obj.getField(key);
if (elmt.eoo())
return handle_scope.Close(v8::Handle<v8::Value>());
val = scope->mongoToV8Element(elmt, false);
info.This()->ForceSet(name, val, v8::DontEnum);
val = scope->mongoToV8Element(elmt, holder->_readOnly);
realObject->Set(index, val);
if (elmt.type() == mongo::Object || elmt.type() == mongo::Array) {
// if accessing a subobject, it may get modified and base obj would not know
@@ -226,14 +243,18 @@ namespace mongo {
string key = str::stream() << index;
BSONHolder* holder = unwrapHolder(info.Holder());
holder->_removed.insert(key);
holder->_extra.remove(key);
holder->_modified = true;
// also delete in JS obj
return v8::Handle<v8::Boolean>();
v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
realObject->Delete(index);
return v8::True();
}
static v8::Handle<v8::Value> indexedGetRO(uint32_t index, const v8::AccessorInfo &info) {
return indexedGet(index, info);
// Rest of function is unused but left in-place to ease backporting of SERVER-9267
v8::HandleScope handle_scope;
v8::Handle<v8::Value> val;
try {
@@ -265,11 +286,11 @@ namespace mongo {
string key = str::stream() << index;
BSONHolder* holder = unwrapHolder(info.Holder());
holder->_removed.erase(key);
holder->_extra.push_back(key);
holder->_modified = true;
// set into JS object
return v8::Handle<v8::Value>();
v8::Handle<v8::Object> realObject = unwrapObject(info.Holder());
realObject->Set(index, value_obj);
return value_obj;
}
v8::Handle<v8::Value> NamedReadOnlySet(v8::Local<v8::String> property,
@@ -298,6 +319,15 @@ namespace mongo {
return v8::Boolean::New(false);
}
/**
* GC Prologue and Epilogue constants (used to display description constants)
*/
struct GCPrologueState { static const char* name; };
const char* GCPrologueState::name = "prologue";
struct GCEpilogueState { static const char* name; };
const char* GCEpilogueState::name = "epilogue";
template <typename _GCState>
void gcCallback(v8::GCType type, v8::GCCallbackFlags flags) {
const int verbosity = 1; // log level for stat collection
if (logLevel < verbosity)
@@ -306,12 +336,13 @@ namespace mongo {
v8::HeapStatistics stats;
v8::V8::GetHeapStatistics(&stats);
LOG(verbosity) << "V8 GC heap stats - "
<< " total: " << stats.total_heap_size()
<< " exec: " << stats.total_heap_size_executable()
<< " used: " << stats.used_heap_size()<< " limit: "
<< stats.heap_size_limit()
<< endl;
log() << "V8 GC " << _GCState::name
<< " heap stats - "
<< " total: " << stats.total_heap_size()
<< " exec: " << stats.total_heap_size_executable()
<< " used: " << stats.used_heap_size()<< " limit: "
<< stats.heap_size_limit()
<< endl;
}
V8ScriptEngine::V8ScriptEngine() :
@@ -455,12 +486,6 @@ namespace mongo {
_isolate = v8::Isolate::New();
v8::Isolate::Scope iscope(_isolate);
// resource constraints must be set on isolate, before any call or lock
v8::ResourceConstraints rc;
rc.set_max_young_space_size(4 * 1024 * 1024);
rc.set_max_old_space_size(64 * 1024 * 1024);
v8::SetResourceConstraints(&rc);
// lock the isolate and enter the context
v8::Locker l(_isolate);
v8::HandleScope handleScope;
@@ -468,7 +493,8 @@ namespace mongo {
v8::Context::Scope context_scope(_context);
// display heap statistics on MarkAndSweep GC run
v8::V8::AddGCPrologueCallback(gcCallback, v8::kGCTypeMarkSweepCompact);
v8::V8::AddGCPrologueCallback(gcCallback<GCPrologueState>, v8::kGCTypeMarkSweepCompact);
v8::V8::AddGCEpilogueCallback(gcCallback<GCEpilogueState>, v8::kGCTypeMarkSweepCompact);
// if the isolate runs out of heap space, raise a flag on the StackGuard instead of
// calling abort()
@@ -479,7 +505,7 @@ namespace mongo {
// initialize lazy object template
lzObjectTemplate = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
lzObjectTemplate->SetInternalFieldCount(1);
lzObjectTemplate->SetInternalFieldCount(2);
lzObjectTemplate->SetNamedPropertyHandler(namedGet, namedSet, 0, namedDelete,
namedEnumerator, v8::External::New(this));
lzObjectTemplate->SetIndexedPropertyHandler(indexedGet, indexedSet, 0, indexedDelete,
@@ -490,7 +516,7 @@ namespace mongo {
v8::DontEnum);
roObjectTemplate = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
roObjectTemplate->SetInternalFieldCount(1);
roObjectTemplate->SetInternalFieldCount(2);
roObjectTemplate->SetNamedPropertyHandler(namedGetRO, NamedReadOnlySet, 0,
NamedReadOnlyDelete, namedEnumerator,
v8::External::New(this));
@@ -1250,188 +1276,13 @@ namespace mongo {
return handle_scope.Close(idCons->NewInstance(1, argv));
}
v8::Local<v8::Object> V8Scope::mongoToV8(const BSONObj& m, bool array, bool readOnly) {
v8::HandleScope handle_scope;
v8::Handle<v8::Value> argv[3]; // arguments for v8 instance constructors
v8::Local<v8::ObjectTemplate> readOnlyObjects;
v8::Local<v8::Object> o;
// handle DBRef. needs to come first. isn't it? (metagoto)
static string ref = "$ref";
if (ref == m.firstElement().fieldName()) {
const BSONElement& id = m["$id"];
if (!id.eoo()) { // there's no check on $id exitence in sm implementation. risky ?
v8::Function* dbRef = getNamedCons("DBRef");
o = dbRef->NewInstance();
}
}
if (!o.IsEmpty()) {
readOnly = false;
}
else if (array) {
// NOTE Looks like it's impossible to add interceptors to v8 arrays.
// so array itself will never be read only, but its values can be
o = v8::Array::New();
}
else if (!readOnly) {
o = v8::Object::New();
}
else {
// NOTE Our readOnly implemention relies on undocumented ObjectTemplate
// functionality that may be fragile, but it still seems like the best option
// for now -- fwiw, the v8 docs are pretty sparse. I've determined experimentally
// that when property handlers are set for an object template, they will attach
// to objects previously created by that template. To get this to work, though,
// it is necessary to initialize the template's property handlers before
// creating objects from the template (as I have in the following few lines
// of code).
// NOTE In my first attempt, I configured the permanent property handlers before
// constructiong the object and replaced the Set() calls below with ForceSet().
// However, it turns out that ForceSet() only bypasses handlers for named
// properties and not for indexed properties.
readOnlyObjects = v8::ObjectTemplate::New();
// NOTE This internal field will store type info for special db types. For
// regular objects the field is unnecessary - for simplicity I'm creating just
// one readOnlyObjects template for objects where the field is & isn't necessary,
// assuming that the overhead of an internal field is slight.
readOnlyObjects->SetInternalFieldCount(1);
readOnlyObjects->SetNamedPropertyHandler(0);
readOnlyObjects->SetIndexedPropertyHandler(0);
o = readOnlyObjects->NewInstance();
}
mongo::BSONObj sub;
for (BSONObjIterator i(m); i.more();) {
const BSONElement& f = i.next();
v8::Local<v8::Value> v;
v8::Handle<v8::String> name = v8StringData(f.fieldName());
switch (f.type()) {
case mongo::Code:
o->ForceSet(name, newFunction(f.valuestr()));
break;
case CodeWScope:
if (!f.codeWScopeObject().isEmpty())
log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
o->ForceSet(name, newFunction(f.codeWScopeCode()));
break;
case mongo::String:
o->ForceSet(name, v8::String::New(f.valuestr()));
break;
case mongo::jstOID: {
v8::Function * idCons = getObjectIdCons();
argv[0] = v8::String::New(f.__oid().str().c_str());
o->ForceSet(name, idCons->NewInstance(1, argv));
break;
}
case mongo::NumberDouble:
case mongo::NumberInt:
o->ForceSet(name, v8::Number::New(f.number()));
break;
case mongo::Array:
sub = f.embeddedObject();
o->ForceSet(name, mongoToV8(sub, true, readOnly));
break;
case mongo::Object:
sub = f.embeddedObject();
o->ForceSet(name, mongoToLZV8(sub, readOnly));
break;
case mongo::Date:
o->ForceSet(name, v8::Date::New((double) ((long long)f.date().millis)));
break;
case mongo::Bool:
o->ForceSet(name, v8::Boolean::New(f.boolean()));
break;
case mongo::jstNULL:
case mongo::Undefined: // duplicate sm behavior
o->ForceSet(name, v8::Null());
break;
case mongo::RegEx: {
v8::Function * regex = getNamedCons("RegExp");
argv[0] = v8::String::New(f.regex());
argv[1] = v8::String::New(f.regexFlags());
o->ForceSet(name, regex->NewInstance(2, argv));
break;
}
case mongo::BinData: {
int len;
const char *data = f.binData(len);
stringstream ss;
base64::encode(ss, data, len);
argv[0] = v8::Number::New(f.binDataType());
argv[1] = v8::String::New(ss.str().c_str());
o->ForceSet(name, getNamedCons("BinData")->NewInstance(2, argv));
break;
}
case mongo::Timestamp: {
v8::Local<v8::Object> sub = readOnly ? readOnlyObjects->NewInstance() :
internalFieldObjects->NewInstance();
sub->ForceSet(v8::String::New("t"), v8::Number::New(f.timestampTime() / 1000));
sub->ForceSet(v8::String::New("i"), v8::Number::New(f.timestampInc()));
sub->SetInternalField(0, v8::Uint32::New(f.type()));
o->ForceSet(name, sub);
break;
}
case mongo::NumberLong: {
unsigned long long val = f.numberLong();
v8::Function* numberLong = getNamedCons("NumberLong");
double floatApprox = (double)(long long)val;
// values above 2^53 are not accurately represented in JS
if ((long long)val == (long long)floatApprox && val < 9007199254740992ULL) {
argv[0] = v8::Number::New(floatApprox);
o->ForceSet(name, numberLong->NewInstance(1, argv));
}
else {
argv[0] = v8::Number::New(floatApprox);
argv[1] = v8::Integer::New(val >> 32);
argv[2] = v8::Integer::New((unsigned long)(val & 0x00000000ffffffff));
o->ForceSet(name, numberLong->NewInstance(3, argv));
}
break;
}
case mongo::MinKey: {
o->ForceSet(name, newMinKeyInstance());
break;
}
case mongo::MaxKey: {
o->ForceSet(name, newMaxKeyInstance());
break;
}
case mongo::DBRef: {
v8::Function* dbPointer = getNamedCons("DBPointer");
argv[0] = v8StringData(f.dbrefNS());
argv[1] = newId(f.dbrefOID());
o->ForceSet(name, dbPointer->NewInstance(2, argv));
break;
}
default:
cout << "can't handle type: ";
cout << f.type() << " ";
cout << f.toString();
cout << endl;
break;
}
}
if (!array && readOnly) {
readOnlyObjects->SetNamedPropertyHandler(0, NamedReadOnlySet,
0, NamedReadOnlyDelete);
readOnlyObjects->SetIndexedPropertyHandler(0, IndexedReadOnlySet,
0, IndexedReadOnlyDelete);
}
return handle_scope.Close(o);
}
/**
* converts a BSONObj to a Lazy V8 object
*/
v8::Persistent<v8::Object> V8Scope::mongoToLZV8(const BSONObj& m, bool readOnly) {
v8::Local<v8::Object> o;
BSONHolder* own = new BSONHolder(m);
own->_readOnly = readOnly;
if (readOnly) {
o = roObjectTemplate->NewInstance();
@@ -1519,6 +1370,7 @@ namespace mongo {
if (!elem.codeWScopeObject().isEmpty())
log() << "warning: CodeWScope doesn't transfer to db.eval" << endl;
return newFunction(elem.codeWScopeCode());
case mongo::Symbol:
case mongo::String:
return v8::String::New(elem.valuestr());
case mongo::jstOID:
@@ -1526,14 +1378,22 @@ namespace mongo {
case mongo::NumberDouble:
case mongo::NumberInt:
return v8::Number::New(elem.number());
case mongo::Array:
case mongo::Array: {
// NB: This comment may no longer be accurate.
// for arrays it's better to use non lazy object because:
// - the lazy array is not a true v8 array and requires some v8 src change
// for all methods to work
// - it made several tests about 1.5x slower
// - most times when an array is accessed, all its values will be used
return mongoToV8(elem.embeddedObject(), true, readOnly);
// It is faster to allow the v8::Array to grow than call nFields() on the array
v8::Handle<v8::Array> array = v8::Array::New();
int i = 0;
BSONForEach(subElem, elem.embeddedObject()) {
array->Set(i++, mongoToV8Element(subElem, readOnly));
}
return array;
}
case mongo::Object:
return mongoToLZV8(elem.embeddedObject(), readOnly);
case mongo::Date:
@@ -1745,8 +1605,14 @@ namespace mongo {
return;
}
if (value->IsArray()) {
BSONObj sub = v8ToMongo(value->ToObject(), depth);
b.appendArray(sname, sub);
// Note: can't use BSONArrayBuilder because need to call recursively
BSONObjBuilder arrBuilder(b.subarrayStart(sname));
v8::Handle<v8::Array> array = value.As<v8::Array>();
const int len = array->Length();
for (int i=0; i < len; i++) {
const string name = BSONObjBuilder::numStr(i);
v8ToMongoElement(arrBuilder, name, array->Get(i), depth+1, originalParent);
}
return;
}
if (value->IsDate()) {

View File

@@ -63,17 +63,18 @@ namespace mongo {
*/
void track(v8::Persistent<v8::Value> instanceHandle, _ObjType* instance) {
TrackedPtr* collectionHandle = new TrackedPtr(instance, this);
_container.insert(collectionHandle);
instanceHandle.MakeWeak(collectionHandle, deleteOnCollect);
}
/**
* Free any remaining objects which are being tracked. Invoked when
* the V8Scope is destructed.
* Free any remaining objects and their TrackedPtrs. Invoked when the
* V8Scope is destructed.
*/
~ObjTracker() {
if (!_container.empty())
LOG(1) << "freeing " << _container.size() << " uncollected "
<< typeid(_ObjType).name() << " objects" << endl;
typename set<_ObjType*>::iterator it = _container.begin();
typename set<TrackedPtr*>::iterator it = _container.begin();
while (it != _container.end()) {
delete *it;
_container.erase(it++);
@@ -90,7 +91,7 @@ namespace mongo {
TrackedPtr(_ObjType* instance, ObjTracker<_ObjType>* tracker) :
_objPtr(instance),
_tracker(tracker) { }
_ObjType* _objPtr;
scoped_ptr<_ObjType> _objPtr;
ObjTracker<_ObjType>* _tracker;
};
@@ -102,14 +103,13 @@ namespace mongo {
*/
static void deleteOnCollect(v8::Persistent<v8::Value> instanceHandle, void* rawData) {
TrackedPtr* trackedPtr = static_cast<TrackedPtr*>(rawData);
trackedPtr->_tracker->_container.erase(trackedPtr->_objPtr);
delete trackedPtr->_objPtr;
trackedPtr->_tracker->_container.erase(trackedPtr);
delete trackedPtr;
instanceHandle.Dispose();
}
// container for all instances of the tracked _ObjType
set<_ObjType*> _container;
// container for all TrackedPtrs created by this ObjTracker instance
set<TrackedPtr*> _container;
};
/**
@@ -213,8 +213,6 @@ namespace mongo {
/**
* Convert BSON types to v8 Javascript types
*/
v8::Local<v8::Object> mongoToV8(const mongo::BSONObj& m, bool array = 0,
bool readOnly = false);
v8::Persistent<v8::Object> mongoToLZV8(const mongo::BSONObj& m, bool readOnly = false);
v8::Handle<v8::Value> mongoToV8Element(const BSONElement& f, bool readOnly = false);
@@ -447,7 +445,7 @@ namespace mongo {
V8Scope* _scope;
BSONObj _obj;
bool _modified;
list<string> _extra;
bool _readOnly;
set<string> _removed;
};

View File

@@ -435,7 +435,7 @@ DB.prototype.repairDatabase = function() {
DB.prototype.help = function() {
print("DB methods:");
print("\tdb.addUser(username, password[, readOnly=false])");
print("\tdb.addUser(userDocument)");
print("\tdb.adminCommand(nameOrDocument) - switches to 'admin' db, and runs command [ just calls db.runCommand(...) ]");
print("\tdb.auth(username, password)");
print("\tdb.cloneDatabase(fromhost)");
@@ -761,7 +761,7 @@ DB.prototype.killOP = DB.prototype.killOp;
DB.tsToSeconds = function(x){
if ( x.t && x.i )
return x.t / 1000;
return x.t;
return x / 4294967296; // low 32 bits are ordinal #s within a second
}

View File

@@ -600,7 +600,11 @@ tojsonObject = function(x, indent, nolint){
var num = 1;
for (var k in keys){
var val = x[k];
if (val == DB.prototype || val == DBCollection.prototype)
// skip internal DB types to avoid issues with interceptors
if (typeof DB != 'undefined' && val == DB.prototype)
continue;
if (typeof DBCollection != 'undefined' && val == DBCollection.prototype)
continue;
s += indent + "\"" + k + "\" : " + tojson(val, indent, nolint);

View File

@@ -459,7 +459,6 @@ private:
int objSize;
BSONObj obj;
obj = fromjson (buf.get(), &objSize);
uassert(15934, "JSON object size didn't match file size", objSize == fileSize);
return obj;
}

View File

@@ -360,7 +360,7 @@ namespace mongo {
(int)ceil(_statUtil.getSeconds()) ) ) );
state->authParams = BSON( "user" << _username <<
"pwd" << _password <<
"userSource" << _authenticationDatabase <<
"userSource" << getAuthenticationDatabase() <<
"mechanism" << _authenticationMechanism );
return true;
}

View File

@@ -270,7 +270,7 @@ namespace mongo {
int ret = -1;
try {
if (!useDirectClient)
if (!useDirectClient && !_noconnection)
auth();
ret = run();
}
@@ -406,6 +406,18 @@ namespace mongo {
throw UserException( 9998 , "you need to specify fields" );
}
std::string Tool::getAuthenticationDatabase() {
if (!_authenticationDatabase.empty()) {
return _authenticationDatabase;
}
if (!_db.empty()) {
return _db;
}
return "admin";
}
/**
* Validate authentication on the server for the given dbname.
*/
@@ -422,17 +434,7 @@ namespace mongo {
return;
}
std::string userSource = _authenticationDatabase;
if ( userSource.empty() ) {
if ( !_db.empty() ) {
userSource = _db;
}
else {
userSource = "admin";
}
}
_conn->auth( BSON( saslCommandPrincipalSourceFieldName << userSource <<
_conn->auth( BSON( saslCommandPrincipalSourceFieldName << getAuthenticationDatabase() <<
saslCommandPrincipalFieldName << _username <<
saslCommandPasswordFieldName << _password <<
saslCommandMechanismFieldName << _authenticationMechanism ) );

View File

@@ -81,6 +81,8 @@ namespace mongo {
return _db + "." + _coll;
}
string getAuthenticationDatabase();
void useStandardOutput( bool mode ) {
_usesstdout = mode;
}

View File

@@ -28,9 +28,11 @@
namespace mongo {
enum CommonErrorCodes {
DatabaseDifferCaseCode = 13297 ,
SendStaleConfigCode = 13388 ,
RecvStaleConfigCode = 9996
OkCode = 0,
DatabaseDifferCaseCode = 13297 , // uassert( 13297 )
SendStaleConfigCode = 13388 , // uassert( 13388 )
RecvStaleConfigCode = 9996, // uassert( 9996 )
PrepareConfigsFailedCode = 13104 // uassert( 13104 )
};
class AssertionCount {

View File

@@ -47,7 +47,7 @@ namespace mongo {
* 1.2.3-rc4-pre-
* If you really need to do something else you'll need to fix _versionArray()
*/
const char versionString[] = "2.4.0";
const char versionString[] = "2.4.2";
// See unit test for example outputs
BSONArray toVersionArray(const char* version){
@@ -227,27 +227,24 @@ namespace mongo {
std::ifstream f("/proc/self/numa_maps", std::ifstream::in);
if (f.is_open()) {
char line[100]; //we only need the first line
f.getline(line, sizeof(line));
std::string line; //we only need the first line
std::getline(f, line);
if (f.fail()) {
warning() << "failed to read from /proc/self/numa_maps: "
<< errnoWithDescription() << startupWarningsLog;
warned = true;
}
else {
// just in case...
line[98] = ' ';
line[99] = '\0';
// skip over pointer
const char* space = strchr(line, ' ');
if ( ! space ) {
std::string::size_type where = line.find(' ');
if ( (where == std::string::npos) || (++where == line.size()) ) {
log() << startupWarningsLog;
log() << "** WARNING: cannot parse numa_maps" << startupWarningsLog;
log() << "** WARNING: cannot parse numa_maps line: '" << line << "'" << startupWarningsLog;
warned = true;
}
else if ( ! startsWith(space+1, "interleave") ) {
// if the text following the space doesn't begin with 'interleave', then
// issue the warning.
else if ( line.find("interleave", where) != where ) {
log() << startupWarningsLog;
log() << "** WARNING: You are running on a NUMA machine." << startupWarningsLog;
log() << "** We suggest launching mongod like this to avoid performance problems:" << startupWarningsLog;