Skip to content

Commit 3de6c20

Browse files
authored
Merge pull request #9756 from Turbo87/num_no_build
Fix build metadata race condition
2 parents 622c786 + 3854751 commit 3de6c20

File tree

5 files changed

+21
-23
lines changed

5 files changed

+21
-23
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
alter table versions
2+
drop constraint versions_crate_id_num_no_build_uindex;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
run_in_transaction = false
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
create unique index concurrently if not exists versions_crate_id_num_no_build_uindex
2+
on versions (crate_id, num_no_build);

src/controllers/krate/publish.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
359359
let hex_cksum: String = Sha256::digest(&tarball_bytes).encode_hex();
360360

361361
// Persist the new version of this crate
362-
let version = NewVersion::builder(krate.id, &version_string)
362+
let new_version = NewVersion::builder(krate.id, &version_string)
363363
.features(serde_json::to_value(&features)?)
364364
.maybe_license(license.as_deref())
365365
// Downcast is okay because the file length must be less than the max upload size
@@ -371,8 +371,18 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
371371
.maybe_rust_version(rust_version.as_deref())
372372
.has_lib(tarball_info.manifest.lib.is_some())
373373
.bin_names(bin_names.as_slice())
374-
.build()
375-
.save(conn, &verified_email_address)?;
374+
.build();
375+
376+
let version = match new_version.save(conn, &verified_email_address) {
377+
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::UniqueViolation, _)) => {
378+
return Err(bad_request(format_args!(
379+
"crate version `{}` is already uploaded",
380+
new_version.num_no_build
381+
)));
382+
},
383+
Err(error) => return Err(error.into()),
384+
Ok(version) => version,
385+
};
376386

377387
insert_version_owner_action(
378388
conn,

src/models/version.rs

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@ use crates_io_index::features::FeaturesMap;
66
use diesel::prelude::*;
77
use serde::Deserialize;
88

9-
use crate::util::errors::{bad_request, AppResult};
10-
119
use crate::models::{Crate, Dependency, User};
1210
use crate::schema::*;
13-
use crate::sql::split_part;
1411
use crate::util::diesel::Conn;
1512

1613
// Queryable has a custom implementation below
@@ -86,7 +83,7 @@ pub struct NewVersion<'a> {
8683
#[builder(start_fn)]
8784
num: &'a str,
8885
#[builder(default = strip_build_metadata(num))]
89-
num_no_build: &'a str,
86+
pub num_no_build: &'a str,
9087
created_at: Option<&'a NaiveDateTime>,
9188
yanked: Option<bool>,
9289
#[builder(default = serde_json::Value::Object(Default::default()))]
@@ -103,24 +100,10 @@ pub struct NewVersion<'a> {
103100
}
104101

105102
impl NewVersion<'_> {
106-
pub fn save(&self, conn: &mut impl Conn, published_by_email: &str) -> AppResult<Version> {
107-
use diesel::dsl::exists;
108-
use diesel::{insert_into, select};
103+
pub fn save(&self, conn: &mut impl Conn, published_by_email: &str) -> QueryResult<Version> {
104+
use diesel::insert_into;
109105

110106
conn.transaction(|conn| {
111-
let num_no_build = strip_build_metadata(self.num);
112-
113-
let already_uploaded = versions::table
114-
.filter(versions::crate_id.eq(self.crate_id))
115-
.filter(split_part(versions::num, "+", 1).eq(num_no_build));
116-
117-
if select(exists(already_uploaded)).get_result(conn)? {
118-
return Err(bad_request(format_args!(
119-
"crate version `{}` is already uploaded",
120-
num_no_build
121-
)));
122-
}
123-
124107
let version: Version = insert_into(versions::table).values(self).get_result(conn)?;
125108

126109
insert_into(versions_published_by::table)

0 commit comments

Comments
 (0)