0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 01:21:03 +01:00

SERVER-40446 Add "NonResumableChangeStreamError" error label

Give the "NonRetryableChangeStreamError" error class a more
appropriate name and propagate it to an "errorLabel" in the command
response.
This commit is contained in:
Justin Seyster 2019-04-23 15:44:56 -04:00
parent 4b7ecadd7d
commit 0e9564888a
3 changed files with 50 additions and 14 deletions

View File

@ -0,0 +1,33 @@
/**
* Test that an erroneous Change Stream pipeline responds with an error that includes the
* "NonResumableChangeStreamError" label.
*/
(function() {
"use strict";
load("jstests/libs/collection_drop_recreate.js"); // For assertDropAndRecreateCollection.
// Drop and recreate the collections to be used in this set of tests.
const coll = assertDropAndRecreateCollection(db, "change_stream_error_label");
// Attaching a projection to the Change Stream that filters out the resume token (stored in the
// _id field) guarantees a ChangeStreamFatalError as soon as we get the first change.
const changeStream = coll.watch([{$project: {_id: 0}}], {batchSize: 1});
assert.commandWorked(coll.insert({a: 1}));
const err = assert.throws(function() {
// Call hasNext() until it throws an error or unexpectedly returns true. We need the
// assert.soon() to keep trying here, because the above insert command isn't immediately
// observable to the change stream in sharded configurations.
assert.soon(function() {
return changeStream.hasNext();
});
});
// The hasNext() sends a getMore command, which should generate a ChangeStreamFatalError reply
// that includes the NonResumableChangeStreamError errorLabel.
assert.commandFailedWithCode(err, ErrorCodes.ChangeStreamFatalError);
assert("errorLabels" in err, err);
assert.contains("NonResumableChangeStreamError", err.errorLabels, err);
}());

View File

@ -357,4 +357,4 @@ error_class("SnapshotError", ["SnapshotTooOld", "SnapshotUnavailable", "StaleChu
error_class("VoteAbortError", ["NoSuchTransaction", "TransactionTooOld"])
error_class("NonRetryableChangeStreamError", ["ChangeStreamFatalError", "ChangeStreamHistoryLost"])
error_class("NonResumableChangeStreamError", ["ChangeStreamFatalError", "ChangeStreamHistoryLost"])

View File

@ -36,23 +36,26 @@ BSONObj getErrorLabels(const OperationSessionInfoFromClient& sessionOptions,
const std::string& commandName,
ErrorCodes::Error code,
bool hasWriteConcernError) {
BSONArrayBuilder labelArray;
// By specifying "autocommit", the user indicates they want to run a transaction.
// It is always false when set.
if (!sessionOptions.getAutocommit()) {
return {};
// Note that we only apply the TransientTxnError label if the "autocommit" field is present in
// the session options. When present, "autocommit" will always be false, so we don't check its
// value.
if (sessionOptions.getAutocommit() &&
isTransientTransactionError(code,
hasWriteConcernError,
commandName == "commitTransaction" ||
commandName == "coordinateCommitTransaction")) {
// An error code for which isTransientTransactionError() is true indicates a transaction
// failure with no persistent side effects.
labelArray << txn::TransientTxnErrorFieldName;
}
// The errors that indicate the transaction fails without any persistent side-effect.
bool isTransient = isTransientTransactionError(
code,
hasWriteConcernError,
commandName == "commitTransaction" || commandName == "coordinateCommitTransaction");
if (isTransient) {
return BSON("errorLabels" << BSON_ARRAY(txn::TransientTxnErrorFieldName));
if (ErrorCodes::isNonResumableChangeStreamError(code)) {
labelArray << "NonResumableChangeStreamError";
}
return {};
return (labelArray.arrSize() > 0) ? BSON("errorLabels" << labelArray.arr()) : BSONObj();
}
} // namespace mongo