Skip to content

Commit d3b25c7

Browse files
committed
index-watcher: handle deletion requests when the crate is only queued
1 parent 97823e2 commit d3b25c7

File tree

5 files changed

+162
-34
lines changed

5 files changed

+162
-34
lines changed

.sqlx/query-4b13fe2c8df2b8b8bf019344313b2bc6442482a604cf90fb6106154f8e69a1c2.json

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

.sqlx/query-f8b389df3451e4b5e6539e9260ba6340edf69c7dba22e667aedd510e868b0f00.json

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

src/build_queue.rs

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,39 @@ impl AsyncBuildQueue {
245245
.await?
246246
.is_some())
247247
}
248+
249+
async fn remove_crate_from_queue(&self, name: &str) -> Result<()> {
250+
let mut conn = self.db.get_async().await?;
251+
sqlx::query!(
252+
"DELETE
253+
FROM queue
254+
WHERE name = $1
255+
",
256+
name
257+
)
258+
.execute(&mut *conn)
259+
.await?;
260+
261+
Ok(())
262+
}
263+
264+
async fn remove_version_from_queue(&self, name: &str, version: &Version) -> Result<()> {
265+
let mut conn = self.db.get_async().await?;
266+
sqlx::query!(
267+
"DELETE
268+
FROM queue
269+
WHERE
270+
name = $1 AND
271+
version = $2
272+
",
273+
name,
274+
version as _,
275+
)
276+
.execute(&mut *conn)
277+
.await?;
278+
279+
Ok(())
280+
}
248281
}
249282

250283
/// Locking functions.
@@ -331,19 +364,22 @@ impl AsyncBuildQueue {
331364
}
332365

333366
self.queue_crate_invalidation(&mut conn, krate).await;
367+
self.remove_crate_from_queue(krate).await?;
334368
continue;
335369
}
336370

