/* test durability, specifically last sequence number function
   runs mongod, kill -9's, recovers
   then writes more data and verifies with DurParanoid that it matches
*/

var debugging = false;
var testname = "lsn";
var step = 1;
var conn = null;

var start = new Date();
function howLongSecs() {
    return (new Date() - start) / 1000;
}

function log(str) {
    if (str)
        print("\n" + testname + " step " + step++ + " " + str);
    else
        print(testname + " step " + step++);
}

function verify() {
    log("verify");
    var d = conn.getDB("test");
    var mycount = d.foo.count();
    print("count:" + mycount);
    assert(mycount > 2, "count wrong");
}

// if you do inserts here, you will want to set _id.  otherwise they won't match on different
// runs so we can't do a binary diff of the resulting files to check they are consistent.
function work() {
    log("work");
    x = 'x';
    while (x.length < 1024)
        x += x;
    var d = conn.getDB("test");
    d.foo.drop();
    d.foo.insert({});

    // go long enough we will have time to kill it later during recovery
    var j = 2;
    var MaxTime = 15;
    if (Math.random() < 0.05) {
        print("doing a longer pass");
        MaxTime = 90;
    }
    while (1) {
        d.foo.insert({_id: j, z: x});
        d.foo.update({_id: j}, {$inc: {a: 1}});
        if (j % 25 == 0)
            d.foo.remove({_id: j});
        j++;
        if (j % 3 == 0)
            d.foo.update({_id: j}, {$inc: {a: 1}}, true);
        if (j % 10000 == 0)
            print(j);
        if (howLongSecs() > MaxTime)
            break;
    }

    verify();
    d.runCommand({getLastError: 1, fsync: 1});
}

if (debugging) {
    // mongod already running in debugger
    print(
        "DOING DEBUG MODE BEHAVIOR AS 'db' IS DEFINED -- RUN mongo --nodb FOR REGULAR TEST BEHAVIOR");
    conn = db.getMongo();
    work();
    sleep(30000);
    quit();
}

// directories
var path2 = MongoRunner.dataPath + testname + "dur";

// run mongod with a short --syncdelay to make LSN writing sooner
log("run mongod --journal and a short --syncdelay");
conn = MongoRunner.runMongod({
    dbpath: path2,
    syncdelay: 2,
    journal: "",
    smallfiles: "",
    journalOptions: 8 /*DurParanoid*/,
    master: "",
    oplogSize: 64
});
work();

log("wait a while for a sync and an lsn write");
sleep(14);  // wait for lsn write

log("kill mongod -9");
MongoRunner.stopMongod(conn, /*signal*/ 9);

// journal file should be present, and non-empty as we killed hard

// check that there is an lsn file
{
    var files = listFiles(path2 + "/journal/");
    assert(files.some(function(f) {
        return f.name.indexOf("lsn") >= 0;
    }),
           "lsn.js FAIL no lsn file found after kill, yet one is expected");
}
/*assert.soon(
    function () {
        var files = listFiles(path2 + "/journal/");
        return files.some(function (f) { return f.name.indexOf("lsn") >= 0; });
    },
    "lsn.js FAIL no lsn file found after kill, yet one is expected"
);*/

// restart and recover
log("restart mongod, recover, verify");
conn = MongoRunner.runMongod({
    restart: true,
    cleanData: false,
    dbpath: path2,
    journal: "",
    smallfiles: "",
    journalOptions: 24,
    master: "",
    oplogSize: 64
});
verify();

// idea here is to verify (in a simplistic way) that we are in a good state to do further ops after
// recovery
log("add data after recovery");
{
    var d = conn.getDB("test");
    d.xyz.insert({x: 1});
    d.xyz.insert({x: 1});
    d.xyz.insert({x: 1});
    d.xyz.update({}, {$set: {x: "aaaaaaaaaaaa"}});
    d.xyz.reIndex();
    d.xyz.drop();
    sleep(1);
    d.xyz.insert({x: 1});
}

log("stop mongod " + conn.port);
MongoRunner.stopMongod(conn);

print(testname + " SUCCESS");
