Skip to content

Commit dd1b2ff

Browse files
muirdmfacebook-github-bot
authored andcommitted
Back out "Back out "[sl] http: add "per batch" concurrent request limit""
Summary: We backed this out thinking it could have caused S513355, but we later determined it was innocent. Summary from original diff D73602841: I think it is useful to have two concurrent request limits - one global (per client object), and one per batch of requests. By batch, I mean, for example, when you fetch 10 million files and split into 1_000 separate requests each with 10_000 files - you have a batch of 1k requests. You don't really want or need to to fire off a lot of requests at once because you can saturate your bandwidth with a small number of requests, and having more requests than you need can unnecessarily overload the server, and make individual requests take longer than otherwise needed to finish, which increases the odds of timeouts or other network errors. The global limit will be set higher, allowing for other concurrent requests to not be starved by a single big batch. Reviewed By: lmvasquezg Differential Revision: D75107491 fbshipit-source-id: b105f319e731ad3cef6adcb8ac3e4574d015d16c
1 parent 07fa11f commit dd1b2ff

2 files changed

Lines changed: 46 additions & 17 deletions

File tree

eden/scm/lib/edenapi/src/builder.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ impl HttpClientBuilder {
210210
let max_requests = get_config(config, "edenapi", "max-concurrent-requests")?
211211
.or(get_config(config, "edenapi", "maxrequests")?);
212212

213+
let max_requests_per_batch =
214+
get_config(config, "edenapi", "max-concurrent-requests-per-batch")?;
215+
213216
let try_route_consistently =
214217
get_config(config, "edenapi", "try-route-consistently")?.unwrap_or_default();
215218

@@ -305,6 +308,7 @@ impl HttpClientBuilder {
305308
let mut http_config = hg_http::http_config(config, &server_url)?;
306309
http_config.verbose_stats |= debug;
307310
http_config.max_concurrent_requests = max_requests;
311+
http_config.max_concurrent_requests_per_batch = max_requests_per_batch;
308312

309313
let builder = HttpClientBuilder {
310314
repo_name,

eden/scm/lib/http-client/src/client.rs

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ pub struct Config {
6565
// library to limit the number of in-flight requests separately, _before_ the
6666
// requests are given to curl.
6767
pub max_concurrent_requests: Option<usize>,
68+
69+
// Limit the number of concurrent requests for a "batch" of requests passed at once to
70+
// client.send_async(). This allows us to have a high global request limit for small
71+
// "random" requests, while having lower limits for heavy requests (e.g. fetch 10m
72+
// files across 1000 requests).
73+
pub max_concurrent_requests_per_batch: Option<usize>,
74+
6875
// Escape hatch to turn off our request limiting.
6976
pub limit_requests: bool,
7077
// Escape hatch to turn off our response body limiting.
@@ -98,6 +105,7 @@ impl Default for Config {
98105
client_info: None,
99106
disable_tls_verification: false,
100107
max_concurrent_requests: None, // No limit by default
108+
max_concurrent_requests_per_batch: None,
101109
limit_requests: true,
102110
limit_response_buffering: true,
103111
unix_socket_domains: HashSet::new(),
@@ -251,23 +259,37 @@ impl HttpClient {
251259
/// until all of the transfers are complete, and will return
252260
/// the total stats across all transfers when complete.
253261
pub fn stream(&self, requests: Vec<StreamRequest>) -> Result<Stats, HttpClientError> {
262+
// This is a "local" limit for how many concurrent requests we allow for a single
263+
// batch of requests. Requests are still subject to the global limit via self.claimer.
264+
let mut allowed_requests = self
265+
.config
266+
.max_concurrent_requests_per_batch
267+
.unwrap_or(requests.len());
268+
254269
// Add as many of remaining requests to the handle as we can, limited by the claimer.
255-
let try_add =
256-
|h: &MultiDriver, reqs: &mut IntoIter<StreamRequest>| -> Result<(), HttpClientError> {
257-
for claim in self.claimer.try_claim_requests(reqs.len()) {
258-
let mut request = match reqs.next() {
259-
Some(request) => request,
260-
// Shouldn't happen, but just in case.
261-
None => break,
262-
};
263-
264-
self.event_listeners
265-
.trigger_new_request(request.request.ctx_mut());
266-
h.add(request.into_easy(claim)?)?;
267-
}
270+
let try_add = |h: &MultiDriver,
271+
reqs: &mut IntoIter<StreamRequest>,
272+
allowed_requests: &mut usize|
273+
-> Result<(), HttpClientError> {
274+
for claim in self
275+
.claimer
276+
.try_claim_requests((*allowed_requests).min(reqs.len()))
277+
{
278+
let mut request = match reqs.next() {
279+
Some(request) => request,
280+
// Shouldn't happen, but just in case.
281+
None => break,
282+
};
283+
284+
self.event_listeners
285+
.trigger_new_request(request.request.ctx_mut());
286+
h.add(request.into_easy(claim)?)?;
287+
288+
*allowed_requests -= 1;
289+
}
268290

269-
Ok(())
270-
};
291+
Ok(())
292+
};
271293

272294
let mut requests = requests.into_iter();
273295
let mut stats = Stats::default();
@@ -282,7 +304,7 @@ impl HttpClient {
282304
let driver = MultiDriver::new(multi.get(), self.config.verbose_stats);
283305

284306
// Add requests to the driver. This can add anywhere from zero to all the requests.
285-
try_add(&driver, &mut requests)?;
307+
try_add(&driver, &mut requests, &mut allowed_requests)?;
286308

287309
let mut tls_error = false;
288310
let result = driver
@@ -296,14 +318,17 @@ impl HttpClient {
296318

297319
self.report_result_and_drop_receiver(res)?;
298320

321+
allowed_requests += 1;
322+
299323
// A request finished - let's see if there are pending requests we can now add
300324
// to this multi. This allows pending requests to proceed without needing to
301325
// wait for _all_ in-progress requests to finish. Note that there may be other
302326
// curl multis active bound by the same request limit, so it is still possible
303327
// for our pending requests to wait longer than they need to (i.e. when a
304328
// request finishes on a different multi, our loop here will still wait for one
305329
// of our requests to finish before trying to enqueue new requests).
306-
try_add(&driver, &mut requests).map_err(|err| Abort::WithReason(err.into()))
330+
try_add(&driver, &mut requests, &mut allowed_requests)
331+
.map_err(|err| Abort::WithReason(err.into()))
307332
})
308333
.inspect(|stats| {
309334
self.event_listeners.trigger_stats(stats);

0 commit comments

Comments
 (0)