SERVER-108318 Introduce new error label indicating a failure is unconditionally retryable (#42840)
GitOrigin-RevId: 21ca15bf2da061b81ade7f0f7d6bd966126f9459
This commit is contained in:
parent
07d77e5061
commit
1d26946296
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user