@@ -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,16 +385,40 @@ 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)
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+ let published_default_version = DefaultVersion {
396+ id : version. id ,
397+ num : semver,
398+ yanked : false ,
399+ } ;
400+
401+ // Upsert the `default_value` determined by the existing `default_value` and the
402+ // published version. Note that this could potentially write an outdated version
403+ // (although this should not happen regularly), as we might be comparing to an
404+ // outdated value.
405+ //
406+ // Compared to only using a background job, this prevents us from getting into a
407+ // situation where a crate exists in the `crates` table but doesn't have a default
408+ // version in the `default_versions` table.
409+ let default_version = if let Some ( existing_default_version) = existing_default_version {
410+ std:: cmp:: max ( existing_default_version, published_default_version)
411+ } else {
412+ published_default_version
413+ } ;
414+ diesel:: insert_into ( default_versions:: table)
393415 . values ( (
394416 default_versions:: crate_id. eq ( krate. id ) ,
395- default_versions:: version_id. eq ( version . id ) ,
417+ default_versions:: version_id. eq ( default_version . id ) ,
396418 ) )
397- . on_conflict_do_nothing ( )
419+ . on_conflict ( default_versions:: crate_id)
420+ . do_update ( )
421+ . set ( default_versions:: version_id. eq ( default_version. id ) )
398422 . execute ( conn) ?;
399423
400424 // Update all keywords for this crate
@@ -446,11 +470,9 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
446470
447471 SendPublishNotificationsJob :: new ( version. id ) . enqueue ( conn) ?;
448472
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- }
473+ // Update the default version asynchronously in a background job
474+ // to ensure correctness and eventual consistency.
475+ UpdateDefaultVersion :: new ( krate. id ) . enqueue ( conn) ?;
454476
455477 // Experiment: check new crates for potential typosquatting.
456478 if existing_crate. is_none ( ) {
0 commit comments