summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/fail_point/fail_point.js143
-rw-r--r--src/mongo/util/fail_point.cpp37
-rw-r--r--src/mongo/util/fail_point.h11
3 files changed, 114 insertions, 77 deletions
diff --git a/jstests/fail_point/fail_point.js b/jstests/fail_point/fail_point.js
index b8c877cb9b2..a16971857b6 100644
--- a/jstests/fail_point/fail_point.js
+++ b/jstests/fail_point/fail_point.js
@@ -1,78 +1,91 @@
-/**
- * Performs basic checks on the configureFailPoint command. Also check
- * mongo/util/fail_point_test.cpp for unit tests.
- *
- * @param adminDB {DB} the admin database database object
- */
-var runTest = function(adminDB) {
- var expectFailPointState = function(fpState, expectedMode, expectedData) {
- assert.eq(expectedMode, fpState.mode);
+(function() {
+ 'use strict';
- // Check that all expected data is present.
- for (var field in expectedData) { // Valid only for 1 level field checks
- assert.eq(expectedData[field], fpState.data[field]);
- }
+ /**
+ * Performs basic checks on the configureFailPoint command. Also check
+ * mongo/util/fail_point_test.cpp for unit tests.
+ *
+ * @param adminDB {DB} the admin database database object
+ */
+ function runTest(adminDB) {
+ function expectFailPointState(fpState, expectedMode, expectedData) {
+ assert.eq(expectedMode, fpState.mode);
+
+ // Check that all expected data is present.
+ for (var field in expectedData) { // Valid only for 1 level field checks
+ assert.eq(expectedData[field], fpState.data[field]);
+ }
- // Check that all present data is expected.
- for (field in fpState.data) {
- assert.eq(expectedData[field], fpState.data[field]);
+ // Check that all present data is expected.
+ for (field in fpState.data) {
+ assert.eq(expectedData[field], fpState.data[field]);
+ }
}
- };
- // A failpoint's state can be read through getParameter by prefixing its name with "failpoint."
+ var res;
+
+ // A failpoint's state can be read through getParameter by prefixing its name with
+ // "failpoint"
+
+ // Test non-existing fail point
+ assert.commandFailed(
+ adminDB.runCommand({configureFailPoint: 'fpNotExist', mode: 'alwaysOn', data: {x: 1}}));
- // Test non-existing fail point
- assert.commandFailed(
- adminDB.runCommand({configureFailPoint: 'fpNotExist', mode: 'alwaysOn', data: {x: 1}}));
+ // Test bad mode string
+ assert.commandFailed(
+ adminDB.runCommand({configureFailPoint: 'dummy', mode: 'badMode', data: {x: 1}}));
+ res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
+ assert.commandWorked(res);
+ expectFailPointState(res["failpoint.dummy"], 0, {});
- // Test bad mode string
- assert.commandFailed(
- adminDB.runCommand({configureFailPoint: 'dummy', mode: 'badMode', data: {x: 1}}));
- var res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
- assert.commandWorked(res);
- expectFailPointState(res["failpoint.dummy"], 0, {});
+ // Test bad mode obj
+ assert.commandFailed(
+ adminDB.runCommand({configureFailPoint: 'dummy', mode: {foo: 3}, data: {x: 1}}));
+ res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
+ assert.commandWorked(res);
+ expectFailPointState(res["failpoint.dummy"], 0, {});
- // Test bad mode obj
- assert.commandFailed(
- adminDB.runCommand({configureFailPoint: 'dummy', mode: {foo: 3}, data: {x: 1}}));
- var res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
- assert.commandWorked(res);
- expectFailPointState(res["failpoint.dummy"], 0, {});
+ // Test bad mode type
+ assert.commandFailed(
+ adminDB.runCommand({configureFailPoint: 'dummy', mode: true, data: {x: 1}}));
+ res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
+ assert.commandWorked(res);
+ expectFailPointState(res["failpoint.dummy"], 0, {});
- // Test bad mode type
- assert.commandFailed(
- adminDB.runCommand({configureFailPoint: 'dummy', mode: true, data: {x: 1}}));
- var res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
- assert.commandWorked(res);
- expectFailPointState(res["failpoint.dummy"], 0, {});
+ // Test bad data type
+ assert.commandFailed(
+ adminDB.runCommand({configureFailPoint: 'dummy', mode: 'alwaysOn', data: 'data'}));
+ res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
+ assert.commandWorked(res);
+ expectFailPointState(res["failpoint.dummy"], 0, {});
- // Test bad data type
- assert.commandFailed(
- adminDB.runCommand({configureFailPoint: 'dummy', mode: 'alwaysOn', data: 'data'}));
- var res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
- assert.commandWorked(res);
- expectFailPointState(res["failpoint.dummy"], 0, {});
+ // Test setting mode to off.
+ assert.commandWorked(adminDB.runCommand({configureFailPoint: 'dummy', mode: 'off'}));
+ res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
+ assert.commandWorked(res);
+ expectFailPointState(res["failpoint.dummy"], 0, {});
- // Test setting mode to off.
- assert.commandWorked(adminDB.runCommand({configureFailPoint: 'dummy', mode: 'off'}));
- var res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
- assert.commandWorked(res);
- expectFailPointState(res["failpoint.dummy"], 0, {});
+ // Test setting mode to skip.
+ assert.commandWorked(adminDB.runCommand({configureFailPoint: 'dummy', mode: {skip: 2}}));
+ res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
+ assert.commandWorked(res);
+ expectFailPointState(res["failpoint.dummy"], 4, {});
- // Test good command w/ data
- assert.commandWorked(
- adminDB.runCommand({configureFailPoint: 'dummy', mode: 'alwaysOn', data: {x: 1}}));
- var res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
- assert.commandWorked(res);
- expectFailPointState(res["failpoint.dummy"], 1, {x: 1});
-};
+ // Test good command w/ data
+ assert.commandWorked(
+ adminDB.runCommand({configureFailPoint: 'dummy', mode: 'alwaysOn', data: {x: 1}}));
+ res = adminDB.runCommand({getParameter: 1, "failpoint.dummy": 1});
+ assert.commandWorked(res);
+ expectFailPointState(res["failpoint.dummy"], 1, {x: 1});
+ }
-var conn = MongoRunner.runMongod();
-runTest(conn.getDB('admin'));
-MongoRunner.stopMongod(conn);
+ var conn = MongoRunner.runMongod();
+ runTest(conn.getDB('admin'));
+ MongoRunner.stopMongod(conn);
-///////////////////////////////////////////////////////////
-// Test mongos
-var st = new ShardingTest({shards: 1});
-runTest(st.s.getDB('admin'));
-st.stop();
+ ///////////////////////////////////////////////////////////
+ // Test mongos
+ var st = new ShardingTest({shards: 1});
+ runTest(st.s.getDB('admin'));
+ st.stop();
+})();
diff --git a/src/mongo/util/fail_point.cpp b/src/mongo/util/fail_point.cpp
index 8f85ba18f17..75be5f48231 100644
--- a/src/mongo/util/fail_point.cpp
+++ b/src/mongo/util/fail_point.cpp
@@ -76,7 +76,7 @@ void FailPoint::setThreadPRNGSeed(int32_t seed) {
FailPointPRNG::current()->resetSeed(seed);
}
-FailPoint::FailPoint() : _fpInfo(0), _mode(off), _timesOrPeriod(0) {}
+FailPoint::FailPoint() = default;
void FailPoint::shouldFailCloseBlock() {
_fpInfo.subtractAndFetch(1);
@@ -151,24 +151,28 @@ FailPoint::RetCode FailPoint::slowShouldFailOpenBlock() {
switch (_mode) {
case alwaysOn:
return slowOn;
-
case random: {
const AtomicInt32::WordType maxActivationValue = _timesOrPeriod.load();
- if (FailPointPRNG::current()->nextPositiveInt32() < maxActivationValue) {
+ if (FailPointPRNG::current()->nextPositiveInt32() < maxActivationValue)
return slowOn;
- }
+
return slowOff;
}
case nTimes: {
- AtomicInt32::WordType newVal = _timesOrPeriod.subtractAndFetch(1);
-
- if (newVal <= 0) {
+ if (_timesOrPeriod.subtractAndFetch(1) <= 0)
disableFailPoint();
- }
return slowOn;
}
+ case skip: {
+ // Ensure that once the skip counter reaches within some delta from 0 we don't continue
+ // decrementing it unboundedly because at some point it will roll over and become
+ // positive again
+ if (_timesOrPeriod.load() <= 0 || _timesOrPeriod.subtractAndFetch(1) < 0)
+ return slowOn;
+ return slowOff;
+ }
default:
error() << "FailPoint Mode not supported: " << static_cast<int>(_mode);
fassertFailed(16444);
@@ -212,6 +216,23 @@ StatusWith<std::tuple<FailPoint::Mode, FailPoint::ValType, BSONObj>> FailPoint::
return {ErrorCodes::BadValue, "'times' option to 'mode' is too large"};
}
val = static_cast<int>(longVal);
+ } else if (modeObj.hasField("skip")) {
+ mode = FailPoint::skip;
+
+ long long longVal;
+ auto status = bsonExtractIntegerField(modeObj, "skip", &longVal);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (longVal < 0) {
+ return {ErrorCodes::BadValue, "'skip' option to 'mode' must be positive"};
+ }
+
+ if (longVal > std::numeric_limits<int>::max()) {
+ return {ErrorCodes::BadValue, "'skip' option to 'mode' is too large"};
+ }
+ val = static_cast<int>(longVal);
} else if (modeObj.hasField("activationProbability")) {
mode = FailPoint::random;
diff --git a/src/mongo/util/fail_point.h b/src/mongo/util/fail_point.h
index 77bb9ab882a..a5fc58d21d5 100644
--- a/src/mongo/util/fail_point.h
+++ b/src/mongo/util/fail_point.h
@@ -69,7 +69,7 @@ class FailPoint {
public:
typedef AtomicUInt32::WordType ValType;
- enum Mode { off, alwaysOn, random, nTimes };
+ enum Mode { off, alwaysOn, random, nTimes, skip };
enum RetCode { fastOff = 0, slowOff, slowOn };
/**
@@ -136,6 +136,9 @@ public:
* activate.
* - nTimes: the number of times this fail point will be active when
* #shouldFail or #shouldFailOpenBlock is called.
+ * - skip: the number of times this failpoint will be inactive when
+ * #shouldFail or #shouldFailOpenBlock is called. After this number is reached, the
+ * failpoint will always be active.
*
* @param extra arbitrary BSON object that can be stored to this fail point
* that can be referenced afterwards with #getData. Defaults to an empty
@@ -155,11 +158,11 @@ private:
// Bit layout:
// 31: tells whether this fail point is active.
// 0~30: unsigned ref counter for active dynamic instances.
- AtomicUInt32 _fpInfo;
+ AtomicUInt32 _fpInfo{0};
// Invariant: These should be read only if ACTIVE_BIT of _fpInfo is set.
- Mode _mode;
- AtomicInt32 _timesOrPeriod;
+ Mode _mode{off};
+ AtomicInt32 _timesOrPeriod{0};
BSONObj _data;
// protects _mode, _timesOrPeriod, _data