summaryrefslogtreecommitdiff
path: root/jstests/core/fsync.js
blob: ee66fcb313f43f413aa28f1bd7f8039ead93a5ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
 * Test fsyncLock functionality
 * - Skip for all storage engines which don't support fsync
 * - Run the fsyncLock command, confirm we lock correctly with currentOp
 * - Confirm that we cannot insert during fsyncLock
 * - Confirm that writes can progress after fsyncUnlock
 * - Confirm that the command can be run repeatedly without breaking things
 *
 * @tags: [
 *   requires_fastcount,
 *   requires_fsync,
 *   uses_parallel_shell,
 * ]
 */
(function() {
"use strict";

function waitUntilOpCountIs(opFilter, num) {
    assert.soon(() => {
        let ops = db.getSiblingDB('admin')
                      .aggregate([
                          {$currentOp: {}},
                          {$match: opFilter},
                      ])
                      .toArray();
        if (ops.length != num) {
            jsTest.log("Num opeartions: " + ops.length + ", expected: " + num);
            jsTest.log(ops);
            return false;
        }
        return true;
    });
}

// Start with a clean DB.
var fsyncLockDB = db.getSiblingDB('fsyncLockTestDB');
fsyncLockDB.dropDatabase();

// Tests the db.fsyncLock/fsyncUnlock features.
var storageEngine = db.serverStatus().storageEngine.name;

// As of SERVER-18899 fsyncLock/fsyncUnlock will error when called on a storage engine
// that does not support the begin/end backup commands.
var supportsFsync = db.fsyncLock();

if (!supportsFsync.ok) {
    assert.commandFailedWithCode(supportsFsync, ErrorCodes.CommandNotSupported);
    jsTestLog("Skipping test for " + storageEngine + " as it does not support fsync");
    return;
}
db.fsyncUnlock();

var resFail = fsyncLockDB.runCommand({fsync: 1, lock: 1});

// Start with a clean DB
var fsyncLockDB = db.getSiblingDB('fsyncLockTestDB');
fsyncLockDB.dropDatabase();

// Test that a single, regular write works as expected.
assert.commandWorked(fsyncLockDB.coll.insert({x: 1}));

// Test that fsyncLock doesn't work unless invoked against the admin DB.
var resFail = fsyncLockDB.runCommand({fsync: 1, lock: 1});
assert(!resFail.ok, "fsyncLock command succeeded against DB other than admin.");

// Uses admin automatically and locks the server for writes.
var fsyncLockRes = db.fsyncLock();
assert(fsyncLockRes.ok, "fsyncLock command failed against admin DB");
assert(db.getSiblingDB('admin').runCommand({currentOp: 1}).fsyncLock,
       "Value in currentOp result incorrect for fsyncLocked server");

// Make sure writes are blocked. Spawn a write operation in a separate shell and make sure it
// is blocked. There is really no way to do that currently, so just check that the write didn't
// go through.
var writeOpHandle = startParallelShell("db.getSiblingDB('fsyncLockTestDB').coll.insert({x:1});");
waitUntilOpCountIs({op: 'insert', ns: 'fsyncLockTestDB.coll', waitingForLock: true}, 1);

// Make sure reads can still run even though there is a pending write and also that the write
// didn't get through.
assert.eq(1, fsyncLockDB.coll.find({}).itcount());

// Unlock and make sure the insert succeeded.
var fsyncUnlockRes = db.fsyncUnlock();
assert(fsyncUnlockRes.ok, "fsyncUnlock command failed");
assert(db.getSiblingDB('admin').runCommand({currentOp: 1}).fsyncLock == null,
       "fsyncUnlock is not null in currentOp result");

// Make sure the db is unlocked and the initial write made it through.
writeOpHandle();
assert.commandWorked(fsyncLockDB.coll.insert({x: 2}));

assert.eq(3, fsyncLockDB.coll.count({}));

// Issue the fsyncLock and fsyncUnlock a second time, to ensure that we can
// run this command repeatedly with no problems.
var fsyncLockRes = db.fsyncLock();
assert(fsyncLockRes.ok, "Second execution of fsyncLock command failed");

var fsyncUnlockRes = db.fsyncUnlock();
assert(fsyncUnlockRes.ok, "Second execution of fsyncUnlock command failed");

// Make sure that insert attempts made during multiple fsyncLock requests will not execute until
// all locks have been released.
fsyncLockRes = db.fsyncLock();
assert.commandWorked(fsyncLockRes);
assert(fsyncLockRes.lockCount == 1, tojson(fsyncLockRes));
let currentOp = db.getSiblingDB('admin').runCommand({currentOp: 1});
assert.commandWorked(currentOp);
assert(currentOp.fsyncLock, "Value in currentOp result incorrect for fsyncLocked server");

let shellHandle1 =
    startParallelShell("db.getSiblingDB('fsyncLockTestDB').multipleLock.insert({x:1});");

fsyncLockRes = db.fsyncLock();
assert.commandWorked(fsyncLockRes);
assert(fsyncLockRes.lockCount == 2, tojson(fsyncLockRes));
currentOp = db.getSiblingDB('admin').runCommand({currentOp: 1});
assert.commandWorked(currentOp);
assert(currentOp.fsyncLock, "Value in currentOp result incorrect for fsyncLocked server");

let shellHandle2 =
    startParallelShell("db.getSiblingDB('fsyncLockTestDB').multipleLock.insert({x:1});");
waitUntilOpCountIs({op: 'insert', ns: 'fsyncLockTestDB.multipleLock', waitingForLock: true}, 2);

assert.eq(0, fsyncLockDB.multipleLock.find({}).itcount());

fsyncUnlockRes = db.fsyncUnlock();
assert.commandWorked(fsyncUnlockRes);
assert(fsyncUnlockRes.lockCount == 1, tojson(fsyncLockRes));
sleep(1000);
assert.eq(0, fsyncLockDB.multipleLock.find({}).itcount());

fsyncUnlockRes = db.fsyncUnlock();
assert.commandWorked(fsyncUnlockRes);
assert(fsyncUnlockRes.lockCount == 0, tojson(fsyncLockRes));
shellHandle1();
shellHandle2();
assert.eq(2, fsyncLockDB.multipleLock.find({}).itcount());
}());