Skip to content

Add endpoint for single graph #1054

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

Merged
merged 2 commits into from
Oct 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions site/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,28 @@ pub mod dashboard {
}

pub mod graph {
use super::graphs::{GraphKind, Series};
use collector::Bound;
use serde::{Deserialize, Serialize};

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Request {
pub benchmark: String,
pub profile: String,
pub scenario: String,
pub metric: String,
pub start: Bound,
pub end: Bound,
pub kind: GraphKind,
}

#[derive(Debug, PartialEq, Clone, Serialize)]
pub struct Response {
pub series: Series,
}
}

pub mod graphs {
use collector::Bound;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
Expand Down
2 changes: 1 addition & 1 deletion site/src/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub struct SiteCtxt {
/// Site configuration
pub config: Config,
/// Cached site landing page
pub landing_page: ArcSwap<Option<Arc<crate::api::graph::Response>>>,
pub landing_page: ArcSwap<Option<Arc<crate::api::graphs::Response>>>,
/// Index of various common queries
pub index: ArcSwap<crate::db::Index>,
/// Database connection pool
Expand Down
2 changes: 1 addition & 1 deletion site/src/request_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod status_page;
pub use bootstrap::handle_bootstrap;
pub use dashboard::handle_dashboard;
pub use github::handle_github;
pub use graph::handle_graphs;
pub use graph::{handle_graph, handle_graphs};
pub use next_commit::handle_next_commit;
pub use self_profile::{
handle_self_profile, handle_self_profile_processed_download, handle_self_profile_raw,
Expand Down
86 changes: 62 additions & 24 deletions site/src/request_handlers/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,34 @@ use collector::Bound;
use std::collections::HashMap;
use std::sync::Arc;

use crate::api::graph::GraphKind;
use crate::api::{graph, ServerResult};
use crate::api::graphs::GraphKind;
use crate::api::{graph, graphs, ServerResult};
use crate::db::{self, ArtifactId, Benchmark, Profile, Scenario};
use crate::interpolate::IsInterpolated;
use crate::load::SiteCtxt;
use crate::selector::{Query, Selector, SeriesResponse, Tag};

pub async fn handle_graph(
request: graph::Request,
ctxt: Arc<SiteCtxt>,
) -> ServerResult<graph::Response> {
log::info!("handle_graph({:?})", request);

create_graph(request, ctxt).await
}

pub async fn handle_graphs(
body: graph::Request,
ctxt: &SiteCtxt,
) -> ServerResult<Arc<graph::Response>> {
log::info!("handle_graph({:?})", body);
request: graphs::Request,
ctxt: Arc<SiteCtxt>,
) -> ServerResult<Arc<graphs::Response>> {
log::info!("handle_graphs({:?})", request);

let is_default_query = body
== graph::Request {
let is_default_query = request
== graphs::Request {
start: Bound::None,
end: Bound::None,
stat: String::from("instructions:u"),
kind: graph::GraphKind::Raw,
kind: graphs::GraphKind::Raw,
};

if is_default_query {
Expand All @@ -30,7 +39,7 @@ pub async fn handle_graphs(
}
}

let resp = graph_response(body, ctxt).await?;
let resp = create_graphs(request, &ctxt).await?;

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

async fn graph_response(
body: graph::Request,
async fn create_graph(
request: graph::Request,
ctxt: Arc<SiteCtxt>,
) -> ServerResult<graph::Response> {
let artifact_ids = artifact_ids_for_range(&ctxt, request.start, request.end);
let mut series_iterator = ctxt
.statistic_series(
Query::new()
.set::<String>(Tag::Benchmark, Selector::One(request.benchmark))
.set::<String>(Tag::Profile, Selector::One(request.profile))
.set::<String>(Tag::Scenario, Selector::One(request.scenario))
.set::<String>(Tag::Metric, Selector::One(request.metric)),
Arc::new(artifact_ids),
)
.await?
.into_iter()
.map(SeriesResponse::interpolate);

let result = series_iterator.next().unwrap();
let graph_series = graph_series(result.series, request.kind);
Ok(graph::Response {
series: graph_series,
})
}

async fn create_graphs(
request: graphs::Request,
ctxt: &SiteCtxt,
) -> ServerResult<Arc<graph::Response>> {
let range = ctxt.data_range(body.start..=body.end);
let commits: Arc<Vec<_>> = Arc::new(range.into_iter().map(|c| c.into()).collect());
) -> ServerResult<Arc<graphs::Response>> {
let artifact_ids = Arc::new(artifact_ids_for_range(ctxt, request.start, request.end));
let mut benchmarks = HashMap::new();

let interpolated_responses: Vec<_> = ctxt
Expand All @@ -53,23 +86,23 @@ async fn graph_response(
.set::<String>(Tag::Benchmark, Selector::All)
.set::<String>(Tag::Profile, Selector::All)
.set::<String>(Tag::Scenario, Selector::All)
.set::<String>(Tag::Metric, Selector::One(body.stat)),
commits.clone(),
.set::<String>(Tag::Metric, Selector::One(request.stat)),
artifact_ids.clone(),
)
.await?
.into_iter()
.map(|sr| sr.interpolate().map(|series| series.collect::<Vec<_>>()))
.collect();

let summary_benchmark = create_summary(ctxt, &interpolated_responses, body.kind)?;
let summary_benchmark = create_summary(ctxt, &interpolated_responses, request.kind)?;

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

for response in interpolated_responses {
let benchmark = response.path.get::<Benchmark>()?.to_string();
let profile = *response.path.get::<Profile>()?;
let scenario = response.path.get::<Scenario>()?.to_string();
let graph_series = graph_series(response.series.into_iter(), body.kind);
let graph_series = graph_series(response.series.into_iter(), request.kind);

benchmarks
.entry(benchmark)
Expand All @@ -79,8 +112,8 @@ async fn graph_response(
.insert(scenario, graph_series);
}

Ok(Arc::new(graph::Response {
commits: Arc::try_unwrap(commits)
Ok(Arc::new(graphs::Response {
commits: Arc::try_unwrap(artifact_ids)
.unwrap()
.into_iter()
.map(|c| match c {
Expand All @@ -92,13 +125,18 @@ async fn graph_response(
}))
}

fn artifact_ids_for_range(ctxt: &SiteCtxt, start: Bound, end: Bound) -> Vec<ArtifactId> {
let range = ctxt.data_range(start..=end);
range.into_iter().map(|c| c.into()).collect()
}

/// Creates a summary "benchmark" that averages the results of all other
/// test cases per profile type
fn create_summary(
ctxt: &SiteCtxt,
interpolated_responses: &[SeriesResponse<Vec<((ArtifactId, Option<f64>), IsInterpolated)>>],
graph_kind: GraphKind,
) -> ServerResult<HashMap<Profile, HashMap<String, graph::Series>>> {
) -> ServerResult<HashMap<Profile, HashMap<String, graphs::Series>>> {
let mut baselines = HashMap::new();
let mut summary_benchmark = HashMap::new();
let summary_query_cases = iproduct!(
Expand Down Expand Up @@ -152,8 +190,8 @@ fn create_summary(
fn graph_series(
points: impl Iterator<Item = ((ArtifactId, Option<f64>), IsInterpolated)>,
kind: GraphKind,
) -> graph::Series {
let mut graph_series = graph::Series {
) -> graphs::Series {
let mut graph_series = graphs::Series {
points: Vec::new(),
interpolated_indices: Default::default(),
};
Expand Down
64 changes: 46 additions & 18 deletions site/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use serde::de::DeserializeOwned;
use serde::Serialize;

pub use crate::api::{
self, bootstrap, comparison, dashboard, github, graph, info, self_profile, self_profile_raw,
self, bootstrap, comparison, dashboard, github, graphs, info, self_profile, self_profile_raw,
status, triage, ServerResult,
};
use crate::db::{self, ArtifactId};
Expand Down Expand Up @@ -131,6 +131,39 @@ impl Server {
.unwrap())
}

async fn handle_fallible_get_async<F, R, S, E>(
&self,
req: &Request,
handler: F,
) -> Result<Response, ServerError>
where
F: FnOnce(Arc<SiteCtxt>) -> R,
R: std::future::Future<Output = Result<S, E>> + Send,
S: Serialize,
E: Into<Vec<u8>>,
{
check_http_method!(*req.method(), http::Method::GET);
let ctxt = self.ctxt.clone();
let ctxt = ctxt.read().as_ref().unwrap().clone();
let result = handler(ctxt).await;
let response = match result {
Ok(result) => {
let response = http::Response::builder()
.header_typed(ContentType::json())
.header_typed(CacheControl::new().with_no_cache().with_no_store());
let body = serde_json::to_vec(&result).unwrap();
response.body(hyper::Body::from(body)).unwrap()
}
Err(err) => http::Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.header_typed(ContentType::text_utf8())
.header_typed(CacheControl::new().with_no_cache().with_no_store())
.body(hyper::Body::from(err.into()))
.unwrap(),
};
Ok(response)
}

fn check_auth(&self, req: &http::request::Parts) -> bool {
if let Some(auth) = req
.headers
Expand Down Expand Up @@ -317,6 +350,18 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
crate::comparison::handle_triage(input, &ctxt).await,
));
}
"/perf/graph" => {
let query = check!(parse_query_string(req.uri()));
return server
.handle_fallible_get_async(&req, |c| request_handlers::handle_graph(query, c))
.await;
}
"/perf/graphs" => {
let query = check!(parse_query_string(req.uri()));
return server
.handle_fallible_get_async(&req, |c| request_handlers::handle_graphs(query, c))
.await;
}
"/perf/metrics" => {
return Ok(server.handle_metrics(req).await);
}
Expand Down Expand Up @@ -403,23 +448,6 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
"/perf/self-profile-raw" => Ok(to_response(
request_handlers::handle_self_profile_raw(check!(parse_body(&body)), &ctxt).await,
)),
"/perf/graphs" => Ok(
match request_handlers::handle_graphs(check!(parse_body(&body)), &ctxt).await {
Ok(result) => {
let response = http::Response::builder()
.header_typed(ContentType::json())
.header_typed(CacheControl::new().with_no_cache().with_no_store());
let body = serde_json::to_vec(&result).unwrap();
response.body(hyper::Body::from(body)).unwrap()
}
Err(err) => http::Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.header_typed(ContentType::text_utf8())
.header_typed(CacheControl::new().with_no_cache().with_no_store())
.body(hyper::Body::from(err))
.unwrap(),
},
),
"/perf/bootstrap" => Ok(
match request_handlers::handle_bootstrap(check!(parse_body(&body)), &ctxt).await {
Ok(result) => {
Expand Down
10 changes: 4 additions & 6 deletions site/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,9 @@ <h3>This may take a while!</h3>
document.querySelector("#loading").style.display = 'none';
}

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

function prepData(data) {
Expand Down Expand Up @@ -439,7 +437,7 @@ <h3>This may take a while!</h3>
stat: "instructions:u",
kind: "raw",
}, state);
post_json("/graphs", values).then(prepData).then(data =>
get_json("/graphs", values).then(prepData).then(data =>
renderPlots(data, values));
});
</script>
Expand Down