-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Add request timeout for ValidatorSites: #2902
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,15 +26,15 @@ | |
#include <ripple/basics/Slice.h> | ||
#include <ripple/json/json_reader.h> | ||
#include <ripple/protocol/JsonFields.h> | ||
#include <boost/algorithm/clamp.hpp> | ||
#include <boost/regex.hpp> | ||
#include <algorithm> | ||
|
||
namespace ripple { | ||
|
||
// default site query frequency - 5 minutes | ||
auto constexpr DEFAULT_REFRESH_INTERVAL = std::chrono::minutes{5}; | ||
auto constexpr ERROR_RETRY_INTERVAL = std::chrono::seconds{30}; | ||
unsigned short constexpr MAX_REDIRECTS = 3; | ||
auto constexpr DEFAULT_REFRESH_INTERVAL = std::chrono::minutes{5}; | ||
auto constexpr ERROR_RETRY_INTERVAL = std::chrono::seconds{30}; | ||
unsigned short constexpr MAX_REDIRECTS = 3; | ||
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. I'll express a preference. My preference is to reserve |
||
|
||
ValidatorSite::Site::Resource::Resource (std::string uri_) | ||
: uri {std::move(uri_)} | ||
|
@@ -90,14 +90,16 @@ ValidatorSite::Site::Site (std::string uri) | |
ValidatorSite::ValidatorSite ( | ||
boost::asio::io_service& ios, | ||
ValidatorList& validators, | ||
beast::Journal j) | ||
beast::Journal j, | ||
std::chrono::seconds timeout) | ||
: ios_ (ios) | ||
, validators_ (validators) | ||
, j_ (j) | ||
, timer_ (ios_) | ||
, fetching_ (false) | ||
, pending_ (false) | ||
, stopping_ (false) | ||
, requestTimeout_ (timeout) | ||
{ | ||
} | ||
|
||
|
@@ -168,10 +170,12 @@ ValidatorSite::stop() | |
{ | ||
std::unique_lock<std::mutex> lock{state_mutex_}; | ||
stopping_ = true; | ||
cv_.wait(lock, [&]{ return ! fetching_; }); | ||
|
||
// work::cancel() must be called before the | ||
// cv wait in order to kick any asio async operations | ||
// that might be pending. | ||
if(auto sp = work_.lock()) | ||
sp->cancel(); | ||
cv_.wait(lock, [&]{ return ! fetching_; }); | ||
|
||
error_code ec; | ||
timer_.cancel(ec); | ||
|
@@ -210,17 +214,30 @@ ValidatorSite::makeRequest ( | |
fetching_ = true; | ||
sites_[siteIdx].activeResource = resource; | ||
std::shared_ptr<detail::Work> sp; | ||
auto timeoutCancel = | ||
[this] () | ||
{ | ||
std::unique_lock <std::mutex> lock_state{state_mutex_}; | ||
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. Taking this lock looks safe assuming that the previously existing code was safe. Both I'll add a nit, however. This file is using a mix of If you agree with my assessment that using the smaller hammer is the best choice, then I found a total of six 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. fixed - thanks for pointing that out. |
||
error_code ec; | ||
timer_.cancel_one(ec); | ||
}; | ||
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. This overload of 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. fixed |
||
auto onFetch = | ||
[this, siteIdx] (error_code const& err, detail::response_type&& resp) | ||
[this, siteIdx, timeoutCancel] ( | ||
error_code const& err, detail::response_type&& resp) | ||
{ | ||
timeoutCancel (); | ||
onSiteFetch (err, std::move(resp), siteIdx); | ||
}; | ||
|
||
auto onFetchFile = | ||
[this, siteIdx] (error_code const& err, std::string const& resp) | ||
{ | ||
onTextFetch (err, resp, siteIdx); | ||
}; | ||
[this, siteIdx, timeoutCancel] ( | ||
error_code const& err, std::string const& resp) | ||
{ | ||
timeoutCancel (); | ||
onTextFetch (err, resp, siteIdx); | ||
}; | ||
|
||
JLOG (j_.debug()) << "Starting request for " << resource->uri; | ||
|
||
if (resource->pUrl.scheme == "https") | ||
{ | ||
|
@@ -252,6 +269,32 @@ ValidatorSite::makeRequest ( | |
|
||
work_ = sp; | ||
sp->run (); | ||
// start a timer for the request, which shouldn't take more | ||
// than requestTimeout_ to complete | ||
std::unique_lock <std::mutex> lock_state{state_mutex_}; | ||
timer_.expires_at ( clock_type::now() + requestTimeout_); | ||
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. I don't have an opinion about this, but I'm curious why you called
Also, is the extra space in front of 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. yep, |
||
timer_.async_wait ( std::bind ( | ||
&ValidatorSite::onRequestTimeout, this, siteIdx, std::placeholders::_1)); | ||
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. I personally prefer lambdas over
If you make this change you could consider a similar update to the one other use of |
||
} | ||
|
||
void | ||
ValidatorSite::onRequestTimeout ( | ||
std::size_t siteIdx, | ||
error_code const& ec) | ||
{ | ||
if (ec) | ||
return; | ||
|
||
{ | ||
std::unique_lock <std::mutex> lock_site{sites_mutex_}; | ||
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. Both the 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. While you are in the neighborhood... it seems the caught exception at line 327, It would also be nice to add a documenting param to 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. yep - good point. I'll move the lock inside the try and that should fix it. I'll also remove that assert since we don't have a state lock here and I'll add the lock param to setTimer too. |
||
JLOG (j_.warn()) << | ||
"Request for " << sites_[siteIdx].activeResource->uri << | ||
" took too long"; | ||
} | ||
|
||
std::unique_lock<std::mutex> lock_state{state_mutex_}; | ||
if(auto sp = work_.lock()) | ||
sp->cancel(); | ||
} | ||
|
||
void | ||
|
@@ -370,10 +413,14 @@ ValidatorSite::parseJsonResponse ( | |
if (body.isMember ("refresh_interval") && | ||
body["refresh_interval"].isNumeric ()) | ||
{ | ||
// TODO: should we sanity check/clamp this value | ||
// to something reasonable? | ||
sites_[siteIdx].refreshInterval = | ||
std::chrono::minutes{body["refresh_interval"].asUInt ()}; | ||
auto refresh = | ||
boost::algorithm::clamp( | ||
body["refresh_interval"].asUInt (), | ||
1u, | ||
60u*24); | ||
sites_[siteIdx].refreshInterval = std::chrono::minutes{refresh}; | ||
sites_[siteIdx].nextRefresh = | ||
clock_type::now() + sites_[siteIdx].refreshInterval; | ||
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. I think the following code is a bit more consistent with time units and possibly easier to read:
Your choice. 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. 👍 - fixed 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. suggestion... |
||
} | ||
} | ||
|
||
|
@@ -435,6 +482,8 @@ ValidatorSite::onSiteFetch( | |
{ | ||
{ | ||
std::lock_guard <std::mutex> lock_sites{sites_mutex_}; | ||
JLOG (j_.debug()) << "Got completion for " | ||
<< sites_[siteIdx].activeResource->uri; | ||
auto onError = [&](std::string const& errMsg, bool retry) | ||
{ | ||
sites_[siteIdx].lastRefreshStatus.emplace( | ||
|
@@ -503,7 +552,7 @@ ValidatorSite::onSiteFetch( | |
sites_[siteIdx].activeResource.reset(); | ||
} | ||
|
||
std::lock_guard <std::mutex> lock_state{state_mutex_}; | ||
std::unique_lock <std::mutex> lock_state{state_mutex_}; | ||
fetching_ = false; | ||
if (! stopping_) | ||
setTimer (); | ||
|
@@ -544,7 +593,7 @@ ValidatorSite::onTextFetch( | |
sites_[siteIdx].activeResource.reset(); | ||
} | ||
|
||
std::lock_guard <std::mutex> lock_state{state_mutex_}; | ||
std::unique_lock <std::mutex> lock_state{state_mutex_}; | ||
fetching_ = false; | ||
if (! stopping_) | ||
setTimer (); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an observation. When I first saw this my inclination was to move the
Journal
to the end of the arguments, since that matches our usual parameter order. I see you made an exception to that expectation so you could default the timeout.After playing around with it a bit I'm comfortable with your choice. There's (currently) only one place where the timeout is explicitly passed in by the caller, and that's in a unit test.
Not asking for a change. I just thought you'd want to know that I played around with the options and agree that you made the right choice.