@@ -15,8 +15,7 @@ use diesel::dsl::{exists, select};
1515use diesel:: prelude:: * ;
1616use diesel_async:: scoped_futures:: ScopedFutureExt ;
1717use diesel_async:: { AsyncConnection , AsyncPgConnection , RunQueryDsl } ;
18- use futures_util:: TryFutureExt ;
19- use futures_util:: TryStreamExt ;
18+ use futures_util:: { FutureExt , TryFutureExt , TryStreamExt } ;
2019use hex:: ToHex ;
2120use http:: request:: Parts ;
2221use http:: StatusCode ;
@@ -433,10 +432,10 @@ pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult<Json<Go
433432 // Link this new version to all dependencies
434433 add_dependencies ( conn, & deps, version. id ) . await ?;
435434
436- let existing_default_version = default_versions:: table
435+ let existing_default_version: Option < ( DefaultVersion , Option < i32 > ) > = default_versions:: table
437436 . inner_join ( versions:: table)
438437 . filter ( default_versions:: crate_id. eq ( krate. id ) )
439- . select ( DefaultVersion :: as_select ( ) )
438+ . select ( ( DefaultVersion :: as_select ( ) , default_versions :: num_versions ) )
440439 . first ( conn)
441440 . await
442441 . optional ( ) ?;
@@ -450,22 +449,44 @@ pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult<Json<Go
450449 // Compared to only using a background job, this prevents us from getting into a
451450 // situation where a crate exists in the `crates` table but doesn't have a default
452451 // version in the `default_versions` table.
453- if let Some ( existing_default_version) = existing_default_version {
452+ if let Some ( ( existing_default_version, existing_num_versions ) ) = existing_default_version {
454453 let published_default_version = DefaultVersion {
455454 id : version. id ,
456455 num : semver,
457456 yanked : false ,
458457 } ;
459-
460- if existing_default_version < published_default_version {
461- diesel:: update ( default_versions:: table)
462- . filter ( default_versions:: crate_id. eq ( krate. id ) )
463- . set ( default_versions:: version_id. eq ( version. id ) )
458+ let update_base = diesel:: update ( default_versions:: table)
459+ . filter ( default_versions:: crate_id. eq ( krate. id ) ) ;
460+
461+ // If `num_versions` isn't set, this only occurs when a crate already exists and
462+ // releases a new version before the admin sets the value.
463+ // In this scenario, the `num_versions` value is updated in a background job. This case
464+ // should be safe to remove once all `num_versions` values have been set.
465+ let is_new = existing_default_version < published_default_version;
466+ let fut = match ( is_new, existing_num_versions. is_none ( ) ) {
467+ ( true , true ) => {
468+ update_base. set ( default_versions:: version_id. eq ( version. id ) ) . execute ( conn)
469+ } ,
470+ ( false , true ) => {
471+ default_version = Some ( existing_default_version. num . to_string ( ) ) ;
472+ async { Ok ( 0 ) } . boxed ( )
473+ } ,
474+ ( true , false ) => {
475+ update_base. set ( (
476+ default_versions:: version_id. eq ( version. id ) ,
477+ default_versions:: num_versions. eq ( default_versions:: num_versions + 1 )
478+ ) )
464479 . execute ( conn)
465- . await ?;
466- } else {
467- default_version = Some ( existing_default_version. num . to_string ( ) ) ;
468- }
480+ } ,
481+ ( false , false ) => {
482+ default_version = Some ( existing_default_version. num . to_string ( ) ) ;
483+ update_base. set (
484+ default_versions:: num_versions. eq ( default_versions:: num_versions + 1 )
485+ )
486+ . execute ( conn)
487+ } ,
488+ } ;
489+ fut. await ?;
469490
470491 // Update the default version asynchronously in a background job
471492 // to ensure correctness and eventual consistency.
@@ -475,6 +496,7 @@ pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult<Json<Go
475496 . values ( (
476497 default_versions:: crate_id. eq ( krate. id ) ,
477498 default_versions:: version_id. eq ( version. id ) ,
499+ default_versions:: num_versions. eq ( 1 ) ,
478500 ) )
479501 . execute ( conn)
480502 . await ?;
0 commit comments