Files
mongo/jstests/sharding/retryable_write_error_labels.js

156 lines
5.5 KiB
JavaScript

/**
* Test RetryableWriteError label in retryable writes and in transactions.
*
* The "requires_find_command" tag excludes this test from the op_query suites, which are
* incompatible with implicit sessions.
*
* @tags: [
* requires_find_command,
* uses_transactions,
* ]
*/
(function() {
"use strict";
load("jstests/libs/fail_point_util.js");
const dbName = "test";
const collName = "retryable_write_error_labels";
// Use ShardingTest because we need to test both mongod and mongos behaviors
let overrideMaxAwaitTimeMS = {'mode': 'alwaysOn', 'data': {maxAwaitTimeMS: 1000}};
const st = new ShardingTest({
config: 1,
mongos:
{s0: {setParameter: "failpoint.overrideMaxAwaitTimeMS=" + tojson(overrideMaxAwaitTimeMS)}},
shards: 1
});
function checkErrorCode(res, errorCode, isWCError) {
if (isWCError) {
assert.neq(null, res.writeConcernError, res);
assert.eq(res.writeConcernError.code, errorCode, res);
} else {
assert.commandFailedWithCode(res, errorCode);
assert.eq(null, res.writeConcernError, res);
}
}
function checkErrorLabels(res) {
assert(!res.hasOwnProperty("errorLabels"), res);
}
function enableFailCommand(isWCError, errorCode, commands) {
jsTestLog("Enabling failCommand fail point for " + commands + " with writeConcern error " +
isWCError);
// Sharding tests require {failInternalCommands: true},
// s appears to mongod to be an internal client.
let failCommandData = {failInternalCommands: true, failCommands: commands};
if (isWCError) {
failCommandData['writeConcernError'] = {code: NumberInt(errorCode), errmsg: "dummy"};
} else {
failCommandData['errorCode'] = NumberInt(errorCode);
}
return configureFailPoint(
st.rs0.getPrimary(), "failCommand", failCommandData, "alwaysOn" /*failPointMode*/);
}
function runTest(errorCode, isWCError) {
const testDB = st.getDB(dbName);
const session = st.s.startSession();
const sessionDb = session.getDatabase(dbName);
const sessionColl = sessionDb.getCollection(collName);
let insertFailPoint = enableFailCommand(isWCError, errorCode, ["insert"]);
jsTestLog(`Testing with errorCode: ${errorCode}, isWCError: ${isWCError}`);
// Test retryable writes.
jsTestLog("Retryable write should return error " + errorCode +
" without RetryableWriteError label");
let res = testDB.runCommand(
{insert: collName, documents: [{a: errorCode, b: "retryable"}], txnNumber: NumberLong(0)});
checkErrorCode(res, errorCode, isWCError);
checkErrorLabels(res);
// Test non-retryable writes.
jsTestLog("Non-retryable write should return error " + errorCode +
" without RetryableWriteError label");
res = testDB.runCommand({insert: collName, documents: [{a: errorCode, b: "non-retryable"}]});
checkErrorCode(res, errorCode, isWCError);
checkErrorLabels(res);
insertFailPoint.off();
let commitTxnFailPoint = enableFailCommand(isWCError, errorCode, ["commitTransaction"]);
// Test commitTransaction command in a transaction.
jsTestLog("commitTransaction should return error " + errorCode +
" without RetryableWriteError label");
session.startTransaction();
assert.commandWorked(sessionColl.update({}, {$inc: {x: 1}}));
res = sessionDb.adminCommand({
commitTransaction: 1,
txnNumber: NumberLong(session.getTxnNumber_forTesting()),
autocommit: false
});
checkErrorCode(res, errorCode, isWCError);
checkErrorLabels(res);
assert.commandWorkedOrFailedWithCode(
session.abortTransaction_forTesting(),
[ErrorCodes.TransactionCommitted, ErrorCodes.NoSuchTransaction]);
commitTxnFailPoint.off();
// Test abortTransaction command in a transaction.
let abortTransactionFailPoint = enableFailCommand(isWCError, errorCode, ["abortTransaction"]);
jsTestLog("abortTransaction should return error " + errorCode +
" without RetryableWriteError label");
session.startTransaction();
assert.commandWorked(sessionColl.update({}, {$inc: {x: 1}}));
res = sessionDb.adminCommand({
abortTransaction: 1,
txnNumber: NumberLong(session.getTxnNumber_forTesting()),
autocommit: false
});
checkErrorCode(res, errorCode, isWCError);
checkErrorLabels(res);
abortTransactionFailPoint.off();
assert.commandWorkedOrFailedWithCode(session.abortTransaction_forTesting(),
ErrorCodes.NoSuchTransaction);
session.endSession();
}
const retryableCodes = [
ErrorCodes.InterruptedAtShutdown,
ErrorCodes.InterruptedDueToReplStateChange,
ErrorCodes.NotWritablePrimary,
ErrorCodes.NotPrimaryNoSecondaryOk,
ErrorCodes.NotPrimaryOrSecondary,
ErrorCodes.PrimarySteppedDown,
ErrorCodes.ShutdownInProgress,
ErrorCodes.HostNotFound,
ErrorCodes.HostUnreachable,
ErrorCodes.NetworkTimeout,
ErrorCodes.SocketException,
ErrorCodes.ExceededTimeLimit,
ErrorCodes.WriteConcernFailed
];
// Test retryable error codes.
retryableCodes.forEach(function(code) {
// Mongos should never return RetryableWriteError labels.
runTest(code, false /* isWCError */);
});
// Test retryable error codes in writeConcern error.
retryableCodes.forEach(function(code) {
// Mongos should never return RetryableWriteError labels.
runTest(code, true /* isWCError */);
});
st.s.adminCommand({"configureFailPoint": "overrideMaxAwaitTimeMS", "mode": "off"});
st.stop();
}());