Skip to content

Commit ba858ae

Browse files
committed
Update report-only mode to include (proposed) rejection reason
1 parent 7be19f4 commit ba858ae

File tree

1 file changed

+30
-21
lines changed

1 file changed

+30
-21
lines changed

src/middleware/balance_capacity.rs

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,32 @@ impl BalanceCapacity {
3737
dl_only_at_percentage: read_env_percentage("WEB_CAPACITY_DL_ONLY_PCT", 80),
3838
}
3939
}
40+
41+
/// Handle a request normally
42+
fn handle(&self, request: &mut dyn RequestExt) -> AfterResult {
43+
self.handler.as_ref().unwrap().call(request)
44+
}
45+
46+
/// Handle a request when load exceeds a threshold
47+
///
48+
/// In report-only mode, log metadata is added but the request is still served. Otherwise,
49+
/// the request is rejected with a service unavailable response.
50+
fn handle_high_load(&self, request: &mut dyn RequestExt, note: &str) -> AfterResult {
51+
if self.report_only {
52+
// In report-only mode we serve all requests but add log metadata
53+
super::log_request::add_custom_metadata(request, "would_reject", note);
54+
self.handle(request)
55+
} else {
56+
// Reject the request
57+
super::log_request::add_custom_metadata(request, "cause", note);
58+
let body = "Service temporarily unavailable";
59+
Response::builder()
60+
.status(StatusCode::SERVICE_UNAVAILABLE)
61+
.header(header::CONTENT_LENGTH, body.len())
62+
.body(Body::from_static(body.as_bytes()))
63+
.map_err(box_error)
64+
}
65+
}
4066
}
4167

4268
impl AroundMiddleware for BalanceCapacity {
@@ -49,50 +75,33 @@ impl Handler for BalanceCapacity {
4975
fn call(&self, request: &mut dyn RequestExt) -> AfterResult {
5076
// The _drop_on_exit ensures the counter is decremented for all exit paths (including panics)
5177
let (_drop_on_exit, count) = RequestCounter::add_one(&self.in_flight_requests);
52-
let handler = self.handler.as_ref().unwrap();
5378
let load = 100 * count / self.capacity;
5479

5580
// Begin logging request count so early stages of load increase can be located
5681
if load >= self.log_at_percentage {
5782
super::log_request::add_custom_metadata(request, "in_flight_requests", count);
5883
}
5984

60-
// In report-only mode we serve all requests and only enforce the logging limit above
61-
if self.report_only {
62-
return handler.call(request);
63-
}
64-
6585
// Download requests are always accepted
6686
if request.path().starts_with("/api/v1/crates/") && request.path().ends_with("/download") {
67-
return handler.call(request);
87+
return self.handle(request);
6888
}
6989

7090
// Reject read-only requests as load nears capacity. Bots are likely to send only safe
7191
// requests and this helps prioritize requests that users may be reluctant to retry.
7292
if load >= self.throttle_at_percentage && request.method().is_safe() {
73-
return over_capacity_response(request);
93+
return self.handle_high_load(request, "over capacity (throttle)");
7494
}
7595

7696
// As load reaches capacity, all non-download requests are rejected
7797
if load >= self.dl_only_at_percentage {
78-
return over_capacity_response(request);
98+
return self.handle_high_load(request, "over capacity (download only)");
7999
}
80100

81-
handler.call(request)
101+
self.handle(request)
82102
}
83103
}
84104

85-
fn over_capacity_response(request: &mut dyn RequestExt) -> AfterResult {
86-
// TODO: Generate an alert so we can investigate
87-
super::log_request::add_custom_metadata(request, "cause", "over capacity");
88-
let body = "Service temporarily unavailable";
89-
Response::builder()
90-
.status(StatusCode::SERVICE_UNAVAILABLE)
91-
.header(header::CONTENT_LENGTH, body.len())
92-
.body(Body::from_static(body.as_bytes()))
93-
.map_err(box_error)
94-
}
95-
96105
fn read_env_percentage(name: &str, default: usize) -> usize {
97106
if let Ok(value) = env::var(name) {
98107
value.parse().unwrap_or(default)

0 commit comments

Comments
 (0)