@@ -23,8 +23,8 @@ use tokio::runtime::Handle;
2323use url:: Url ;
2424
2525use crate :: models:: {
26- insert_version_owner_action , Category , Crate , DependencyKind , Keyword , NewCrate , NewVersion ,
27- Rights , VersionAction ,
26+ default_versions :: Version as DefaultVersion , insert_version_owner_action , Category , Crate ,
27+ DependencyKind , Keyword , NewCrate , NewVersion , Rights , VersionAction ,
2828} ;
2929
3030use crate :: licenses:: parse_license_expr;
@@ -64,7 +64,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
6464
6565 Crate :: validate_crate_name ( "crate" , & metadata. name ) . map_err ( bad_request) ?;
6666
67- let version = match semver:: Version :: parse ( & metadata. vers ) {
67+ let semver = match semver:: Version :: parse ( & metadata. vers ) {
6868 Ok ( parsed) => parsed,
6969 Err ( _) => {
7070 return Err ( bad_request ( format_args ! (
@@ -75,7 +75,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
7575 } ;
7676
7777 // Convert the version back to a string to deal with any inconsistencies
78- let version_string = version . to_string ( ) ;
78+ let version_string = semver . to_string ( ) ;
7979
8080 let request_log = req. request_log ( ) ;
8181 request_log. add ( "crate_name" , & * metadata. name ) ;
@@ -385,17 +385,43 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
385385 // Link this new version to all dependencies
386386 add_dependencies ( conn, & deps, version. id ) ?;
387387
388- // Insert the default version if it doesn't already exist. Compared
389- // to only using a background job, this prevents us from getting
390- // into a situation where a crate exists in the `crates` table but
391- // doesn't have a default version in the `default_versions` table.
392- let inserted_default_versions = diesel:: insert_into ( default_versions:: table)
393- . values ( (
394- default_versions:: crate_id. eq ( krate. id ) ,
395- default_versions:: version_id. eq ( version. id ) ,
396- ) )
397- . on_conflict_do_nothing ( )
398- . execute ( conn) ?;
388+ let existing_default_version = default_versions:: table
389+ . inner_join ( versions:: table)
390+ . filter ( default_versions:: crate_id. eq ( krate. id ) )
391+ . select ( DefaultVersion :: as_select ( ) )
392+ . first ( conn)
393+ . optional ( ) ?;
394+
395+ // Upsert the `default_value` determined by the existing `default_value` and the
396+ // published version. Note that this could potentially write an outdated version
397+ // (although this should not happen regularly), as we might be comparing to an
398+ // outdated value.
399+ //
400+ // Compared to only using a background job, this prevents us from getting into a
401+ // situation where a crate exists in the `crates` table but doesn't have a default
402+ // version in the `default_versions` table.
403+ // let default_version = std::cmp::max(existing_default_version, Some(published_default_version))
404+ if let Some ( existing_default_version) = existing_default_version {
405+ let published_default_version = DefaultVersion {
406+ id : version. id ,
407+ num : semver,
408+ yanked : false ,
409+ } ;
410+
411+ if existing_default_version < published_default_version {
412+ diesel:: update ( default_versions:: table)
413+ . filter ( default_versions:: crate_id. eq ( krate. id ) )
414+ . set ( default_versions:: version_id. eq ( version. id ) )
415+ . execute ( conn) ?;
416+ }
417+ } else {
418+ diesel:: insert_into ( default_versions:: table)
419+ . values ( (
420+ default_versions:: crate_id. eq ( krate. id ) ,
421+ default_versions:: version_id. eq ( version. id ) ,
422+ ) )
423+ . execute ( conn) ?;
424+ }
399425
400426 // Update all keywords for this crate
401427 Keyword :: update_crate ( conn, & krate, & keywords) ?;
@@ -446,11 +472,9 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
446472
447473 SendPublishNotificationsJob :: new ( version. id ) . enqueue ( conn) ?;
448474
449- // If this is a new version for an existing crate it is sufficient
450- // to update the default version asynchronously in a background job.
451- if inserted_default_versions == 0 {
452- UpdateDefaultVersion :: new ( krate. id ) . enqueue ( conn) ?;
453- }
475+ // Update the default version asynchronously in a background job
476+ // to ensure correctness and eventual consistency.
477+ UpdateDefaultVersion :: new ( krate. id ) . enqueue ( conn) ?;
454478
455479 // Experiment: check new crates for potential typosquatting.
456480 if existing_crate. is_none ( ) {
0 commit comments