@@ -8,12 +8,14 @@ use crate::{cdn, BuildPackageSummary};
88use crate :: { Config , Index , InstanceMetrics , RustwideBuilder } ;
99use anyhow:: Context as _;
1010use fn_error_context:: context;
11- use futures_util:: stream:: TryStreamExt ;
11+ use futures_util:: { stream:: TryStreamExt , StreamExt } ;
1212use sqlx:: Connection as _;
1313use std:: collections:: HashMap ;
1414use std:: sync:: Arc ;
1515use tokio:: runtime:: Runtime ;
16- use tracing:: { debug, error, info} ;
16+ use tracing:: { debug, error, info, instrument} ;
17+
18+ pub ( crate ) const REBUILD_PRIORITY : i32 = 20 ;
1719
1820// Threshold priority to decide whether a crate will in the rebuild-queue-list.
1921// If crate is in the rebuild-queue-list it won't in the build-queue-list.
@@ -656,12 +658,191 @@ impl BuildQueue {
656658 }
657659}
658660
661+ /// Queue rebuilds as configured.
662+ ///
663+ /// The idea is to rebuild:
664+ /// * the latest release of each crate
665+ /// * when the nightly version is older than our configured threshold
666+ /// * and there was a successful build for that release, that included documentation.
667+ /// * starting with the oldest nightly versions.
668+ /// * also checking if there is already a build queued.
669+ ///
670+ /// This might exclude releases from rebuilds that
671+ /// * previously failed but would succeed with a newer nightly version
672+ /// * previously failed but would succeed just with a retry.
673+ #[ instrument( skip_all) ]
674+ pub async fn queue_rebuilds (
675+ conn : & mut sqlx:: PgConnection ,
676+ config : & Config ,
677+ build_queue : & AsyncBuildQueue ,
678+ ) -> Result < ( ) > {
679+ let already_queued_rebuilds = sqlx:: query_scalar!(
680+ r#"SELECT COUNT(*) as "count!" FROM queue WHERE priority >= $1"# ,
681+ REBUILD_PRIORITY
682+ )
683+ . fetch_one ( & mut * conn)
684+ . await ?;
685+
686+ let rebuilds_to_queue = config
687+ . max_queued_rebuilds
688+ . expect ( "config.max_queued_rebuilds not set" ) as i64
689+ - already_queued_rebuilds;
690+
691+ if rebuilds_to_queue <= 0 {
692+ info ! ( "not queueing rebuilds; queue limit reached" ) ;
693+ return Ok ( ( ) ) ;
694+ }
695+
696+ let mut results = sqlx:: query!(
697+ "SELECT i.* FROM (
698+ SELECT
699+ c.name,
700+ r.version,
701+ max(b.rustc_nightly_date) as rustc_nightly_date
702+
703+ FROM crates AS c
704+ INNER JOIN releases AS r ON c.latest_version_id = r.id
705+ INNER JOIN builds AS b ON r.id = b.rid
706+
707+ WHERE
708+ r.rustdoc_status = TRUE
709+
710+ GROUP BY c.name, r.version
711+ ) as i
712+ WHERE i.rustc_nightly_date < $1
713+ ORDER BY i.rustc_nightly_date ASC
714+ LIMIT $2" ,
715+ config
716+ . rebuild_up_to_date
717+ . expect( "config.rebuild_up_to_date not set" ) ,
718+ rebuilds_to_queue,
719+ )
720+ . fetch ( & mut * conn) ;
721+
722+ while let Some ( row) = results. next ( ) . await {
723+ let row = row?;
724+
725+ if !build_queue
726+ . has_build_queued ( & row. name , & row. version )
727+ . await ?
728+ {
729+ info ! ( "queueing rebuild for {} {}..." , & row. name, & row. version) ;
730+ build_queue
731+ . add_crate ( & row. name , & row. version , REBUILD_PRIORITY , None )
732+ . await ?;
733+ }
734+ }
735+
736+ Ok ( ( ) )
737+ }
738+
659739#[ cfg( test) ]
660740mod tests {
741+ use crate :: test:: FakeBuild ;
742+
661743 use super :: * ;
662- use chrono:: Utc ;
744+ use chrono:: { NaiveDate , Utc } ;
663745 use std:: time:: Duration ;
664746
747+ #[ test]
748+ fn test_dont_rebuild_when_new ( ) {
749+ crate :: test:: async_wrapper ( |env| async move {
750+ env. override_config ( |config| {
751+ config. max_queued_rebuilds = Some ( 100 ) ;
752+ config. rebuild_up_to_date = Some ( NaiveDate :: from_ymd_opt ( 2020 , 1 , 1 ) . unwrap ( ) ) ;
753+ } ) ;
754+
755+ env. async_fake_release ( )
756+ . await
757+ . name ( "foo" )
758+ . version ( "0.1.0" )
759+ . builds ( vec ! [ FakeBuild :: default ( )
760+ . rustc_version( "rustc 1.84.0-nightly (e7c0d2750 2020-10-15)" ) ] )
761+ . create_async ( )
762+ . await ?;
763+
764+ let build_queue = env. async_build_queue ( ) . await ;
765+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
766+
767+ let mut conn = env. async_db ( ) . await . async_conn ( ) . await ;
768+ queue_rebuilds ( & mut conn, & env. config ( ) , & build_queue) . await ?;
769+
770+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
771+
772+ Ok ( ( ) )
773+ } )
774+ }
775+
776+ #[ test]
777+ fn test_rebuild_when_old ( ) {
778+ crate :: test:: async_wrapper ( |env| async move {
779+ env. override_config ( |config| {
780+ config. max_queued_rebuilds = Some ( 100 ) ;
781+ config. rebuild_up_to_date = Some ( NaiveDate :: from_ymd_opt ( 2024 , 1 , 1 ) . unwrap ( ) ) ;
782+ } ) ;
783+
784+ env. async_fake_release ( )
785+ . await
786+ . name ( "foo" )
787+ . version ( "0.1.0" )
788+ . builds ( vec ! [ FakeBuild :: default ( )
789+ . rustc_version( "rustc 1.84.0-nightly (e7c0d2750 2020-10-15)" ) ] )
790+ . create_async ( )
791+ . await ?;
792+
793+ let build_queue = env. async_build_queue ( ) . await ;
794+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
795+
796+ let mut conn = env. async_db ( ) . await . async_conn ( ) . await ;
797+ queue_rebuilds ( & mut conn, & env. config ( ) , & build_queue) . await ?;
798+
799+ let queue = build_queue. queued_crates ( ) . await ?;
800+ assert_eq ! ( queue. len( ) , 1 ) ;
801+ assert_eq ! ( queue[ 0 ] . name, "foo" ) ;
802+ assert_eq ! ( queue[ 0 ] . version, "0.1.0" ) ;
803+ assert_eq ! ( queue[ 0 ] . priority, REBUILD_PRIORITY ) ;
804+
805+ Ok ( ( ) )
806+ } )
807+ }
808+
809+ #[ test]
810+ fn test_dont_rebuild_when_full ( ) {
811+ crate :: test:: async_wrapper ( |env| async move {
812+ env. override_config ( |config| {
813+ config. max_queued_rebuilds = Some ( 1 ) ;
814+ config. rebuild_up_to_date = Some ( NaiveDate :: from_ymd_opt ( 2024 , 1 , 1 ) . unwrap ( ) ) ;
815+ } ) ;
816+
817+ let build_queue = env. async_build_queue ( ) . await ;
818+ build_queue
819+ . add_crate ( "foo1" , "0.1.0" , REBUILD_PRIORITY , None )
820+ . await ?;
821+ build_queue
822+ . add_crate ( "foo2" , "0.1.0" , REBUILD_PRIORITY , None )
823+ . await ?;
824+
825+ env. async_fake_release ( )
826+ . await
827+ . name ( "foo" )
828+ . version ( "0.1.0" )
829+ . builds ( vec ! [ FakeBuild :: default ( )
830+ . rustc_version( "rustc 1.84.0-nightly (e7c0d2750 2020-10-15)" ) ] )
831+ . create_async ( )
832+ . await ?;
833+
834+ let build_queue = env. async_build_queue ( ) . await ;
835+ assert_eq ! ( build_queue. queued_crates( ) . await ?. len( ) , 2 ) ;
836+
837+ let mut conn = env. async_db ( ) . await . async_conn ( ) . await ;
838+ queue_rebuilds ( & mut conn, & env. config ( ) , & build_queue) . await ?;
839+
840+ assert_eq ! ( build_queue. queued_crates( ) . await ?. len( ) , 2 ) ;
841+
842+ Ok ( ( ) )
843+ } )
844+ }
845+
665846 #[ test]
666847 fn test_add_duplicate_doesnt_fail_last_priority_wins ( ) {
667848 crate :: test:: async_wrapper ( |env| async move {
0 commit comments