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
|
'use strict';
/**
* Test a snapshot read spanning a find and getmore that runs concurrently with killSessions,
* killOp, killCursors, and txnNumber change.
*
* @tags: [uses_transactions]
*/
load('jstests/concurrency/fsm_workload_helpers/snapshot_read_utils.js');
var $config = (function() {
const data = {numIds: 100, batchSize: 50};
const states = {
init: function init(db, collName) {
let session = db.getMongo().startSession({causalConsistency: false});
// Store the session ID in the database so any unterminated transactions can be aborted
// at teardown.
insertSessionDoc(db, collName, this.tid, session.getSessionId().id);
this.sessionDb = session.getDatabase(db.getName());
this.txnNumber = 0;
this.stmtId = 0;
this.iteration = 1;
},
snapshotFind: function snapshotFind(db, collName) {
const sortByAscending = false;
doSnapshotFind(sortByAscending,
collName,
this,
[ErrorCodes.NoSuchTransaction, ErrorCodes.LockTimeout]);
},
snapshotGetMore: function snapshotGetMore(db, collName) {
doSnapshotGetMore(collName,
this,
[
ErrorCodes.NoSuchTransaction,
ErrorCodes.CursorNotFound,
ErrorCodes.Interrupted,
ErrorCodes.LockTimeout
],
[ErrorCodes.NoSuchTransaction]);
},
incrementTxnNumber: function incrementTxnNumber(db, collName) {
this.txnNumber++;
},
killSessions: function killSessions(db, collName) {
// Kill a random active session.
const idToKill = "sessionDoc" + Math.floor(Math.random() * this.threadCount);
const sessionDocToKill = db[collName].find({"_id": idToKill});
assert.commandWorked(
this.sessionDb.runCommand({killSessions: [{id: sessionDocToKill.id}]}));
},
killOp: function killOp(db, collName) {
// Find the object ID of the getMore in the snapshot read, if it is running, and attempt
// to kill the operation.
const res = assert.commandWorked(this.sessionDb.adminCommand(
{currentOp: 1, ns: {$regex: db.getName() + "\." + collName}, op: "getmore"}));
if (res.inprog.length) {
const killOpCmd = {killOp: 1, op: res.inprog[0].opid};
const killRes = this.sessionDb.adminCommand(killOpCmd);
assert.commandWorked(killRes);
}
},
killCursors: function killCursors(db, collName) {
const killCursorCmd = {killCursors: collName, cursors: [this.cursorId]};
const res = this.sessionDb.runCommand(killCursorCmd);
assertWorkedOrFailed(killCursorCmd, res, [ErrorCodes.CursorNotFound]);
},
};
// Wrap each state in a cleanupOnLastIteration() invocation.
for (let stateName of Object.keys(states)) {
const stateFn = states[stateName];
states[stateName] = function(db, collName) {
cleanupOnLastIteration(this, () => stateFn.apply(this, arguments));
};
}
const transitions = {
init: {snapshotFind: 1.0},
snapshotFind: {
incrementTxnNumber: 0.20,
killSessions: 0.20,
killOp: 0.20,
killCursors: 0.20,
snapshotGetMore: 0.20
},
incrementTxnNumber: {snapshotGetMore: 1.0},
killSessions: {snapshotGetMore: 1.0},
killOp: {snapshotGetMore: 1.0},
killCursors: {snapshotGetMore: 1.0},
snapshotGetMore: {snapshotFind: 1.0}
};
function setup(db, collName, cluster) {
assertWhenOwnColl.commandWorked(db.runCommand({create: collName}));
for (let i = 0; i < this.numIds; ++i) {
const res = db[collName].insert({_id: i, value: i});
assert.writeOK(res);
assert.eq(1, res.nInserted);
}
}
function teardown(db, collName, cluster) {
// Make sure any currently running transactions are aborted.
killSessionsFromDocs(db, collName);
}
return {
threadCount: 5,
iterations: 10,
startState: 'init',
states: states,
transitions: transitions,
setup: setup,
teardown: teardown,
data: data,
};
})();
|