[SERVER-27642] Replace long-running string-ified code with function expression in JS tests Created: 11/Jan/17  Updated: 05/Apr/17  Resolved: 13/Jan/17

Status: Closed
Project: Core Server
Component/s: Testing Infrastructure
Affects Version/s: None
Fix Version/s: 3.5.2

Type: Bug Priority: Major - P3
Reporter: Eddie Louie Assignee: Eddie Louie
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
Related
Backwards Compatibility: Fully Compatible
Sprint: TIG 2017-01-02
Participants:
Linked BF Score: 0

 Description   

Certain JS tests contain string-ified code that when evaluated by assert*.automsg or startParallelShell in fuzzed tests could lead to long run times. This is especially true on variants like arm64. We should wrap these strings in function expressions to allow the fuzzer to process these statements and enforce overrides.



 Comments   
Comment by Githook User [ 13/Jan/17 ]

Author:

{u'username': u'elouie99', u'name': u'Eddie Louie', u'email': u'eddie.louie@mongodb.com'}

Message: SERVER-27642 Replace long-running string-ified code with function expression in JS tests
Branch: master
https://github.com/mongodb/mongo/commit/474e4f1e978a95d1f613273201d88780ab8a3b30

Comment by Max Hirschhorn [ 13/Jan/17 ]

It appears that the following JavaScript integration tests use while-loops and/or for-loops within strings evaluated via startParallelShell() or the assert.automsg() functions.

  • jstests/core/distinct3.js
  • jstests/core/evalc.js
  • jstests/core/evald.js
  • jstests/core/geo_update_btree.js
  • jstests/core/killop.js
  • jstests/core/mr_killop.js
  • jstests/core/queryoptimizer3.js
  • jstests/core/remove9.js
  • jstests/core/removeb.js
  • jstests/core/removec.js
  • jstests/core/updatef.js
  • jstests/disk/killall.js
  • jstests/disk/repair5.js
  • jstests/dur/closeall.js
  • jstests/noPassthrough/repair2.js
  • jstests/noPassthrough/update_server-5552.js
  • jstests/noPassthrough/write_local.js
  • jstests/noPassthroughWithMongod/explain1.js
  • jstests/noPassthroughWithMongod/explain2.js
  • jstests/noPassthroughWithMongod/explain3.js
  • jstests/replsets/replsets_killop.js

