Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e52c1a73c | ||
|
|
0ec9eca507 | ||
|
|
72acf70c11 | ||
|
|
c6ec08e021 | ||
|
|
ab76660a75 | ||
|
|
6edf4a7f08 | ||
|
|
9f073b6cae | ||
|
|
cde1ad8f45 | ||
|
|
a3dae84ade | ||
|
|
1a62390256 | ||
|
|
219986d5ca | ||
|
|
aefcffa124 | ||
|
|
ee21ceb8d4 | ||
|
|
9a846d8688 | ||
|
|
04707f7b39 | ||
|
|
81212176da | ||
|
|
822840b180 | ||
|
|
64ccdfa2f6 | ||
|
|
3bc9328524 | ||
|
|
4a472d8df3 | ||
|
|
0502e6aada | ||
|
|
f1017ee044 | ||
|
|
9e05b382ca | ||
|
|
f00104e92f | ||
|
|
e4890bf85c | ||
|
|
337b300277 | ||
|
|
3bf409d4e5 | ||
|
|
198c577fe4 | ||
|
|
85fc27444f | ||
|
|
9ba71622c9 | ||
|
|
ffe9de8df3 | ||
|
|
2e87ed15f0 | ||
|
|
6d7ac05ea2 | ||
|
|
bca711f26f | ||
|
|
9412afa6f3 | ||
|
|
7f45f0fe14 | ||
|
|
4f26c3137e | ||
|
|
6f677a8150 | ||
|
|
719d6d8207 | ||
|
|
fde9ff094d | ||
|
|
27b3840145 | ||
|
|
033f58e7d5 | ||
|
|
f9289144fc | ||
|
|
7ec1528ae3 | ||
|
|
182955c65b | ||
|
|
d54170cb2b | ||
|
|
ab32409b04 | ||
|
|
a16001b741 | ||
|
|
8301275677 | ||
|
|
4ffa043511 | ||
|
|
656008683a | ||
|
|
1b8a702436 | ||
|
|
1dc86f438b | ||
|
|
b74d1bb85d | ||
|
|
bafb7708c6 |
@@ -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,-)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ) )
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++){
|
||||
|
||||
@@ -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 );
|
||||
|
||||
62
jstests/sharding/cursor_cleanup.js
Normal file
62
jstests/sharding/cursor_cleanup.js
Normal 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();
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 } } );
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 );
|
||||
|
||||
52
jstests/slowNightly/ttl_repl_secondary_disabled.js
Normal file
52
jstests/slowNightly/ttl_repl_secondary_disabled.js
Normal 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();
|
||||
@@ -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}");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]],"
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
85
src/mongo/dbtests/gle_test.cpp
Normal file
85
src/mongo/dbtests/gle_test.cpp
Normal 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;
|
||||
}
|
||||
@@ -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 >();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}") ) );
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 ) );
|
||||
|
||||
@@ -81,6 +81,8 @@ namespace mongo {
|
||||
return _db + "." + _coll;
|
||||
}
|
||||
|
||||
string getAuthenticationDatabase();
|
||||
|
||||
void useStandardOutput( bool mode ) {
|
||||
_usesstdout = mode;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user