-
Notifications
You must be signed in to change notification settings - Fork 857
APM-164: Add basic overload control to arangod. #14796
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
6f02ef5
8540a88
f29c93a
6d37569
d29aaec
65cac54
398a40a
02fe39d
a83853c
9bb8a5a
a6caf93
133a524
f05726b
1d373e9
44a4444
fdf8560
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
This change adds the `x-arango-queue-time-seconds` header to all responses sent by arangod. This header contains the most recent request dequeing time (in seconds) as tracked by the scheduler. This value can be used by client applications and drivers to detect server overload and react on it. The new startup option `--http.return-queue-time-header` can be set to `false` to suppress these headers in responses sent by arangod. In addition, client applications and drivers can optionally augment their requests sent to arangod with the a header of the same name. If set, the value of the header should contain the maximum queuing time (in seconds) that the client is willing to accept. If the header is set in an incoming request, arangod will compare the current dequeing time from its scheduler with the maximum queue time value contained in the request. If the current dequeing time exceeds the value set in the header, arangod will reject the request and return HTTP 412 (precondition failed) with the new error code 21004 (queue time violated).
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,31 +65,6 @@ inline bool startsWith(std::string const& path, char const* other) { | |
path.compare(0, size, other, size) == 0); | ||
} | ||
|
||
} // namespace | ||
|
||
// ----------------------------------------------------------------------------- | ||
// --SECTION-- constructors and destructors | ||
// ----------------------------------------------------------------------------- | ||
|
||
CommTask::CommTask(GeneralServer& server, | ||
ConnectionInfo info) | ||
: _server(server), | ||
_connectionInfo(std::move(info)), | ||
_connectionStatistics(ConnectionStatistics::acquire()), | ||
_auth(AuthenticationFeature::instance()) { | ||
TRI_ASSERT(_auth != nullptr); | ||
_connectionStatistics.SET_START(); | ||
} | ||
|
||
CommTask::~CommTask() { | ||
_connectionStatistics.SET_END(); | ||
} | ||
|
||
// ----------------------------------------------------------------------------- | ||
// --SECTION-- protected methods | ||
// ----------------------------------------------------------------------------- | ||
|
||
namespace { | ||
TRI_vocbase_t* lookupDatabaseFromRequest(application_features::ApplicationServer& server, | ||
GeneralRequest& req) { | ||
// get database name from request | ||
|
@@ -127,8 +102,43 @@ bool resolveRequestContext(application_features::ApplicationServer& server, | |
// the "true" means the request is the owner of the context | ||
return true; | ||
} | ||
|
||
bool queueTimeViolated(GeneralRequest const& req) { | ||
// check if the client sent the "x-arango-queue-time-seconds" header | ||
bool found = false; | ||
std::string const& queueTimeValue = req.header(StaticStrings::XArangoQueueTimeSeconds, found); | ||
if (found) { | ||
// yes, now parse the sent time value | ||
double requestedQueueTime = StringUtils::doubleDecimal(queueTimeValue); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the outcome if queueTimeValue is not a valid double? If I understand it will just get ignored, would it make sense signal/inform here (e.g., at least on higher log-levels)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood, this is set by ArangoDB and not user specified |
||
if (requestedQueueTime > 0.0) { | ||
// value is > 0.0, so now check the last dequeue time that the scheduler reported | ||
double lastDequeueTime = static_cast<double>( | ||
SchedulerFeature::SCHEDULER->getLastLowPriorityDequeueTime()) / 1000.0; | ||
|
||
if (lastDequeueTime > requestedQueueTime) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
} // namespace | ||
|
||
CommTask::CommTask(GeneralServer& server, | ||
ConnectionInfo info) | ||
: _server(server), | ||
_connectionInfo(std::move(info)), | ||
_connectionStatistics(ConnectionStatistics::acquire()), | ||
_auth(AuthenticationFeature::instance()) { | ||
TRI_ASSERT(_auth != nullptr); | ||
_connectionStatistics.SET_START(); | ||
} | ||
|
||
CommTask::~CommTask() { | ||
_connectionStatistics.SET_END(); | ||
} 8000 td> | ||
|
||
/// Must be called before calling executeRequest, will send an error | ||
/// response if execution is supposed to be aborted | ||
|
||
|
@@ -311,6 +321,12 @@ void CommTask::finishExecution(GeneralResponse& res, std::string const& origin) | |
// use "IfNotSet" to not overwrite an existing response header | ||
res.setHeaderNCIfNotSet(StaticStrings::XContentTypeOptions, StaticStrings::NoSniff); | ||
} | ||
|
||
// add "x-arango-queue-time-seconds" header | ||
if (_server.server().getFeature<GeneralServerFeature>().returnQueueTimeHeader()) { | ||
res.setHeaderNC(StaticStrings::XArangoQueueTimeSeconds, | ||
std::to_string(static_cast<double>(SchedulerFeature::SCHEDULER->getLastLowPriorityDequeueTime()) / 1000.0)); | ||
} | ||
} | ||
|
||
/// Push this request into the execution pipeline | ||
|
@@ -336,8 +352,17 @@ void CommTask::executeRequest(std::unique_ptr<GeneralRequest> request, | |
LOG_TOPIC("2cece", WARN, Logger::REQUESTS) | ||
<< "could not find corresponding request/response"; | ||
} | ||
|
||
rest::ContentType const respType = request->contentTypeResponse(); | ||
|
||
// check if "x-arango-queue-time-seconds" header was set, and the value | ||
// contained in it is above the current dequeing time | ||
jsteemann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (::queueTimeViolated(*request)) { | ||
sendErrorResponse(rest::ResponseCode::PRECONDITION_FAILED, | ||
respType, messageId, TRI_ERROR_QUEUE_TIME_REQUIREMENT_VIOLATED); | ||
return; | ||
} | ||
|
||
// create a handler, this takes ownership of request and response | ||
auto& server = _server.server(); | ||
auto& factory = server.getFeature<GeneralServerFeature>().handlerFactory(); | ||
|
@@ -351,7 +376,7 @@ void CommTask::executeRequest(std::unique_ptr<GeneralRequest> request, | |
VPackBuffer<uint8_t>()); | ||
return; | ||
} | ||
|
||
jsteemann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// forward to correct server if necessary | ||
bool forwarded; | ||
auto res = handler->forwardRequest(forwarded); | ||
|
Uh oh!
There was an error while loading. Please reload this page.