@@ -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,46 @@ 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+ if let Some ( existing_default_version) = existing_default_version {
404+ let published_default_version = DefaultVersion {
405+ id : version. id ,
406+ num : semver,
407+ yanked : false ,
408+ } ;
409+
410+ if existing_default_version < published_default_version {
411+ diesel:: update ( default_versions:: table)
412+ . filter ( default_versions:: crate_id. eq ( krate. id ) )
413+ . set ( default_versions:: version_id. eq ( version. id ) )
414+ . execute ( conn) ?;
415+ }
416+
417+ // Update the default version asynchronously in a background job
418+ // to ensure correctness and eventual consistency.
419+ UpdateDefaultVersion :: new ( krate. id ) . enqueue ( conn) ?;
420+ } else {
421+ diesel:: insert_into ( default_versions:: table)
422+ . values ( (
423+ default_versions:: crate_id. eq ( krate. id ) ,
424+ default_versions:: version_id. eq ( version. id ) ,
425+ ) )
426+ . execute ( conn) ?;
427+ }
399428
400429 // Update all keywords for this crate
401430 Keyword :: update_crate ( conn, & krate, & keywords) ?;
@@ -446,12 +475,6 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
446475
447476 SendPublishNotificationsJob :: new ( version. id ) . enqueue ( conn) ?;
448477
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- }
454-
455478 // Experiment: check new crates for potential typosquatting.
456479 if existing_crate. is_none ( ) {
457480 CheckTyposquat :: new ( & krate. name ) . enqueue ( conn) ?;
0 commit comments