Skip to content

Commit 82db663

Browse files
committed
admin/render_readmes: Remove spawn_blocking() call
1 parent 3bfe1cf commit 82db663

File tree

3 files changed

+59
-43
lines changed

3 files changed

+59
-43
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ tracing-subscriber = { version = "=0.3.18", features = ["env-filter", "json"] }
122122
typomania = { version = "=0.1.2", default-features = false }
123123
url = "=2.5.4"
124124
unicode-xid = "=0.2.6"
125+
async-compression = { version = "=0.4.18", default-features = false, features = ["gzip", "tokio"] }
126+
krata-tokio-tar = "=0.4.2"
127+
tokio-util = { version = "=0.7.12", features = ["compat"] }
125128

126129
[dev-dependencies]
127130
bytes = "=1.8.0"

src/bin/crates-admin/render_readmes.rs

Lines changed: 52 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,23 @@ use crates_io::{
44
models::Version,
55
schema::{crates, readme_renderings, versions},
66
};
7-
use std::path::PathBuf;
8-
use std::{io::Read, path::Path, sync::Arc};
7+
use futures_util::{StreamExt, TryStreamExt};
8+
use std::path::{Path, PathBuf};
9+
use std::{future, sync::Arc};
10+
use tokio::io::{AsyncRead, AsyncReadExt};
11+
use tokio_util::compat::FuturesAsyncReadCompatExt;
912

13+
use async_compression::tokio::bufread::GzipDecoder;
1014
use chrono::{NaiveDateTime, Utc};
1115
use crates_io::storage::Storage;
12-
use crates_io::tasks::spawn_blocking;
1316
use crates_io_markdown::text_to_html;
1417
use crates_io_tarball::{Manifest, StringOrBool};
1518
use diesel::prelude::*;
1619
use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
1720
use diesel_async::{AsyncPgConnection, RunQueryDsl};
18-
use flate2::read::GzDecoder;
1921
use reqwest::{header, Client};
2022
use std::str::FromStr;
21-
use tar::{self, Archive};
23+
use tokio_tar::{self, Archive};
2224

2325
const USER_AGENT: &str = "crates-admin";
2426

@@ -168,22 +170,25 @@ async fn get_readme(
168170
));
169171
}
170172

171-
let body = response.bytes().await?;
172-
173-
spawn_blocking(move || {
174-
let reader = GzDecoder::new(&*body);
175-
let archive = Archive::new(reader);
176-
render_pkg_readme(archive, &pkg_name)
177-
})
178-
.await?
173+
let reader = response
174+
.bytes_stream()
175+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
176+
.into_async_read();
177+
let reader = GzipDecoder::new(reader.compat());
178+
let archive = Archive::new(reader);
179+
render_pkg_readme(archive, &pkg_name).await
179180
}
180181

