Files
mongo/jstests/libs/test_background_ops.js
2012-07-30 14:53:53 -04:00

341 lines
10 KiB
JavaScript

//
// Utilities related to background operations while other operations are working
//
/**
* Allows synchronization between background ops and the test operations
*/
var waitForLock = function( mongo, name ){
var ts = new ObjectId()
var lockColl = mongo.getCollection( "config.testLocks" )
lockColl.update({ _id : name, state : 0 }, { $set : { state : 0 } }, true)
//
// Wait until we can set the state to 1 with our id
//
var startTime = new Date().getTime()
assert.soon( function() {
lockColl.update({ _id : name, state : 0 }, { $set : { ts : ts, state : 1 } })
var gleObj = lockColl.getDB().getLastErrorObj()
if( new Date().getTime() - startTime > 20 * 1000 ){
print( "Waiting for..." )
printjson( gleObj )
printjson( lockColl.findOne() )
printjson( ts )
}
return gleObj.n == 1 || gleObj.updatedExisting
}, "could not acquire lock", 30 * 1000, 100 )
print( "Acquired lock " + tojson( { _id : name, ts : ts } ) + " curr : " +
tojson( lockColl.findOne({ _id : name }) ) )
// Set the state back to 0
var unlock = function(){
print( "Releasing lock " + tojson( { _id : name, ts : ts } ) + " curr : " +
tojson( lockColl.findOne({ _id : name }) ) )
lockColl.update({ _id : name, ts : ts }, { $set : { state : 0 } })
}
// Return an object we can invoke unlock on
return { unlock : unlock }
}
/**
* Allows a test or background op to say it's finished
*/
var setFinished = function( mongo, name, finished ){
if( finished || finished == undefined )
mongo.getCollection( "config.testFinished" ).update({ _id : name }, { _id : name }, true )
else
mongo.getCollection( "config.testFinished" ).remove({ _id : name })
}
/**
* Checks whether a test or background op is finished
*/
var isFinished = function( mongo, name ){
return mongo.getCollection( "config.testFinished" ).findOne({ _id : name }) != null
}
/**
* Sets the result of a background op
*/
var setResult = function( mongo, name, result, err ){
mongo.getCollection( "config.testResult" ).update({ _id : name }, { _id : name, result : result, err : err }, true )
}
/**
* Gets the result for a background op
*/
var getResult = function( mongo, name ){
return mongo.getCollection( "config.testResult" ).findOne({ _id : name })
}
/**
* Overrides the parallel shell code in mongo
*/
function startParallelShell( jsCode, port ){
var x;
if ( port ) {
x = startMongoProgramNoConnect( "mongo" , "--port" , port , "--eval" , jsCode );
} else {
x = startMongoProgramNoConnect( "mongo" , "--eval" , jsCode , db ? db.getMongo().host : null );
}
return function(){
jsTestLog( "Waiting for shell " + x + "..." )
waitProgram( x );
jsTestLog( "Shell " + x + " finished." )
};
}
startParallelOps = function( mongo, proc, args, context ){
var procName = proc.name + "-" + new ObjectId()
var seed = new ObjectId( new ObjectId().valueOf().split("").reverse().join("") )
.getTimestamp().getTime()
// Make sure we aren't finished before we start
setFinished( mongo, procName, false )
setResult( mongo, procName, undefined, undefined )
// TODO: Make this a context of its own
var procContext = { procName : procName,
seed : seed,
waitForLock : waitForLock,
setFinished : setFinished,
isFinished : isFinished,
setResult : setResult,
setup : function( context, stored ){
waitForLock = function(){
return context.waitForLock( db.getMongo(), context.procName )
}
setFinished = function( finished ){
return context.setFinished( db.getMongo(), context.procName, finished )
}
isFinished = function(){
return context.isFinished( db.getMongo(), context.procName )
}
setResult = function( result, err ){
return context.setResult( db.getMongo(), context.procName, result, err )
}
}}
var bootstrapper = function( stored ){
var procContext = stored.procContext
procContext.setup( procContext, stored )
var contexts = stored.contexts
eval( "contexts = " + contexts )
for( var i = 0; i < contexts.length; i++ ){
if( typeof( contexts[i] ) != "undefined" ){
// Evaluate all contexts
contexts[i]( procContext )
}
}
var operation = stored.operation
eval( "operation = " + operation )
var args = stored.args
eval( "args = " + args )
result = undefined
err = undefined
try{
result = operation.apply( null, args )
}
catch( e ){
err = e
}
setResult( result, err )
}
var contexts = [ RandomFunctionContext, context ]
var testDataColl = mongo.getCollection( "config.parallelTest" )
testDataColl.insert({ _id : procName,
bootstrapper : tojson( bootstrapper ),
operation : tojson( proc ),
args : tojson( args ),
procContext : procContext,
contexts : tojson( contexts ) })
assert.eq( null, testDataColl.getDB().getLastError() )
var bootstrapStartup =
"{ var procName = '" + procName + "'; " +
"var stored = db.getMongo().getCollection( '" + testDataColl + "' )" +
".findOne({ _id : procName }); " +
"var bootstrapper = stored.bootstrapper; " +
"eval( 'bootstrapper = ' + bootstrapper ); " +
"bootstrapper( stored ); " +
"}"
var oldDB = db
db = mongo.getDB( "test" )
jsTest.log( "Starting " + proc.name + " operations..." )
var rawJoin = startParallelShell( bootstrapStartup )
db = oldDB
var join = function(){
setFinished( mongo, procName, true )
rawJoin();
result = getResult( mongo, procName )
assert.neq( result, null )
if( result.err ) throw "Error in parallel ops " + procName + " : "
+ tojson( result.err )
else return result.result
}
join.isFinished = function(){
return isFinished( mongo, procName )
}
join.setFinished = function( finished ){
return setFinished( mongo, procName, finished )
}
join.waitForLock = function( name ){
return waitForLock( mongo, name )
}
return join
}
var RandomFunctionContext = function( context ){
Random.srand( context.seed );
Random.randBool = function(){ return Random.rand() > 0.5 }
Random.randInt = function( min, max ){
if( max == undefined ){
max = min
min = 0
}
return min + Math.floor( Random.rand() * max )
}
Random.randShardKey = function(){
var numFields = 2 //Random.randInt(1, 3)
var key = {}
for( var i = 0; i < numFields; i++ ){
var field = String.fromCharCode( "a".charCodeAt() + i )
key[ field ] = 1
}
return key
}
Random.randShardKeyValue = function( shardKey ){
var keyValue = {}
for( field in shardKey ){
keyValue[ field ] = Random.randInt(1, 100)
}
return keyValue
}
Random.randCluster = function(){
var numShards = 2 //Random.randInt( 1, 10 )
var rs = false //Random.randBool()
var st = new ShardingTest({ shards : numShards,
mongos : 4,
other : { separateConfig : true, rs : rs } })
return st
}
}
//
// Some utility operations
//
function moveOps( collName, options ){
options = options || {}
var admin = db.getMongo().getDB( "admin" )
var config = db.getMongo().getDB( "config" )
var shards = config.shards.find().toArray()
var shardKey = config.collections.findOne({ _id : collName }).key
while( ! isFinished() ){
var findKey = Random.randShardKeyValue( shardKey )
var toShard = shards[ Random.randInt( shards.length ) ]._id
try {
printjson( admin.runCommand({ moveChunk : collName,
find : findKey,
to : toShard }) )
}
catch( e ){
printjson( e )
}
sleep( 1000 )
}
jsTest.log( "Stopping moveOps..." )
}
function splitOps( collName, options ){
options = options || {}
var admin = db.getMongo().getDB( "admin" )
var config = db.getMongo().getDB( "config" )
var shards = config.shards.find().toArray()
var shardKey = config.collections.findOne({ _id : collName }).key
while( ! isFinished() ){
var middleKey = Random.randShardKeyValue( shardKey )
try {
printjson( admin.runCommand({ split : collName,
middle : middleKey }) )
}
catch( e ){
printjson( e )
}
sleep( 1000 )
}
jsTest.log( "Stopping splitOps..." )
}