[js_test:distinct3] 2017-01-12T20:51:07.403-0500 2017-01-12T20:51:07.403-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: for( i = 0; i < 100; ++i ) {                                  var bulk = db.jstests_distinct3.initializeUnorderedBulkOp();    bulk.find( { a:49 } ).remove();                           for( j = 0; j < 20; ++j ) {                                   bulk.insert( { a:49, c:49, d:j } );                   }                                                         assert.writeOK(bulk.execute());                       }                                                          :
[js_test:evalc] 2017-01-12T20:51:07.626-0500 2017-01-12T20:51:07.625-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: print( 'starting forked:' + Date() ); for ( i=0; i<10*1000; i++ ){ db.currentOp(); } print( 'ending forked:' + Date() ); db.evalc_done.insert( { x : 1 } );  :
[js_test:evald] 2017-01-12T20:51:07.887-0500 2017-01-12T20:51:07.887-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: db.jstests_evald.count( { $where: function() { while(1) { sleep(1); } } } ) :
[js_test:geo_update_btree] 2017-01-12T20:51:09.168-0500 2017-01-12T20:51:09.168-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: Random.setRandomSeed();for ( var i = 0; i < 1000; i++ ) {    var doc = { loc: [ Random.rand() * 180, Random.rand() * 180 ], v: '' };    db.jstests_geo_update_btree.insert(doc);} :
[js_test:killop] 2017-01-12T20:51:09.943-0500 2017-01-12T20:51:09.942-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: db.jstests_killop.count({ $where: function() { while (1) { sleep(500); } } }); :
[js_test:mr_killop] 2017-01-12T20:51:11.341-0500 2017-01-12T20:51:11.341-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: assert.commandWorked( db.runCommand( { 	'mapreduce' : 'jstests_mr_killop', 	'out' : 'jstests_mr_killop_out', 	'map' : function () {     while (1) {         sleep(1000);     } }, 	'reduce' : function (k, v) {              return v[0];          } } ) ); :
[js_test:queryoptimizer3] 2017-01-12T20:51:13.053-0500 2017-01-12T20:51:13.053-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: for( i = 0; i < 400; ++i ) { sleep( 50 ); db.jstests_queryoptimizer3.drop(); } :
[js_test:remove9] 2017-01-12T20:51:13.753-0500 2017-01-12T20:51:13.753-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: t = db.jstests_remove9; Random.setRandomSeed(); for( j = 0; j < 5000; ++j ) { i = Random.randInt( 499 ) * 2; t.update( {i:i}, {$set:{i:2000}} ); t.remove( {i:2000} ); t.save( {i:i} ); } :
[js_test:removec] 2017-01-12T20:51:14.073-0500 2017-01-12T20:51:14.072-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: t = db.jstests_removec;Random.setRandomSeed();for( j = 0; j < 1000; ++j ) {    o = t.findOne( { a:Random.randInt( 1100 ) } );    t.remove( { _id:o._id } );    t.insert( o );} :
[js_test:updatef] 2017-01-12T20:51:15.205-0500 2017-01-12T20:51:15.205-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: for( i=0; i < 100; ++i ) { db.jstests_updatef.renameCollection( 'jstests_updatef_' ); db.jstests_updatef_.renameCollection( 'jstests_updatef' ); } :
[js_test:killall] 2017-01-12T20:51:17.117-0500 2017-01-12T20:51:17.117-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: db.jstests_disk_killall.count( { $where: function() { while( 1 ) { ; } } } ) :
[js_test:repair5] 2017-01-12T20:51:20.640-0500 2017-01-12T20:51:20.639-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: function killRepair() {
[js_test:closeall] 2017-01-12T20:51:22.964-0500 2017-01-12T20:51:22.964-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: var coll = db.getSiblingDB("closealltest").foo;                                        for( var i = 0; i < 1000; i++ ) {                                            var bulk = coll.initializeUnorderedBulkOp();                                            bulk.insert({ x: 1 });                                            if ( i % 7 == 0 )                                                bulk.insert({ x: 99, y: 2 });                                            if ( i % 49 == 0 )                                                bulk.find({ x: 99 }).update(                                                    { $set: { a: 1, b: 2, c: 3, d: 4 }});                                            if( i == 800 )                                                coll.ensureIndex({ x: 1 });                                            assert.writeOK(bulk.execute());                                        } :
[js_test:removeb] 2017-01-12T20:51:23.793-0500 2017-01-12T20:51:23.792-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: while( db.jstests_removeb.count() == 20000 );for( i = 20000; i < 40000; ++i ) {    db.jstests_removeb.insert( { a:i } );    if (i % 1000 == 0) {        print( i-20000 + " of 20000 documents inserted" );    }} :
[js_test:repair2] 2017-01-12T20:51:32.365-0500 2017-01-12T20:51:32.364-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: db = db.getSiblingDB( 'jstests_repair2');for( i = 0; i < 10; ++i ) { db.repairDatabase();sleep( 5000 ); } :
[js_test:update_server-5552] 2017-01-12T20:51:32.843-0500 2017-01-12T20:51:32.843-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: while( db.foo.findOne( { _id : 0 } ).x == 1 ); db.foo.ensureIndex( { x : 1 } ); :
[js_test:explain1] 2017-01-12T20:51:33.094-0500 2017-01-12T20:51:33.094-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: t = db.jstests_slowNightly_explain1; for( var i = 0; i < 80; ++i ) { t.drop(); t.ensureIndex({x:1}); for( var j = 0; j < 1000; ++j ) { t.save( {x:j,y:1} ) }; sleep( 100 ); } :
[js_test:explain3] 2017-01-12T20:51:33.370-0500 2017-01-12T20:51:33.370-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: t = db.jstests_slowNightly_explain1; for( var i = 0; i < 80; ++i ) { t.drop(); t.ensureIndex({x:1}); for( var j = 0; j < 1000; ++j ) { t.save( {x:j,y:1} ) }; sleep( 100 ); } :
[js_test:explain2] 2017-01-12T20:51:33.378-0500 2017-01-12T20:51:33.377-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: for( i = 0; i < 50000; ++i ) { db.jstests_slowNightly_explain2.insert( {x:i,y:1} ); } :
[js_test:write_local] 2017-01-12T20:51:34.118-0500 2017-01-12T20:51:34.118-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: var mydb=db.getSiblingDB("test"); (function() {     for(var i=0; i < 10*1000; i++) {         mydb.capped.insert({ x: i });     } })(); :
[js_test:connections_opened] 2017-01-12T20:51:34.549-0500 2017-01-12T20:51:34.549-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: var conn = null;assert.soon(function() {try { conn = new Mongo("hanamizu:20760"); return conn} catch (x) {return false;}}, "Timed out waiting for temporary connection to connect", 30000, 5000);assert.soon(function() {return conn.getDB('connectionsOpenedTest').getCollection('keepRunning').findOne().stop;}, "Parallel shell never told to terminate", 10 * 60000); :
[js_test:replsets_killop] 2017-01-12T20:52:03.456-0500 2017-01-12T20:52:03.455-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: var bulk = db.test.initializeUnorderedBulkOp();      for( i = 1; i < 100000; ++i ) {          bulk.insert({ a: i });       }      bulk.execute(); :
[js_test:index_multi] 2017-01-12T20:52:30.394-0500 2017-01-12T20:52:30.393-0500 E QUERY    [main] Error: Passed a stringified expression to evaluate: var conn = null;assert.soon(function() {  try {    conn = new Mongo("127.0.0.1:21750");    return conn;  } catch (x) {    return false;  }}, 'Timed out waiting for temporary connection to connect', 30000, 5000);var db = conn.getDB('test');printjson(db.index_multi.createIndex({ "field90" : 1, "field91" : 1, "field92" : 1 },{ background: true }));db.results.insert(Object.extend(db.runCommand({ getlasterror: 1 }), { "field90" : 1, "field91" : 1, "field92" : 1 }) ); :


The candidate files were chosen based on the output from the following command.

ag -l "(startParallelShell|assert\.(.*\.)?automsg)" jstests/

I then ran those tests under resmoke.py with the following patch applied to error if those functions received a string to evaluate. Note that --storageEngine=mmapv1 was used to satisfy the tests from the jstests/disk/ directory.

python buildscripts/resmoke.py --suite=test_server27642.yml --continueOnFailure --storageEngine=mmapv1

diff --git a/src/mongo/shell/assert.js b/src/mongo/shell/assert.js
index 2ce8e1e..c159a7e 100644
--- a/src/mongo/shell/assert.js
+++ b/src/mongo/shell/assert.js
@@ -36,6 +36,11 @@ assert = function(b, msg) {
 };
 
 assert.automsg = function(b) {
+    if (typeof b === "string") {
+        if (/\b(for|while)\b/.test(b)) {
+            throw new Error("Passed a stringified expression to evaluate: " + b);
+        }
+    }
     assert(eval(b), b);
 };
 
@@ -119,6 +124,16 @@ assert.docEq = function(a, b, msg) {
 };
 
 assert.eq.automsg = function(a, b) {
+    if (typeof a === "string") {
+        if (/\b(for|while)\b/.test(a)) {
+            throw new Error("Passed a stringified expression to evaluate: " + a);
+        }
+    }
+    if (typeof b === "string") {
+        if (/\b(for|while)\b/.test(b)) {
+            throw new Error("Passed a stringified expression to evaluate: " + b);
+        }
+    }
     assert.eq(eval(a), eval(b), "[" + a + "] != [" + b + "]");
 };
 
diff --git a/src/mongo/shell/servers_misc.js b/src/mongo/shell/servers_misc.js
index 223d7d2..846030e 100644
--- a/src/mongo/shell/servers_misc.js
+++ b/src/mongo/shell/servers_misc.js
@@ -229,6 +229,16 @@ function startParallelShell(jsCode, port, noConnect) {
     if (typeof(jsCode) == "function") {
         jsCode = "(" + jsCode.toString() + ")();";
     } else if (typeof(jsCode) == "string") {
+        if (/\b(for|while)\b/.test(jsCode)) {
+            const conn = port ? new Mongo("localhost:" + port) : db;
+            while (true) {
+                const res = conn.getDB("admin").runCommand({fsyncUnlock: 1});
+                if (res.ok || res.errmsg === "fsyncUnlock called when not locked") {
+                    break;
+                }
+            }
+            throw new Error("Passed a stringified expression to evaluate: " + jsCode);
+        }
     }
     // do nothing
     else {
diff --git a/test_server27642.yml b/test_server27642.yml
new file mode 100644
index 0000000..cf8b1d1
--- /dev/null
+++ b/test_server27642.yml
@@ -0,0 +1,109 @@
+selector:
+  js_test:
+    roots:
+    - jstests/auth/killop_own_ops.js
+    - jstests/core/compact_keeps_indexes.js
+    - jstests/core/count10.js
+    - jstests/core/count_plan_summary.js
+    - jstests/core/coveredIndex3.js
+    - jstests/core/currentop.js
+    - jstests/core/cursora.js
+    - jstests/core/distinct3.js
+    - jstests/core/evalc.js
+    - jstests/core/evald.js
+    - jstests/core/explain3.js
+    - jstests/core/find_and_modify_concurrent_update.js
+    - jstests/core/fsync.js
+    - jstests/core/geo_update_btree.js
+    - jstests/core/group6.js
+    - jstests/core/in6.js
+    - jstests/core/index1.js
+    - jstests/core/index_check6.js
+    - jstests/core/kill_cursors.js
+    - jstests/core/killop.js
+    - jstests/core/killop_drop_collection.js
+    - jstests/core/loadserverscripts.js
+    - jstests/core/max_time_ms.js
+    - jstests/core/mr_killop.js
+    - jstests/core/numberint.js
+    - jstests/core/numberlong.js
+    - jstests/core/or4.js
+    - jstests/core/or5.js
+    - jstests/core/or7.js
+    - jstests/core/or8.js
+    - jstests/core/or9.js
+    - jstests/core/ora.js
+    - jstests/core/orb.js
+    - jstests/core/queryoptimizer3.js
+    - jstests/core/remove9.js
+    - jstests/core/removeb.js
+    - jstests/core/removec.js
+    - jstests/core/shellstartparallel.js
+    - jstests/core/update_arraymatch6.js
+    - jstests/core/updatef.js
+    - jstests/disk/killall.js
+    - jstests/disk/repair.js
+    - jstests/disk/repair2.js
+    - jstests/disk/repair3.js
+    - jstests/disk/repair4.js
+    - jstests/disk/repair5.js
+    - jstests/dur/closeall.js
+    - jstests/libs/override_methods/set_majority_read_and_write_concerns.js
+    - jstests/libs/test_background_ops.js
+    - jstests/mmap_v1/capped7.js
+    - jstests/mmap_v1/indexh.js
+    - jstests/noPassthrough/currentop_query.js
+    - jstests/noPassthrough/index_killop.js
+    - jstests/noPassthrough/index_no_retry.js
+    - jstests/noPassthrough/index_retry.js
+    - jstests/noPassthrough/lock_stats.js
+    - jstests/noPassthrough/query_yield1.js
+    - jstests/noPassthrough/query_yield2.js
+    - jstests/noPassthrough/read_majority.js
+    - jstests/noPassthrough/repair2.js
+    - jstests/noPassthrough/update_server-5552.js
+    - jstests/noPassthrough/write_local.js
+    - jstests/noPassthrough/wt_nojournal_skip_recovery.js
+    - jstests/noPassthrough/wt_nojournal_toggle.js
+    - jstests/noPassthroughWithMongod/connections_opened.js
+    - jstests/noPassthroughWithMongod/dup_bgindex.js
+    - jstests/noPassthroughWithMongod/explain1.js
+    - jstests/noPassthroughWithMongod/explain2.js
+    - jstests/noPassthroughWithMongod/explain3.js
+    - jstests/noPassthroughWithMongod/indexbg_updates.js
+    - jstests/parallel/update_serializability1.js
+    - jstests/parallel/update_serializability2.js
+    - jstests/repl/repair.js
+    - jstests/repl/repl1.js
+    - jstests/repl/repl2.js
+    - jstests/replsets/clean_shutdown_oplog_state.js
+    - jstests/replsets/get_replication_info_helper.js
+    - jstests/replsets/initialsync_with_write_load.js
+    - jstests/replsets/linearizable_read_concern.js
+    - jstests/replsets/maintenance.js
+    - jstests/replsets/read_after_optime.js
+    - jstests/replsets/replsets_killop.js
+    - jstests/replsets/resync_with_write_load.js
+    - jstests/replsets/shutdown_primary.js
+    - jstests/replsets/stepdown3.js
+    - jstests/replsets/stepdown_kill_other_ops.js
+    - jstests/replsets/stepdown_killop.js
+    - jstests/replsets/stepdown_long_wait_time.js
+    - jstests/replsets/write_concern_after_stepdown.js
+    - jstests/replsets/write_concern_after_stepdown_and_stepup.js
+    - jstests/serial_run/index_multi.js
+    - jstests/sharding/delete_during_migrate.js
+    - jstests/sharding/features3.js
+    - jstests/sharding/sharding_migrate_cursor1.js
+    - jstests/slow1/conc_update.js
+
+executor:
+  js_test:
+    config:
+      shell_options:
+        readMode: commands
+    fixture:
+      class: MongoDFixture
+      mongod_options:
+        set_parameters:
+          enableTestCommands: 1

Generated at Thu Feb 08 04:15:45 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.