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
|
/**
* Validate that under significant WiredTiger cache pressure an operation can fail
* with TemporarilyUnavailable error.
*
* @tags: [
* # Exclude in-memory engine, rollbacks due to pinned cache content rely on eviction.
* requires_persistence,
* requires_replication,
* requires_wiredtiger,
* ]
*/
(function() {
"use strict";
const replSet = new ReplSetTest({
nodes: 1,
nodeOptions: {
wiredTigerCacheSizeGB: 0.256,
setParameter: {
logComponentVerbosity: tojson({control: 1, write: 1}),
enableTemporarilyUnavailableExceptions: true,
// Lower these values from the defaults to speed up the test.
temporarilyUnavailableMaxRetries: 3,
temporarilyUnavailableBackoffBaseMs: 10,
}
},
});
replSet.startSet();
replSet.initiate();
const db = replSet.getPrimary().getDB("test");
// Generate a workload pinning enough dirty data in cache that causes WiredTiger
// to roll back transactions. This workload is adapted from the reproducer in the
// SERVER-61909 ticket description.
assert.commandWorked(db.c.createIndex({x: "text"}));
let doc = {x: []};
for (let j = 0; j < 50000; j++)
doc.x.push("" + Math.random() + Math.random());
(function temporarilyUnavailableNonTransaction() {
const serverStatusBefore = db.serverStatus();
let caughtTUerror = false;
let attempts;
for (attempts = 1; attempts <= 20; attempts++) {
print("temporarilyUnavailableNonTransaction attempt " + attempts);
const ret = db.c.insert(doc);
if (ret["nInserted"] === 1) {
// The write succeeded.
continue;
}
assert.eq(0, ret["nInserted"]);
assert.commandFailedWithCode(ret, ErrorCodes.TemporarilyUnavailable);
caughtTUerror = true;
jsTestLog("returned the expected TemporarilyUnavailable code at attempt " + attempts);
break;
}
assert(caughtTUerror,
"did not return the expected TemporarilyUnavailable error after " + (attempts - 1) +
" attempts");
const serverStatusAfter = db.serverStatus();
assert.gt(serverStatusAfter.metrics.operation.temporarilyUnavailableErrors,
serverStatusBefore.metrics.operation.temporarilyUnavailableErrors);
assert.gt(serverStatusAfter.metrics.operation.temporarilyUnavailableErrorsEscaped,
serverStatusBefore.metrics.operation.temporarilyUnavailableErrorsEscaped);
})();
(function temporarilyUnavailableInTransactionIsConvertedToWriteConflict() {
// Inside a transaction, TemporarilyUnavailable errors should be converted to
// WriteConflicts and tagged as TransientTransactionErrors.
const serverStatusBefore = db.serverStatus();
let caughtWriteConflict = false;
let attempts;
let ret;
for (attempts = 1; attempts <= 20; attempts++) {
print("temporarilyUnavailableInTransactionIsConvertedToWriteConflict attempt " + attempts);
const session = db.getMongo().startSession();
session.startTransaction();
const sessionDB = session.getDatabase("test");
ret = sessionDB.c.insert(doc);
if (ret["nInserted"] === 1) {
// The write succeeded.
session.commitTransaction();
continue;
}
assert.commandFailedWithCode(ret, ErrorCodes.WriteConflict, ret);
assert(ret.hasOwnProperty("errorLabels"), ret);
assert.contains("TransientTransactionError", ret.errorLabels, ret);
caughtWriteConflict = true;
jsTestLog("returned the expected WriteConflict code at attempt " + attempts);
session.abortTransaction();
break;
}
assert(caughtWriteConflict,
"did not return the expected WriteConflict error after " + (attempts - 1) +
" attempts. Result: " + tojson(ret));
const serverStatusAfter = db.serverStatus();
assert.gt(
serverStatusAfter.metrics.operation.temporarilyUnavailableErrorsConvertedToWriteConflict,
serverStatusBefore.metrics.operation.temporarilyUnavailableErrorsConvertedToWriteConflict);
assert.eq(serverStatusAfter.metrics.operation.temporarilyUnavailableErrors,
serverStatusBefore.metrics.operation.temporarilyUnavailableErrors);
assert.eq(serverStatusAfter.metrics.operation.temporarilyUnavailableErrorsEscaped,
serverStatusBefore.metrics.operation.temporarilyUnavailableErrorsEscaped);
})();
replSet.stopSet();
})();
|