@@ -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 , 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 :: total ) )
440439 . first ( conn)
441440 . await
442441 . optional ( ) ?;
@@ -450,22 +449,45 @@ 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_total ) ) = 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 `total` isn't set (it defaults to 0), this only occurs when a crate already
462+ // exists and releases a new version before the admin sets the total.
463+ // In this scenario, the `total` value is updated in a background job. This case should
464+ // be safe to remove once all `total` values have been set.
465+ let is_new = existing_default_version < published_default_version;
466+ let default_total = existing_total == 0 ;
467+ let fut = match ( is_new, default_total) {
468+ ( true , true ) => {
469+ update_base. set ( default_versions:: version_id. eq ( version. id ) ) . execute ( conn)
470+ } ,
471+ ( false , true ) => {
472+ default_version = Some ( existing_default_version. num . to_string ( ) ) ;
473+ async { Ok ( 0 ) } . boxed ( )
474+ } ,
475+ ( true , false ) => {
476+ update_base. set ( (
477+ default_versions:: version_id. eq ( version. id ) ,
478+ default_versions:: total. eq ( default_versions:: total + 1 )
479+ ) )
464480 . execute ( conn)
465- . await ?;
466- } else {
467- default_version = Some ( existing_default_version. num . to_string ( ) ) ;
468- }
481+ } ,
482+ ( false , false ) => {
483+ default_version = Some ( existing_default_version. num . to_string ( ) ) ;
484+ update_base. set (
485+ default_versions:: total. eq ( default_versions:: total + 1 )
486+ )
487+ . execute ( conn)
488+ } ,
489+ } ;
490+ fut. await ?;
469491
470492 // Update the default version asynchronously in a background job
471493 // to ensure correctness and eventual consistency.
@@ -475,6 +497,7 @@ pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult<Json<Go
475497 . values ( (
476498 default_versions:: crate_id. eq ( krate. id ) ,
477499 default_versions:: version_id. eq ( version. id ) ,
500+ default_versions:: total. eq ( 1 ) ,
478501 ) )
479502 . execute ( conn)
480503 . await ?;
0 commit comments