Skip to content

Commit f532e24

Browse files
authored
controllers/summary: Use tokio::try_join!() to perform database queries in parallel (#10082)
1 parent 8e88f59 commit f532e24

File tree

2 files changed

+113
-91
lines changed

2 files changed

+113
-91
lines changed

src/controllers/summary.rs

Lines changed: 110 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -9,85 +9,98 @@ use axum_extra::json;
99
use axum_extra::response::ErasedJson;
1010
use diesel::prelude::*;
1111
use diesel_async::{AsyncPgConnection, RunQueryDsl};
12+
use futures_util::FutureExt;
13+
use std::future::Future;
1214

1315
/// Handles the `GET /summary` route.
1416
pub async fn summary(state: AppState) -> AppResult<ErasedJson> {
1517
let mut conn = state.db_read().await?;
1618

17-
let popular_categories = Category::toplevel(&mut conn, "crates", 10, 0)
18-
.await?
19-
.into_iter()
20-
.map(Category::into)
21-
.collect::<Vec<EncodableCategory>>();
22-
23-
let num_crates: i64 = crates::table.count().get_result(&mut conn).await?;
24-
let num_downloads: i64 = metadata::table
25-
.select(metadata::total_downloads)
26-
.get_result(&mut conn)
27-
.await?;
28-
2919
let config = &state.config;
3020

31-
let new_crates = crates::table
32-
.inner_join(crate_downloads::table)
33-
.left_join(recent_crate_downloads::table)
34-
.left_join(default_versions::table)
35-
.left_join(versions::table.on(default_versions::version_id.eq(versions::id)))
36-
.order(crates::created_at.desc())
37-
.select(Record::as_select())
38-
.limit(10)
39-
.load(&mut conn)
40-
.await?;
41-
let just_updated = crates::table
42-
.inner_join(crate_downloads::table)
43-
.left_join(recent_crate_downloads::table)
44-
.left_join(default_versions::table)
45-
.left_join(versions::table.on(default_versions::version_id.eq(versions::id)))
46-
.filter(crates::updated_at.ne(crates::created_at))
47-
.order(crates::updated_at.desc())
48-
.select(Record::as_select())
49-
.limit(10)
50-
.load(&mut conn)
51-
.await?;
21+
let (
22+
num_crates,
23+
num_downloads,
24+
new_crates,
25+
just_updated,
26+
most_downloaded,
27+
most_recently_downloaded,
28+
popular_categories,
29+
popular_keywords,
30+
) = tokio::try_join!(
31+
crates::table.count().get_result::<i64>(&mut conn).boxed(),
32+
metadata::table
33+
.select(metadata::total_downloads)
34+
.get_result::<i64>(&mut conn)
35+
.boxed(),
36+
crates::table
37+
.inner_join(crate_downloads::table)
38+
.left_join(recent_crate_downloads::table)
39+
.left_join(default_versions::table)
40+
.left_join(versions::table.on(default_versions::version_id.eq(versions::id)))
41+
.order(crates::created_at.desc())
42+
.select(Record::as_select())
43+
.limit(10)
44+
.load(&mut conn)
45+
.boxed(),
46+
crates::table
47+
.inner_join(crate_downloads::table)
48+
.left_join(recent_crate_downloads::table)
49+
.left_join(default_versions::table)
50+
.left_join(versions::table.on(default_versions::version_id.eq(versions::id)))
51+
.filter(crates::updated_at.ne(crates::created_at))
52+
.order(crates::updated_at.desc())
53+
.select(Record::as_select())
54+
.limit(10)
55+
.load(&mut conn)
56+
.boxed(),
57+
crates::table
58+
.inner_join(crate_downloads::table)
59+
.left_join(recent_crate_downloads::table)
60+
.left_join(default_versions::table)
61+
.left_join(versions::table.on(default_versions::version_id.eq(versions::id)))
62+
.filter(crates::name.ne_all(&config.excluded_crate_names))
63+
.then_order_by(crate_downloads::downloads.desc())
64+
.select(Record::as_select())
65+
.limit(10)
66+
.load(&mut conn)
67+
.boxed(),
68+
crates::table
69+
.inner_join(crate_downloads::table)
70+
.inner_join(recent_crate_downloads::table)
71+
.left_join(default_versions::table)
72+
.left_join(versions::table.on(default_versions::version_id.eq(versions::id)))
73+
.filter(crates::name.ne_all(&config.excluded_crate_names))
74+
.then_order_by(recent_crate_downloads::downloads.desc())
75+
.select(Record::as_select())
76+
.limit(10)
77+
.load(&mut conn)
78+
.boxed(),
79+
Category::toplevel(&mut conn, "crates", 10, 0),
80+
keywords::table
81+
.order(keywords::crates_cnt.desc())
82+
.limit(10)
83+
.load(&mut conn)
84+
.boxed(),
85+
)?;
5286

53-
let most_downloaded = crates::table
54-
.inner_join(crate_downloads::table)
55-
.left_join(recent_crate_downloads::table)
56-
.left_join(default_versions::table)
57-
.left_join(versions::table.on(default_versions::version_id.eq(versions::id)))
58-
.filter(crates::name.ne_all(&config.excluded_crate_names))
59-
.then_order_by(crate_downloads::downloads.desc())
60-
.select(Record::as_select())
61-
.limit(10)
62-
.load(&mut conn)
63-
.await?;
87+
let (new_crates, most_downloaded, most_recently_downloaded, just_updated) = tokio::try_join!(
88+
encode_crates(&mut conn, new_crates),
89+
encode_crates(&mut conn, most_downloaded),
90+
encode_crates(&mut conn, most_recently_downloaded),
91+
encode_crates(&mut conn, just_updated),
92+
)?;
6493

65-
let most_recently_downloaded = crates::table
66-
.inner_join(crate_downloads::table)
67-
.inner_join(recent_crate_downloads::table)
68-
.left_join(default_versions::table)
69-
.left_join(versions::table.on(default_versions::version_id.eq(versions::id)))
70-
.filter(crates::name.ne_all(&config.excluded_crate_names))
71-
.then_order_by(recent_crate_downloads::downloads.desc())
72-
.select(Record::as_select())
73-
.limit(10)
74-
.load(&mut conn)
75-
.await?;
94+
let popular_categories = popular_categories
95+
.into_iter()
96+
.map(Category::into)
97+
.collect::<Vec<EncodableCategory>>();
7698

77-
let popular_keywords = keywords::table
78-
.order(keywords::crates_cnt.desc())
79-
.limit(10)
80-
.load(&mut conn)
81-
.await?
99+
let popular_keywords = popular_keywords
82100
.into_iter()
83101
.map(Keyword::into)
84102
.collect::<Vec<EncodableKeyword>>();
85103

86-
let new_crates = encode_crates(&mut conn, new_crates).await?;
87-
let most_downloaded = encode_crates(&mut conn, most_downloaded).await?;
88-
let most_recently_downloaded = encode_crates(&mut conn, most_recently_downloaded).await?;
89-
let just_updated = encode_crates(&mut conn, just_updated).await?;
90-
91104
Ok(json!({
92105
"num_downloads": num_downloads,
93106
"num_crates": num_crates,
@@ -115,32 +128,41 @@ struct Record {
115128
yanked: Option<bool>,
116129
}
117130

118-
async fn encode_crates(
131+
fn encode_crates(
119132
conn: &mut AsyncPgConnection,
120133
data: Vec<Record>,
121-
) -> AppResult<Vec<EncodableCrate>> {
122-
let krates = data.iter().map(|record| &record.krate).collect::<Vec<_>>();
123-
let versions: Vec<Version> = Version::belonging_to(&krates)
134+
) -> impl Future<Output = AppResult<Vec<EncodableCrate>>> {
135+
let crate_ids = data
136+
.iter()
137+
.map(|record| record.krate.id)
138+
.collect::<Vec<_>>();
139+
140+
let future = versions::table
141+
.filter(versions::crate_id.eq_any(crate_ids))
124142
.filter(versions::yanked.eq(false))
125143
.select(Version::as_select())
126-
.load(conn)
127-
.await?;
144+
.load(conn);
128145

129-
versions
130-
.grouped_by(&krates)
131-
.into_iter()
132-
.map(TopVersions::from_versions)
133-
.zip(data)
134-
.map(|(top_versions, record)| {
135-
Ok(EncodableCrate::from_minimal(
136-
record.krate,
137-
record.default_version.as_deref(),
138-
record.yanked,
139-
Some(&top_versions),
140-
false,
141-
record.total_downloads,
142-
record.recent_downloads,
143-
))
144-
})
145-
.collect()
146+
async move {
147+
let versions: Vec<Version> = future.await?;
148+
149+
let krates = data.iter().map(|record| &record.krate).collect::<Vec<_>>();
150+
versions
151+
.grouped_by(&krates)
152+
.into_iter()
153+
.map(TopVersions::from_versions)
154+
.zip(data)
155+
.map(|(top_versions, record)| {
156+
Ok(EncodableCrate::from_minimal(
157+
record.krate,
158+
record.default_version.as_deref(),
159+
record.yanked,
160+
Some(&top_versions),
161+
false,
162+
record.total_downloads,
163+
record.recent_downloads,
164+
))
165+
})
166+
.collect()
167+
}
146168
}

src/models/category.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use diesel::{
55
};
66
use diesel_async::scoped_futures::ScopedFutureExt;
77
use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl};
8+
use std::future::Future;
89

910
use crate::models::Crate;
1011
use crate::schema::*;
@@ -97,12 +98,12 @@ impl Category {
9798
.await
9899
}
99100

100-
pub async fn toplevel(
101+
pub fn toplevel(
101102
conn: &mut AsyncPgConnection,
102103
sort: &str,
103104
limit: i64,
104105
offset: i64,
105-
) -> QueryResult<Vec<Category>> {
106+
) -> impl Future<Output = QueryResult<Vec<Category>>> {
106107
use diesel::sql_types::Int8;
107108

108109
let sort_sql = match sort {
@@ -116,7 +117,6 @@ impl Category {
116117
.bind::<Int8, _>(limit)
117118
.bind::<Int8, _>(offset)
118119
.load(conn)
119-
.await
120120
}
121121

122122
pub async fn subcategories(&self, conn: &mut AsyncPgConnection) -> QueryResult<Vec<Category>> {

0 commit comments

Comments
 (0)