@@ -13,6 +13,7 @@ use crate::{
1313 utils:: { ConfigName , get_config, get_crate_priority, report_error, retry, set_config} ,
1414} ;
1515use anyhow:: Context as _;
16+ use chrono:: NaiveDate ;
1617use fn_error_context:: context;
1718use futures_util:: { StreamExt , stream:: TryStreamExt } ;
1819use opentelemetry:: metrics:: Counter ;
@@ -44,6 +45,8 @@ impl BuildQueueMetrics {
4445/// collapsed in the UI.
4546/// For normal build priorities we use smaller values.
4647pub ( crate ) const REBUILD_PRIORITY : i32 = 20 ;
48+ // TODO what value should we use here?
49+ pub ( crate ) const BROKEN_RUSTDOC_REBUILD_PRIORITY : i32 = 30 ;
4750
4851#[ derive( Debug , Clone , Eq , PartialEq , serde:: Serialize ) ]
4952pub ( crate ) struct QueuedCrate {
@@ -775,9 +778,75 @@ pub async fn queue_rebuilds(
775778 Ok ( ( ) )
776779}
777780
781+ /// Queue rebuilds for failed crates due to a faulty version of rustdoc
782+ ///
783+ /// It is assumed that the version of rustdoc matches the one of rustc, which is persisted in the DB.
784+ /// The priority of the resulting rebuild requests will be lower than previously failed builds.
785+ /// If a crate is already queued to be rebuilt, it will not be requeued.
786+ /// Start date is inclusive, end date is exclusive.
787+ #[ instrument( skip_all) ]
788+ pub async fn queue_rebuilds_faulty_rustdoc (
789+ conn : & mut sqlx:: PgConnection ,
790+ build_queue : & AsyncBuildQueue ,
791+ start_nightly_date : & NaiveDate ,
792+ end_nightly_date : & Option < NaiveDate > ,
793+ ) -> Result < i32 > {
794+ let end_nightly_date =
795+ end_nightly_date. unwrap_or_else ( || start_nightly_date. succ_opt ( ) . unwrap ( ) ) ;
796+ let mut results = sqlx:: query!(
797+ r#"
798+ SELECT c.name,
799+ r.version AS "version: Version"
800+ FROM crates AS c
801+ JOIN releases AS r
802+ ON c.latest_version_id = r.id
803+ AND r.rustdoc_status = TRUE
804+ JOIN LATERAL (
805+ SELECT b.id,
806+ b.build_status,
807+ b.rustc_nightly_date,
808+ COALESCE(b.build_finished, b.build_started) AS last_build_attempt
809+ FROM builds AS b
810+ WHERE b.rid = r.id
811+ ORDER BY last_build_attempt DESC
812+ LIMIT 1
813+ ) AS b ON b.build_status = 'failure' AND b.rustc_nightly_date >= $1 AND b.rustc_nightly_date < $2
814+
815+ "# , start_nightly_date, end_nightly_date
816+ )
817+ . fetch ( & mut * conn) ;
818+
819+ let mut results_count = 0 ;
820+ while let Some ( row) = results. next ( ) . await {
821+ let row = row?;
822+
823+ if !build_queue
824+ . has_build_queued ( & row. name , & row. version )
825+ . await ?
826+ {
827+ results_count += 1 ;
828+ info ! (
829+ "queueing rebuild for {} {} (priority {})..." ,
830+ & row. name, & row. version, BROKEN_RUSTDOC_REBUILD_PRIORITY
831+ ) ;
832+ build_queue
833+ . add_crate (
834+ & row. name ,
835+ & row. version ,
836+ BROKEN_RUSTDOC_REBUILD_PRIORITY ,
837+ None ,
838+ )
839+ . await ?;
840+ }
841+ }
842+
843+ Ok ( results_count)
844+ }
845+
778846#[ cfg( test) ]
779847mod tests {
780848 use super :: * ;
849+ use crate :: db:: types:: BuildStatus ;
781850 use crate :: test:: { FakeBuild , TestEnvironment , V1 , V2 } ;
782851 use chrono:: Utc ;
783852
@@ -817,6 +886,171 @@ mod tests {
817886 Ok ( ( ) )
818887 }
819888
889+ /// Verifies whether a rebuild is queued for a crate that previously failed with a nightly version of rustdoc.
890+ #[ tokio:: test( flavor = "multi_thread" ) ]
891+ async fn test_rebuild_broken_rustdoc_specific_date_simple ( ) -> Result < ( ) > {
892+ let env = TestEnvironment :: with_config (
893+ TestEnvironment :: base_config ( )
894+ . max_queued_rebuilds ( Some ( 100 ) )
895+ . build ( ) ?,
896+ )
897+ . await ?;
898+
899+ for i in 1 ..5 {
900+ let nightly_date = NaiveDate :: from_ymd_opt ( 2020 , 10 , i) . unwrap ( ) ;
901+ env. fake_release ( )
902+ . await
903+ . name ( & format ! ( "foo{}" , i) )
904+ . version ( V1 )
905+ . builds ( vec ! [
906+ FakeBuild :: default ( )
907+ . rustc_version(
908+ format!(
909+ "rustc 1.84.0-nightly (e7c0d2750 {})" ,
910+ nightly_date. format( "%Y-%m-%d" )
911+ )
912+ . as_str( ) ,
913+ )
914+ . build_status( BuildStatus :: Failure ) ,
915+ ] )
916+ . create ( )
917+ . await ?;
918+ }
919+
920+ let build_queue = env. async_build_queue ( ) ;
921+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
922+
923+ let mut conn = env. async_db ( ) . async_conn ( ) . await ;
924+ queue_rebuilds_faulty_rustdoc (
925+ & mut conn,
926+ build_queue,
927+ & NaiveDate :: from_ymd_opt ( 2020 , 10 , 3 ) . unwrap ( ) ,
928+ & None ,
929+ )
930+ . await ?;
931+
932+ let queue = build_queue. queued_crates ( ) . await ?;
933+ assert_eq ! ( queue. len( ) , 1 ) ;
934+ assert_eq ! ( queue[ 0 ] . name, "foo3" ) ;
935+ assert_eq ! ( queue[ 0 ] . version, V1 ) ;
936+ assert_eq ! ( queue[ 0 ] . priority, BROKEN_RUSTDOC_REBUILD_PRIORITY ) ;
937+
938+ Ok ( ( ) )
939+ }
940+
941+ /// Verified whether a rebuild is NOT queued since the latest build for the specific crate is marked as successful.
942+ #[ tokio:: test( flavor = "multi_thread" ) ]
943+ async fn test_rebuild_broken_rustdoc_specific_date_skipped ( ) -> Result < ( ) > {
944+ let env = TestEnvironment :: with_config (
945+ TestEnvironment :: base_config ( )
946+ . max_queued_rebuilds ( Some ( 100 ) )
947+ . build ( ) ?,
948+ )
949+ . await ?;
950+
951+ env. fake_release ( )
952+ . await
953+ . name ( "foo" )
954+ . version ( V1 )
955+ . builds ( vec ! [
956+ FakeBuild :: default ( )
957+ . rustc_version(
958+ format!(
959+ "rustc 1.84.0-nightly (e7c0d2750 {})" ,
960+ NaiveDate :: from_ymd_opt( 2020 , 10 , 1 )
961+ . unwrap( )
962+ . format( "%Y-%m-%d" )
963+ )
964+ . as_str( ) ,
965+ )
966+ . build_status( BuildStatus :: Failure ) ,
967+ FakeBuild :: default ( )
968+ . rustc_version(
969+ format!(
970+ "rustc 1.84.0-nightly (e7c0d2750 {})" ,
971+ NaiveDate :: from_ymd_opt( 2020 , 10 , 1 )
972+ . unwrap( )
973+ . format( "%Y-%m-%d" )
974+ )
975+ . as_str( ) ,
976+ )
977+ . build_status( BuildStatus :: Success ) ,
978+ ] )
979+ . create ( )
980+ . await ?;
981+
982+ let build_queue = env. async_build_queue ( ) ;
983+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
984+
985+ let mut conn = env. async_db ( ) . async_conn ( ) . await ;
986+ queue_rebuilds_faulty_rustdoc (
987+ & mut conn,
988+ build_queue,
989+ & NaiveDate :: from_ymd_opt ( 2020 , 10 , 1 ) . unwrap ( ) ,
990+ & None ,
991+ )
992+ . await ?;
993+
994+ let queue = build_queue. queued_crates ( ) . await ?;
995+ assert_eq ! ( queue. len( ) , 0 ) ;
996+
997+ Ok ( ( ) )
998+ }
999+
1000+ #[ tokio:: test( flavor = "multi_thread" ) ]
1001+ async fn test_rebuild_broken_rustdoc_date_range ( ) -> Result < ( ) > {
1002+ let env = TestEnvironment :: with_config (
1003+ TestEnvironment :: base_config ( )
1004+ . max_queued_rebuilds ( Some ( 100 ) )
1005+ . build ( ) ?,
1006+ )
1007+ . await ?;
1008+
1009+ for i in 1 ..6 {
1010+ let nightly_date = NaiveDate :: from_ymd_opt ( 2020 , 10 , i) . unwrap ( ) ;
1011+ env. fake_release ( )
1012+ . await
1013+ . name ( & format ! ( "foo{}" , i) )
1014+ . version ( V1 )
1015+ . builds ( vec ! [
1016+ FakeBuild :: default ( )
1017+ . rustc_version(
1018+ format!(
1019+ "rustc 1.84.0-nightly (e7c0d2750 {})" ,
1020+ nightly_date. format( "%Y-%m-%d" )
1021+ )
1022+ . as_str( ) ,
1023+ )
1024+ . build_status( BuildStatus :: Failure ) ,
1025+ ] )
1026+ . create ( )
1027+ . await ?;
1028+ }
1029+
1030+ let build_queue = env. async_build_queue ( ) ;
1031+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
1032+
1033+ let mut conn = env. async_db ( ) . async_conn ( ) . await ;
1034+ queue_rebuilds_faulty_rustdoc (
1035+ & mut conn,
1036+ build_queue,
1037+ & NaiveDate :: from_ymd_opt ( 2020 , 10 , 3 ) . unwrap ( ) ,
1038+ & Some ( NaiveDate :: from_ymd_opt ( 2020 , 10 , 5 ) ) . unwrap ( ) ,
1039+ )
1040+ . await ?;
1041+
1042+ let queue = build_queue. queued_crates ( ) . await ?;
1043+ assert_eq ! ( queue. len( ) , 2 ) ;
1044+ assert_eq ! ( queue[ 0 ] . name, "foo3" ) ;
1045+ assert_eq ! ( queue[ 0 ] . version, V1 ) ;
1046+ assert_eq ! ( queue[ 0 ] . priority, BROKEN_RUSTDOC_REBUILD_PRIORITY ) ;
1047+ assert_eq ! ( queue[ 1 ] . name, "foo4" ) ;
1048+ assert_eq ! ( queue[ 1 ] . version, V1 ) ;
1049+ assert_eq ! ( queue[ 1 ] . priority, BROKEN_RUSTDOC_REBUILD_PRIORITY ) ;
1050+
1051+ Ok ( ( ) )
1052+ }
1053+
8201054 #[ tokio:: test( flavor = "multi_thread" ) ]
8211055 async fn test_still_rebuild_when_full_with_failed ( ) -> Result < ( ) > {
8221056 let env = TestEnvironment :: with_config (
0 commit comments