summaryrefslogtreecommitdiff
path: root/src/mongo/db/error_labels.cpp
diff options
context:
space:
mode:
authorBrett Nawrocki <brett.nawrocki@mongodb.com>2022-05-23 21:56:12 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-06-07 16:11:05 +0000
commit612dc47958777d5ca69216359cfe34c06bc66c64 (patch)
treedd416a10427215743dacda61520a79897d23ef8a /src/mongo/db/error_labels.cpp
parentf0a44617983763b0fdc1f37bdc35e391184ea63d (diff)
downloadmongo-612dc47958777d5ca69216359cfe34c06bc66c64.tar.gz
SERVER-66479 Add NoWritesPerformed error label
Diffstat (limited to 'src/mongo/db/error_labels.cpp')
-rw-r--r--src/mongo/db/error_labels.cpp47
1 files changed, 38 insertions, 9 deletions
diff --git a/src/mongo/db/error_labels.cpp b/src/mongo/db/error_labels.cpp
index 4a345922d00..c159d451117 100644
--- a/src/mongo/db/error_labels.cpp
+++ b/src/mongo/db/error_labels.cpp
@@ -137,18 +137,38 @@ bool ErrorLabelBuilder::isResumableChangeStreamError() const {
return swLitePipe.isOK() && swLitePipe.getValue().hasChangeStream();
}
+bool ErrorLabelBuilder::isErrorWithNoWritesPerformed() const {
+ if (!_code && !_wcCode) {
+ return false;
+ }
+ if (_lastOpBeforeRun.isNull() || _lastOpAfterRun.isNull()) {
+ // Last OpTimes are unknown or not usable for determining whether or not a write was
+ // attempted.
+ return false;
+ }
+ return _lastOpBeforeRun == _lastOpAfterRun;
+}
+
void ErrorLabelBuilder::build(BSONArrayBuilder& labels) const {
// PLEASE CONSULT DRIVERS BEFORE ADDING NEW ERROR LABELS.
bool hasTransientTransactionOrRetryableWriteError = false;
if (isTransientTransactionError()) {
labels << ErrorLabel::kTransientTransaction;
hasTransientTransactionOrRetryableWriteError = true;
- } else if (isRetryableWriteError()) {
- // In the rare case where RetryableWriteError and TransientTransactionError are not mutually
- // exclusive, only append the TransientTransactionError label so users know to retry the
- // entire transaction.
- labels << ErrorLabel::kRetryableWrite;
- hasTransientTransactionOrRetryableWriteError = true;
+ } else {
+ if (isRetryableWriteError()) {
+ // In the rare case where RetryableWriteError and TransientTransactionError are not
+ // mutually exclusive, only append the TransientTransactionError label so users know to
+ // retry the entire transaction.
+ labels << ErrorLabel::kRetryableWrite;
+ hasTransientTransactionOrRetryableWriteError = true;
+ if (isErrorWithNoWritesPerformed()) {
+ // The NoWritesPerformed error label is only relevant for retryable writes so that
+ // drivers can determine what error to return when faced with multiple errors (see
+ // SERVER-66479 and DRIVERS-2327).
+ labels << ErrorLabel::kNoWritesPerformed;
+ }
+ }
}
// Change streams cannot run in a transaction, and cannot be a retryable write. Since these
@@ -171,7 +191,9 @@ BSONObj getErrorLabels(OperationContext* opCtx,
boost::optional<ErrorCodes::Error> code,
boost::optional<ErrorCodes::Error> wcCode,
bool isInternalClient,
- bool isMongos) {
+ bool isMongos,
+ const repl::OpTime& lastOpBeforeRun,
+ const repl::OpTime& lastOpAfterRun) {
if (MONGO_unlikely(errorLabelsOverride(opCtx))) {
// This command was failed by a failCommand failpoint. Thus, we return the errorLabels
// specified in the failpoint to supress any other error labels that would otherwise be
@@ -184,8 +206,15 @@ BSONObj getErrorLabels(OperationContext* opCtx,
}
BSONArrayBuilder labelArray;
- ErrorLabelBuilder labelBuilder(
- opCtx, sessionOptions, commandName, code, wcCode, isInternalClient, isMongos);
+ ErrorLabelBuilder labelBuilder(opCtx,
+ sessionOptions,
+ commandName,
+ code,
+ wcCode,
+ isInternalClient,
+ isMongos,
+ lastOpBeforeRun,
+ lastOpAfterRun);
labelBuilder.build(labelArray);
return (labelArray.arrSize() > 0) ? BSON(kErrorLabelsFieldName << labelArray.arr()) : BSONObj();