summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/change_streams/error_label.js33
-rw-r--r--src/mongo/base/error_codes.err2
-rw-r--r--src/mongo/db/handle_request_response.cpp29
3 files changed, 50 insertions, 14 deletions
diff --git a/jstests/change_streams/error_label.js b/jstests/change_streams/error_label.js
new file mode 100644
index 00000000000..1c9a00db356
--- /dev/null
+++ b/jstests/change_streams/error_label.js
@@ -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);
+}()); \ No newline at end of file
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index fe682f8eb62..837d4090b37 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -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"])
diff --git a/src/mongo/db/handle_request_response.cpp b/src/mongo/db/handle_request_response.cpp
index 062075b82db..6628c60e8bc 100644
--- a/src/mongo/db/handle_request_response.cpp
+++ b/src/mongo/db/handle_request_response.cpp
@@ -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