181-
fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> anyhow::Result<String> {
182+
async fn render_pkg_readme<R: AsyncRead + Unpin>(
183+
mut archive: Archive<R>,
184+
pkg_name: &str,
185+
) -> anyhow::Result<String> {
182186
let mut entries = archive.entries().context("Invalid tar archive entries")?;
183187

184188
let manifest: Manifest = {
185189
let path = format!("{pkg_name}/Cargo.toml");
186190
let contents = find_file_by_path(&mut entries, Path::new(&path))
191+
.await
187192
.context("Failed to read Cargo.toml file")?;
188193

189194
Manifest::from_str(&contents).context("Failed to parse manifest file")?
@@ -207,6 +212,7 @@ fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> anyhow
207212

208213
let path = Path::new(pkg_name).join(&readme_path);
209214
let contents = find_file_by_path(&mut entries, Path::new(&path))
215+
.await
210216
.with_context(|| format!("Failed to read {} file", readme_path.display()))?;
211217

212218
// pkg_path_in_vcs Unsupported from admin::render_readmes. See #4095
@@ -226,20 +232,20 @@ fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> anyhow
226232
}
227233

228234
/// Search an entry by its path in a Tar archive.
229-
fn find_file_by_path<R: Read>(
230-
entries: &mut tar::Entries<'_, R>,
235+
async fn find_file_by_path<R: AsyncRead + Unpin>(
236+
entries: &mut tokio_tar::Entries<R>,
231237
path: &Path,
232238
) -> anyhow::Result<String> {
233239
let mut file = entries
234-
.filter_map(|entry| entry.ok())
235-
.find(|file| match file.path() {
236-
Ok(p) => p == path,
237-
Err(_) => false,
238-
})
240+
.filter_map(|entry| future::ready(entry.ok()))
241+
.filter(|entry| future::ready(entry.path().is_ok_and(|p| p == path)))
242+
.next()
243+
.await
239244
.ok_or_else(|| anyhow!("Failed to find tarball entry: {}", path.display()))?;
240245

241246
let mut contents = String::new();
242247
file.read_to_string(&mut contents)
248+
.await
243249
.context("Failed to read file contents")?;
244250

245251
Ok(contents)
@@ -252,8 +258,8 @@ pub mod tests {
252258

253259
use super::render_pkg_readme;
254260

255-
#[test]
256-
fn test_render_pkg_readme() {
261+
#[tokio::test]
262+
async fn test_render_pkg_readme() {
257263
let serialized_archive = TarballBuilder::new()
258264
.add_file(
259265
"foo-0.0.1/Cargo.toml",
@@ -267,13 +273,14 @@ readme = "README.md"
267273
.add_file("foo-0.0.1/README.md", b"readme")
268274
.build_unzipped();
269275

270-
let result =
271-
render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
276+
let result = render_pkg_readme(tokio_tar::Archive::new(&*serialized_archive), "foo-0.0.1")
277+
.await
278+
.unwrap();
272279
assert!(result.contains("readme"))
273280
}
274281

275-
#[test]
276-
fn test_render_pkg_no_readme() {
282+
#[tokio::test]
283+
async fn test_render_pkg_no_readme() {
277284
let serialized_archive = TarballBuilder::new()
278285
.add_file(
279286
"foo-0.0.1/Cargo.toml",
@@ -283,14 +290,13 @@ readme = "README.md"
283290
)
284291
.build_unzipped();
285292

286-
assert_err!(render_pkg_readme(
287-
tar::Archive::new(&*serialized_archive),
288-
"foo-0.0.1"
289-
));
293+
assert_err!(
294+
render_pkg_readme(tokio_tar::Archive::new(&*serialized_archive), "foo-0.0.1").await
295+
);
290296
}
291297

292-
#[test]
293-
fn test_render_pkg_implicit_readme() {
298+
#[tokio::test]
299+
async fn test_render_pkg_implicit_readme() {
294300
let serialized_archive = TarballBuilder::new()
295301
.add_file(
296302
"foo-0.0.1/Cargo.toml",
@@ -303,13 +309,14 @@ version = "0.0.1"
303309
.add_file("foo-0.0.1/README.md", b"readme")
304310
.build_unzipped();
305311

306-
let result =
307-
render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
312+
let result = render_pkg_readme(tokio_tar::Archive::new(&*serialized_archive), "foo-0.0.1")
313+
.await
314+
.unwrap();
308315
assert!(result.contains("readme"))
309316
}
310317

311-
#[test]
312-
fn test_render_pkg_readme_w_link() {
318+
#[tokio::test]
319+
async fn test_render_pkg_readme_w_link() {
313320
let serialized_archive = TarballBuilder::new()
314321
.add_file(
315322
"foo-0.0.1/Cargo.toml",
@@ -324,13 +331,14 @@ repository = "https://github.com/foo/foo"
324331
.add_file("foo-0.0.1/README.md", b"readme [link](./Other.md)")
325332
.build_unzipped();
326333

327-
let result =
328-
render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
334+
let result = render_pkg_readme(tokio_tar::Archive::new(&*serialized_archive), "foo-0.0.1")
335+
.await
336+
.unwrap();
329337
assert!(result.contains("\"https://github.com/foo/foo/blob/HEAD/./Other.md\""))
330338
}
331339

332-
#[test]
333-
fn test_render_pkg_readme_not_at_root() {
340+
#[tokio::test]
341+
async fn test_render_pkg_readme_not_at_root() {
334342
let serialized_archive = TarballBuilder::new()
335343
.add_file(
336344
"foo-0.0.1/Cargo.toml",
@@ -348,8 +356,9 @@ repository = "https://github.com/foo/foo"
348356
)
349357
.build_unzipped();
350358

351-
let result =
352-
render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
359+
let result = render_pkg_readme(tokio_tar::Archive::new(&*serialized_archive), "foo-0.0.1")
360+
.await
361+
.unwrap();
353362
assert!(result.contains("docs/readme"));
354363
assert!(result.contains("\"https://github.com/foo/foo/blob/HEAD/docs/./Other.md\""))
355364
}

0 commit comments

Comments
 (0)