Skip to content

Commit 320e018

Browse files
authored
Merge pull request #1054 from rylev/single-graph-endpoint
Add endpoint for single graph
2 parents 24b6b2d + a9e7bf3 commit 320e018

File tree

6 files changed

+136
-50
lines changed

6 files changed

+136
-50
lines changed

site/src/api.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,28 @@ pub mod dashboard {
4242
}
4343

4444
pub mod graph {
45+
use super::graphs::{GraphKind, Series};
46+
use collector::Bound;
47+
use serde::{Deserialize, Serialize};
48+
49+
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
50+
pub struct Request {
51+
pub benchmark: String,
52+
pub profile: String,
53+
pub scenario: String,
54+
pub metric: String,
55+
pub start: Bound,
56+
pub end: Bound,
57+
pub kind: GraphKind,
58+
}
59+
60+
#[derive(Debug, PartialEq, Clone, Serialize)]
61+
pub struct Response {
62+
pub series: Series,
63+
}
64+
}
65+
66+
pub mod graphs {
4567
use collector::Bound;
4668
use serde::{Deserialize, Serialize};
4769
use std::collections::{HashMap, HashSet};

site/src/load.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ pub struct SiteCtxt {
7979
/// Site configuration
8080
pub config: Config,
8181
/// Cached site landing page
82-
pub landing_page: ArcSwap<Option<Arc<crate::api::graph::Response>>>,
82+
pub landing_page: ArcSwap<Option<Arc<crate::api::graphs::Response>>>,
8383
/// Index of various common queries
8484
pub index: ArcSwap<crate::db::Index>,
8585
/// Database connection pool

site/src/request_handlers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod status_page;
99
pub use bootstrap::handle_bootstrap;
1010
pub use dashboard::handle_dashboard;
1111
pub use github::handle_github;
12-
pub use graph::handle_graphs;
12+
pub use graph::{handle_graph, handle_graphs};
1313
pub use next_commit::handle_next_commit;
1414
pub use self_profile::{
1515
handle_self_profile, handle_self_profile_processed_download, handle_self_profile_raw,

site/src/request_handlers/graph.rs

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,34 @@ use collector::Bound;
22
use std::collections::HashMap;
33
use std::sync::Arc;
44

5-
use crate::api::graph::GraphKind;
6-
use crate::api::{graph, ServerResult};
5+
use crate::api::graphs::GraphKind;
6+
use crate::api::{graph, graphs, ServerResult};
77
use crate::db::{self, ArtifactId, Benchmark, Profile, Scenario};
88
use crate::interpolate::IsInterpolated;
99
use crate::load::SiteCtxt;
1010
use crate::selector::{Query, Selector, SeriesResponse, Tag};
1111

12+
pub async fn handle_graph(
13+
request: graph::Request,
14+
ctxt: Arc<SiteCtxt>,
15+
) -> ServerResult<graph::Response> {
16+
log::info!("handle_graph({:?})", request);
17+
18+
create_graph(request, ctxt).await
19+
}
20+
1221
pub async fn handle_graphs(
13-
body: graph::Request,
14-
ctxt: &SiteCtxt,
15-
) -> ServerResult<Arc<graph::Response>> {
16-
log::info!("handle_graph({:?})", body);
22+
request: graphs::Request,
23+
ctxt: Arc<SiteCtxt>,
24+
) -> ServerResult<Arc<graphs::Response>> {
25+
log::info!("handle_graphs({:?})", request);
1726

18-
let is_default_query = body
19-
== graph::Request {
27+
let is_default_query = request
28+
== graphs::Request {
2029
start: Bound::None,
2130
end: Bound::None,
2231
stat: String::from("instructions:u"),
23-
kind: graph::GraphKind::Raw,
32+
kind: graphs::GraphKind::Raw,
2433
};
2534

2635
if is_default_query {
@@ -30,7 +39,7 @@ pub async fn handle_graphs(
3039
}
3140
}
3241

33-
let resp = graph_response(body, ctxt).await?;
42+
let resp = create_graphs(request, &ctxt).await?;
3443

3544
if is_default_query {
3645
ctxt.landing_page.store(Arc::new(Some(resp.clone())));
@@ -39,12 +48,36 @@ pub async fn handle_graphs(
3948
Ok(resp)
4049
}
4150

42-
async fn graph_response(
43-
body: graph::Request,
51+
async fn create_graph(
52+
request: graph::Request,
53+
ctxt: Arc<SiteCtxt>,
54+
) -> ServerResult<graph::Response> {
55+
let artifact_ids = artifact_ids_for_range(&ctxt, request.start, request.end);
56+
let mut series_iterator = ctxt
57+
.statistic_series(
58+
Query::new()
59+
.set::<String>(Tag::Benchmark, Selector::One(request.benchmark))
60+
.set::<String>(Tag::Profile, Selector::One(request.profile))
61+
.set::<String>(Tag::Scenario, Selector::One(request.scenario))
62+
.set::<String>(Tag::Metric, Selector::One(request.metric)),
63+
Arc::new(artifact_ids),
64+
)
65+
.await?
66+
.into_iter()
67+
.map(SeriesResponse::interpolate);
68+
69+
let result = series_iterator.next().unwrap();
70+
let graph_series = graph_series(result.series, request.kind);
71+
Ok(graph::Response {
72+
series: graph_series,
73+
})
74+
}
75+
76+
async fn create_graphs(
77+
request: graphs::Request,
4478
ctxt: &SiteCtxt,
45-
) -> ServerResult<Arc<graph::Response>> {
46-
let range = ctxt.data_range(body.start..=body.end);
47-
let commits: Arc<Vec<_>> = Arc::new(range.into_iter().map(|c| c.into()).collect());
79+
) -> ServerResult<Arc<graphs::Response>> {
80+
let artifact_ids = Arc::new(artifact_ids_for_range(ctxt, request.start, request.end));
4881
let mut benchmarks = HashMap::new();
4982

5083
let interpolated_responses: Vec<_> = ctxt
@@ -53,23 +86,23 @@ async fn graph_response(
5386
.set::<String>(Tag::Benchmark, Selector::All)
5487
.set::<String>(Tag::Profile, Selector::All)
5588
.set::<String>(Tag::Scenario, Selector::All)
56-
.set::<String>(Tag::Metric, Selector::One(body.stat)),
57-
commits.clone(),
89+
.set::<String>(Tag::Metric, Selector::One(request.stat)),
90+
artifact_ids.clone(),
5891
)
5992
.await?
6093
.into_iter()
6194
.map(|sr| sr.interpolate().map(|series| series.collect::<Vec<_>>()))
6295
.collect();
6396

64-
let summary_benchmark = create_summary(ctxt, &interpolated_responses, body.kind)?;
97+
let summary_benchmark = create_summary(ctxt, &interpolated_responses, request.kind)?;
6598

6699
benchmarks.insert("Summary".to_string(), summary_benchmark);
67100

68101
for response in interpolated_responses {
69102
let benchmark = response.path.get::<Benchmark>()?.to_string();
70103
let profile = *response.path.get::<Profile>()?;
71104
let scenario = response.path.get::<Scenario>()?.to_string();
72-
let graph_series = graph_series(response.series.into_iter(), body.kind);
105+
let graph_series = graph_series(response.series.into_iter(), request.kind);
73106

74107
benchmarks
75108
.entry(benchmark)
@@ -79,8 +112,8 @@ async fn graph_response(
79112
.insert(scenario, graph_series);
80113
}
81114

82-
Ok(Arc::new(graph::Response {
83-
commits: Arc::try_unwrap(commits)
115+
Ok(Arc::new(graphs::Response {
116+
commits: Arc::try_unwrap(artifact_ids)
84117
.unwrap()
85118
.into_iter()
86119
.map(|c| match c {
@@ -92,13 +125,18 @@ async fn graph_response(
92125
}))
93126
}
94127

128+
fn artifact_ids_for_range(ctxt: &SiteCtxt, start: Bound, end: Bound) -> Vec<ArtifactId> {
129+
let range = ctxt.data_range(start..=end);
130+
range.into_iter().map(|c| c.into()).collect()
131+
}
132+
95133
/// Creates a summary "benchmark" that averages the results of all other
96134
/// test cases per profile type
97135
fn create_summary(
98136
ctxt: &SiteCtxt,
99137
interpolated_responses: &[SeriesResponse<Vec<((ArtifactId, Option<f64>), IsInterpolated)>>],
100138
graph_kind: GraphKind,
101-
) -> ServerResult<HashMap<Profile, HashMap<String, graph::Series>>> {
139+
) -> ServerResult<HashMap<Profile, HashMap<String, graphs::Series>>> {
102140
let mut baselines = HashMap::new();
103141
let mut summary_benchmark = HashMap::new();
104142
let summary_query_cases = iproduct!(
@@ -152,8 +190,8 @@ fn create_summary(
152190
fn graph_series(
153191
points: impl Iterator<Item = ((ArtifactId, Option<f64>), IsInterpolated)>,
154192
kind: GraphKind,
155-
) -> graph::Series {
156-
let mut graph_series = graph::Series {
193+
) -> graphs::Series {
194+
let mut graph_series = graphs::Series {
157195
points: Vec::new(),
158196
interpolated_indices: Default::default(),
159197
};

site/src/server.rs

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use serde::de::DeserializeOwned;
1818
use serde::Serialize;
1919

2020
pub use crate::api::{
21-
self, bootstrap, comparison, dashboard, github, graph, info, self_profile, self_profile_raw,
21+
self, bootstrap, comparison, dashboard, github, graphs, info, self_profile, self_profile_raw,
2222
status, triage, ServerResult,
2323
};
2424
use crate::db::{self, ArtifactId};
@@ -131,6 +131,39 @@ impl Server {
131131
.unwrap())
132132
}
133133

134+
async fn handle_fallible_get_async<F, R, S, E>(
135+
&self,
136+
req: &Request,
137+
handler: F,
138+
) -> Result<Response, ServerError>
139+
where
140+
F: FnOnce(Arc<SiteCtxt>) -> R,
141+
R: std::future::Future<Output = Result<S, E>> + Send,
142+
S: Serialize,
143+
E: Into<Vec<u8>>,
144+
{
145+
check_http_method!(*req.method(), http::Method::GET);
146+
let ctxt = self.ctxt.clone();
147+
let ctxt = ctxt.read().as_ref().unwrap().clone();
148+
let result = handler(ctxt).await;
149+
let response = match result {
150+
Ok(result) => {
151+
let response = http::Response::builder()
152+
.header_typed(ContentType::json())
153+
.header_typed(CacheControl::new().with_no_cache().with_no_store());
154+
let body = serde_json::to_vec(&result).unwrap();
155+
response.body(hyper::Body::from(body)).unwrap()
156+
}
157+
Err(err) => http::Response::builder()
158+
.status(StatusCode::INTERNAL_SERVER_ERROR)
159+
.header_typed(ContentType::text_utf8())
160+
.header_typed(CacheControl::new().with_no_cache().with_no_store())
161+
.body(hyper::Body::from(err.into()))
162+
.unwrap(),
163+
};
164+
Ok(response)
165+
}
166+
134167
fn check_auth(&self, req: &http::request::Parts) -> bool {
135168
if let Some(auth) = req
136169
.headers
@@ -317,6 +350,18 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
317350
crate::comparison::handle_triage(input, &ctxt).await,
318351
));
319352
}
353+
"/perf/graph" => {
354+
let query = check!(parse_query_string(req.uri()));
355+
return server
356+
.handle_fallible_get_async(&req, |c| request_handlers::handle_graph(query, c))
357+
.await;
358+
}
359+
"/perf/graphs" => {
360+
let query = check!(parse_query_string(req.uri()));
361+
return server
362+
.handle_fallible_get_async(&req, |c| request_handlers::handle_graphs(query, c))
363+
.await;
364+
}
320365
"/perf/metrics" => {
321366
return Ok(server.handle_metrics(req).await);
322367
}
@@ -403,23 +448,6 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
403448
"/perf/self-profile-raw" => Ok(to_response(
404449
request_handlers::handle_self_profile_raw(check!(parse_body(&body)), &ctxt).await,
405450
)),
406-
"/perf/graphs" => Ok(
407-
match request_handlers::handle_graphs(check!(parse_body(&body)), &ctxt).await {
408-
Ok(result) => {
409-
let response = http::Response::builder()
410-
.header_typed(ContentType::json())
411-
.header_typed(CacheControl::new().with_no_cache().with_no_store());
412-
let body = serde_json::to_vec(&result).unwrap();
413-
response.body(hyper::Body::from(body)).unwrap()
414-
}
415-
Err(err) => http::Response::builder()
416-
.status(StatusCode::INTERNAL_SERVER_ERROR)
417-
.header_typed(ContentType::text_utf8())
418-
.header_typed(CacheControl::new().with_no_cache().with_no_store())
419-
.body(hyper::Body::from(err))
420-
.unwrap(),
421-
},
422-
),
423451
"/perf/bootstrap" => Ok(
424452
match request_handlers::handle_bootstrap(check!(parse_body(&body)), &ctxt).await {
425453
Ok(result) => {

site/static/index.html

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -387,11 +387,9 @@ <h3>This may take a while!</h3>
387387
document.querySelector("#loading").style.display = 'none';
388388
}
389389

390-
function post_json(path, body) {
391-
return fetch(BASE_URL + path, {
392-
method: "POST",
393-
body: JSON.stringify(body),
394-
}).then(r => r.json());
390+
function get_json(path, query) {
391+
let q = Object.entries(query).map(e => `${e[0]}=${e[1]}`).join("&");
392+
return fetch(BASE_URL + path + "?" + q).then(r => r.json());
395393
}
396394

397395
function prepData(data) {
@@ -439,7 +437,7 @@ <h3>This may take a while!</h3>
439437
stat: "instructions:u",
440438
kind: "raw",
441439
}, state);
442-
post_json("/graphs", values).then(prepData).then(data =>
440+
get_json("/graphs", values).then(prepData).then(data =>
443441
renderPlots(data, values));
444442
});
445443
</script>

0 commit comments

Comments
 (0)