SERVER-108318 Introduce new error label indicating a failure is unconditionally retryable (#42840)

GitOrigin-RevId: 21ca15bf2da061b81ade7f0f7d6bd966126f9459
This commit is contained in:
Guillaume Racicot 2025-10-21 11:42:17 -04:00 committed by MongoDB Bot
parent 07d77e5061
commit 1d26946296
6 changed files with 33 additions and 10 deletions

View File

@ -112,9 +112,9 @@ function testRateLimiterMetrics(conn, exemptConn) {
// We run inserts command that will either pass or fail. When it fails, we validate the error
// code and label.
for (let i = 0; i < requestAmount; ++i) {
const assertContainSystemOverloadedErrorLabel = (res) => {
const assertContainsExpectedErrorLabels = (res) => {
assert(res.hasOwnProperty("errorLabels"), res);
assert.sameMembers(["SystemOverloadedError"], res.errorLabels);
assert.sameMembers(["SystemOverloadedError", "RetryableError"], res.errorLabels);
};
const collName = `${jsTest.name()}_coll`;
@ -123,8 +123,8 @@ function testRateLimiterMetrics(conn, exemptConn) {
const result = db.runCommand({insert: collName, documents: [{dummy: 1}]});
if (result.ok === 0) {
assert.commandFailedWithCode(result, ErrorCodes.RateLimitExceeded);
assertContainSystemOverloadedErrorLabel(result);
assert.commandFailedWithCode(result, ErrorCodes.IngressRequestRateLimitExceeded);
assertContainsExpectedErrorLabels(result);
}
}
@ -293,7 +293,8 @@ function runTestCompressed() {
for (let i = 0; i < requestAmount; ++i) {
const assertContainSystemOverloadedErrorLabel = (res) => {
assert(res.hasOwnProperty("errorLabels"), res);
assert.sameMembers(["SystemOverloadedError"], res.errorLabels);
assert.sameMembers(["SystemOverloadedError", "RetryableError"],
res.errorLabels);
};
const collName = `${jsTest.name()}_coll`;
@ -301,7 +302,8 @@ function runTestCompressed() {
const result = db.runCommand({insert: collName, documents: [{dummy: 1}]});
if (result.ok === 0) {
assert.commandFailedWithCode(result, ErrorCodes.RateLimitExceeded);
assert.commandFailedWithCode(result,
ErrorCodes.IngressRequestRateLimitExceeded);
assertContainSystemOverloadedErrorLabel(result);
}
}

View File

@ -919,6 +919,11 @@ error_codes:
- {code: 457, name: ReplayClientInternalError}
- {code: 458, name: ReplayClientSessionSchedulerError}
- {code: 459, name: ReplayClientSessionSimulationError}
- {
code: 462,
name: IngressRequestRateLimitExceeded,
categories: [SystemOverloadedError, RetriableError],
}
# Error codes 4000-8999 are reserved.

View File

@ -212,7 +212,12 @@ Status IngressRequestRateLimiter::admitRequest(Client* client) {
return Status::OK();
}
return _rateLimiter.tryAcquireToken();
auto rateLimitResult = _rateLimiter.tryAcquireToken();
if (MONGO_unlikely(rateLimitResult == admission::RateLimiter::kRejectedErrorCode)) {
return Status{ErrorCodes::IngressRequestRateLimitExceeded, rateLimitResult.reason()};
}
return rateLimitResult;
}
void IngressRequestRateLimiter::updateRateParameters(double refreshRatePerSec,

View File

@ -177,6 +177,12 @@ bool ErrorLabelBuilder::isResumableChangeStreamError() const {
return swLitePipe.isOK() && swLitePipe.getValue().hasChangeStream();
}
bool ErrorLabelBuilder::isOperationIdempotent() const {
// TODO: SERVER-108898 When OperationContext support marking an operation as idempotent, check
// for idempotency to apply the error label
return false;
}
bool ErrorLabelBuilder::isErrorWithNoWritesPerformed() const {
if (!_code && !_wcCode) {
return false;
@ -208,6 +214,8 @@ void ErrorLabelBuilder::build(BSONArrayBuilder& labels) const {
// SERVER-66479 and DRIVERS-2327).
labels << ErrorLabel::kNoWritesPerformed;
}
} else if (isOperationIdempotent()) {
labels << ErrorLabel::kRetryableError;
}
}

View File

@ -53,6 +53,7 @@ constexpr inline auto kNoWritesPerformed = "NoWritesPerformed"_sd;
constexpr inline auto kStreamProcessorRetryableError = "StreamProcessorRetryableError"_sd;
constexpr inline auto kStreamProcessorUserError = "StreamProcessorUserError"_sd;
constexpr inline auto kSystemOverloadedError = "SystemOverloadedError"_sd;
constexpr inline auto kRetryableError = "RetryableError"_sd;
} // namespace ErrorLabel
@ -89,6 +90,7 @@ public:
bool isStreamProcessorUserError() const;
bool isStreamProcessorRetryableError() const;
bool isSystemOverloadedError() const;
bool isOperationIdempotent() const;
private:
bool _isCommitOrAbort() const;

View File

@ -736,13 +736,14 @@ DbResponse makeDbResponseErrorForRateLimiting(const Message& message, const Stat
const auto replyBuilder = rpc::makeReplyBuilder(rpc::protocolForMessage(message));
replyBuilder->setCommandReply(status, {});
// We only expect errors to have no error label or system overloaded error label for now
// As this function is only used for rate limiting at the moment, this is acceptable
if (isSystemOverloadedError(status.code())) {
// We need to add error labels manually as ErrorLabelBuilder requires an operation context and
// we are still before the creation of the operation context at this point.
if (MONGO_likely(status == ErrorCodes::IngressRequestRateLimitExceeded)) {
auto commandBodyBob = replyBuilder->getBodyBuilder();
{
BSONArrayBuilder arrayBuilder(commandBodyBob.subarrayStart(kErrorLabelsFieldName));
arrayBuilder.append(ErrorLabel::kSystemOverloadedError);
arrayBuilder.append(ErrorLabel::kRetryableError);
}
}