337371
if let Some(release) = change.version_deleted() {
372+
let version: Version = release
373+
.version
374+
.parse()
375+
.context("couldn't parse release version as semver")?;
376+
338377
match delete_version(
339378
&mut conn,
340379
&self.storage,
341380
&self.config,
342381
&release.name,
343-
&release
344-
.version
345-
.parse()
346-
.context("couldn't parse release version as semver")?,
382+
&version,
347383
)
348384
.await
349385
.with_context(|| {
@@ -361,6 +397,8 @@ impl AsyncBuildQueue {
361397

362398
self.queue_crate_invalidation(&mut conn, &release.name)
363399
.await;
400+
self.remove_version_from_queue(&release.name, &version)
401+
.await?;
364402
continue;
365403
}
366404

@@ -849,9 +887,8 @@ FROM crates AS c
849887
mod tests {
850888
use super::*;
851889
use crate::db::types::BuildStatus;
852-
use crate::test::{FakeBuild, TestEnvironment, V1, V2};
890+
use crate::test::{FakeBuild, KRATE, TestEnvironment, V1, V2};
853891
use chrono::Utc;
854-
855892
use std::time::Duration;
856893

857894
#[tokio::test(flavor = "multi_thread")]
@@ -1719,4 +1756,48 @@ mod tests {
17191756

17201757
Ok(())
17211758
}
1759+
1760+
#[tokio::test(flavor = "multi_thread")]
1761+
async fn test_delete_version_from_queue() -> Result<()> {
1762+
let env = TestEnvironment::new().await?;
1763+
1764+
let queue = env.async_build_queue();
1765+
assert_eq!(queue.pending_count().await?, 0);
1766+
1767+
queue.add_crate(KRATE, &V1, 0, None).await?;
1768+
queue.add_crate(KRATE, &V2, 0, None).await?;
1769+
1770+
assert_eq!(queue.pending_count().await?, 2);
1771+
queue.remove_version_from_queue(KRATE, &V1).await?;
1772+
1773+
assert_eq!(queue.pending_count().await?, 1);
1774+
1775+
// only v2 remains
1776+
if let [k] = queue.queued_crates().await?.as_slice() {
1777+
assert_eq!(k.name, KRATE);
1778+
assert_eq!(k.version, V2);
1779+
} else {
1780+
panic!("expected only one queued crate");
1781+
}
1782+
1783+
Ok(())
1784+
}
1785+
1786+
#[tokio::test(flavor = "multi_thread")]
1787+
async fn test_delete_crate_from_queue() -> Result<()> {
1788+
let env = TestEnvironment::new().await?;
1789+
1790+
let queue = env.async_build_queue();
1791+
assert_eq!(queue.pending_count().await?, 0);
1792+
1793+
queue.add_crate(KRATE, &V1, 0, None).await?;
1794+
queue.add_crate(KRATE, &V2, 0, None).await?;
1795+
1796+
assert_eq!(queue.pending_count().await?, 2);
1797+
queue.remove_crate_from_queue(KRATE).await?;
1798+
1799+
assert_eq!(queue.pending_count().await?, 0);
1800+
1801+
Ok(())
1802+
}
17221803
}

src/db/delete.rs

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,17 @@ use super::{CrateId, update_latest_version_id};
1515
static LIBRARY_STORAGE_PATHS_TO_DELETE: &[&str] = &["rustdoc", "rustdoc-json", "sources"];
1616
static OTHER_STORAGE_PATHS_TO_DELETE: &[&str] = &["sources"];
1717

18-
#[derive(Debug, thiserror::Error)]
19-
enum CrateDeletionError {
20-
#[error("crate is missing: {0}")]
21-
MissingCrate(String),
22-
}
23-
2418
#[context("error trying to delete crate {name} from database")]
2519
pub async fn delete_crate(
2620
conn: &mut sqlx::PgConnection,
2721
storage: &AsyncStorage,
2822
config: &Config,
2923
name: &str,
3024
) -> Result<()> {
31-
let crate_id = get_id(conn, name).await?;
25+
let Some(crate_id) = get_id(conn, name).await? else {
26+
return Ok(());
27+
};
28+
3229
let is_library = delete_crate_from_database(conn, name, crate_id).await?;
3330
// #899
3431
let paths = if is_library {
@@ -68,7 +65,11 @@ pub async fn delete_version(
6865
name: &str,
6966
version: &Version,
7067
) -> Result<()> {
71-
let is_library = delete_version_from_database(conn, name, version).await?;
68+
let Some(crate_id) = get_id(conn, name).await? else {
69+
return Ok(());
70+
};
71+
72+
let is_library = delete_version_from_database(conn, crate_id, version).await?;
7273
let paths = if is_library {
7374
LIBRARY_STORAGE_PATHS_TO_DELETE
7475
} else {
@@ -105,7 +106,7 @@ pub async fn delete_version(
105106
Ok(())
106107
}
107108

108-
async fn get_id(conn: &mut sqlx::PgConnection, name: &str) -> Result<CrateId> {
109+
async fn get_id(conn: &mut sqlx::PgConnection, name: &str) -> Result<Option<CrateId>> {
109110
Ok(sqlx::query_scalar!(
110111
r#"
111112
SELECT id as "id: CrateId"
@@ -115,8 +116,7 @@ async fn get_id(conn: &mut sqlx::PgConnection, name: &str) -> Result<CrateId> {
115116
name
116117
)
117118
.fetch_optional(&mut *conn)
118-
.await?
119-
.ok_or_else(|| CrateDeletionError::MissingCrate(name.into()))?)
119+
.await?)
120120
}
121121

122122
// metaprogramming!
@@ -131,10 +131,9 @@ const METADATA: &[(&str, &str)] = &[
131131
/// Returns whether this release was a library
132132
async fn delete_version_from_database(
133133
conn: &mut sqlx::PgConnection,
134-
name: &str,
134+
crate_id: CrateId,
135135
version: &Version,
136136
) -> Result<bool> {
137-
let crate_id = get_id(conn, name).await?;
138137
let mut transaction = conn.begin().await?;
139138
for &(table, column) in METADATA {
140139
sqlx::query(
@@ -152,20 +151,6 @@ async fn delete_version_from_database(
152151

153152
update_latest_version_id(&mut transaction, crate_id).await?;
154153

155-
let paths = if is_library {
156-
LIBRARY_STORAGE_PATHS_TO_DELETE
157-
} else {
158-
OTHER_STORAGE_PATHS_TO_DELETE
159-
};
160-
for prefix in paths {
161-
sqlx::query!(
162-
"DELETE FROM files WHERE path LIKE $1;",
163-
format!("{prefix}/{name}/{version}/%"),
164-
)
165-
.execute(&mut *transaction)
166-
.await?;
167-
}
168-
169154
transaction.commit().await?;
170155
Ok(is_library)
171156
}
@@ -224,7 +209,7 @@ mod tests {
224209
use crate::db::ReleaseId;
225210
use crate::registry_api::{CrateOwner, OwnerKind};
226211
use crate::storage::{CompressionAlgorithm, rustdoc_json_path};
227-
use crate::test::{V1, V2, async_wrapper, fake_release_that_failed_before_build};
212+
use crate::test::{KRATE, V1, V2, async_wrapper, fake_release_that_failed_before_build};
228213
use test_case::test_case;
229214

230215
async fn crate_exists(conn: &mut sqlx::PgConnection, name: &str) -> Result<bool> {
@@ -542,4 +527,35 @@ mod tests {
542527
Ok(())
543528
})
544529
}
530+
531+
#[tokio::test(flavor = "multi_thread")]
532+
async fn test_delete_missing_crate_doesnt_error() -> Result<()> {
533+
let env = crate::test::TestEnvironment::new().await?;
534+
535+
let db = env.async_db();
536+
let mut conn = db.async_conn().await;
537+
538+
assert!(!crate_exists(&mut conn, KRATE).await?);
539+
delete_crate(&mut conn, env.async_storage(), env.config(), KRATE).await?;
540+
541+
assert!(!crate_exists(&mut conn, KRATE).await?);
542+
543+
Ok(())
544+
}
545+
546+
#[tokio::test(flavor = "multi_thread")]
547+
async fn test_delete_missing_version_doesnt_error() -> Result<()> {
548+
let env = crate::test::TestEnvironment::new().await?;
549+
550+
let db = env.async_db();
551+
let mut conn = db.async_conn().await;
552+
553+
assert!(!crate_exists(&mut conn, KRATE).await?);
554+
555+
delete_version(&mut conn, env.async_storage(), env.config(), KRATE, &V1).await?;
556+
557+
assert!(!crate_exists(&mut conn, KRATE).await?);
558+
559+
Ok(())
560+
}
545561
}

src/test/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ use tokio::{runtime, task::block_in_place};
4141
use tower::ServiceExt;
4242
use tracing::error;
4343

44+
// testing krate name constants
45+
pub(crate) const KRATE: &str = "krate";
4446
// some versions as constants for tests
4547
pub(crate) const V0_1: Version = Version::new(0, 1, 0);
4648
pub(crate) const V1: Version = Version::new(1, 0, 0);

0 commit comments

Comments
 (0)