@@ -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#[ derive( Debug , Clone , Eq , PartialEq , serde:: Serialize ) ]
1921pub ( crate ) struct QueuedCrate {
@@ -652,12 +654,191 @@ impl BuildQueue {
652654 }
653655}
654656
657+ /// Queue rebuilds as configured.
658+ ///
659+ /// The idea is to rebuild:
660+ /// * the latest release of each crate
661+ /// * when the nightly version is older than our configured threshold
662+ /// * and there was a successful build for that release, that included documentation.
663+ /// * starting with the oldest nightly versions.
664+ /// * also checking if there is already a build queued.
665+ ///
666+ /// This might exclude releases from rebuilds that
667+ /// * previously failed but would succeed with a newer nightly version
668+ /// * previously failed but would succeed just with a retry.
669+ #[ instrument( skip_all) ]
670+ pub async fn queue_rebuilds (
671+ conn : & mut sqlx:: PgConnection ,
672+ config : & Config ,
673+ build_queue : & AsyncBuildQueue ,
674+ ) -> Result < ( ) > {
675+ let already_queued_rebuilds = sqlx:: query_scalar!(
676+ r#"SELECT COUNT(*) as "count!" FROM queue WHERE priority >= $1"# ,
677+ REBUILD_PRIORITY
678+ )
679+ . fetch_one ( & mut * conn)
680+ . await ?;
681+
682+ let rebuilds_to_queue = config
683+ . max_queued_rebuilds
684+ . expect ( "config.max_queued_rebuilds not set" ) as i64
685+ - already_queued_rebuilds;
686+
687+ if rebuilds_to_queue <= 0 {
688+ info ! ( "not queueing rebuilds; queue limit reached" ) ;
689+ return Ok ( ( ) ) ;
690+ }
691+
692+ let mut results = sqlx:: query!(
693+ "SELECT i.* FROM (
694+ SELECT
695+ c.name,
696+ r.version,
697+ max(b.rustc_nightly_date) as rustc_nightly_date
698+
699+ FROM crates AS c
700+ INNER JOIN releases AS r ON c.latest_version_id = r.id
701+ INNER JOIN builds AS b ON r.id = b.rid
702+
703+ WHERE
704+ r.rustdoc_status = TRUE
705+
706+ GROUP BY c.name, r.version
707+ ) as i
708+ WHERE i.rustc_nightly_date < $1
709+ ORDER BY i.rustc_nightly_date ASC
710+ LIMIT $2" ,
711+ config
712+ . rebuild_up_to_date
713+ . expect( "config.rebuild_up_to_date not set" ) ,
714+ rebuilds_to_queue,
715+ )
716+ . fetch ( & mut * conn) ;
717+
718+ while let Some ( row) = results. next ( ) . await {
719+ let row = row?;
720+
721+ if !build_queue
722+ . has_build_queued ( & row. name , & row. version )
723+ . await ?
724+ {
725+ info ! ( "queueing rebuild for {} {}..." , & row. name, & row. version) ;
726+ build_queue
727+ . add_crate ( & row. name , & row. version , REBUILD_PRIORITY , None )
728+ . await ?;
729+ }
730+ }
731+
732+ Ok ( ( ) )
733+ }
734+
655735#[ cfg( test) ]
656736mod tests {
737+ use crate :: test:: FakeBuild ;
738+
657739 use super :: * ;
658- use chrono:: Utc ;
740+ use chrono:: { NaiveDate , Utc } ;
659741 use std:: time:: Duration ;
660742
743+ #[ test]
744+ fn test_dont_rebuild_when_new ( ) {
745+ crate :: test:: async_wrapper ( |env| async move {
746+ env. override_config ( |config| {
747+ config. max_queued_rebuilds = Some ( 100 ) ;
748+ config. rebuild_up_to_date = Some ( NaiveDate :: from_ymd_opt ( 2020 , 1 , 1 ) . unwrap ( ) ) ;
749+ } ) ;
750+
751+ env. async_fake_release ( )
752+ . await
753+ . name ( "foo" )
754+ . version ( "0.1.0" )
755+ . builds ( vec ! [ FakeBuild :: default ( )
756+ . rustc_version( "rustc 1.84.0-nightly (e7c0d2750 2020-10-15)" ) ] )
757+ . create_async ( )
758+ . await ?;
759+
760+ let build_queue = env. async_build_queue ( ) . await ;
761+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
762+
763+ let mut conn = env. async_db ( ) . await . async_conn ( ) . await ;
764+ queue_rebuilds ( & mut conn, & env. config ( ) , & build_queue) . await ?;
765+
766+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
767+
768+ Ok ( ( ) )
769+ } )
770+ }
771+
772+ #[ test]
773+ fn test_rebuild_when_old ( ) {
774+ crate :: test:: async_wrapper ( |env| async move {
775+ env. override_config ( |config| {
776+ config. max_queued_rebuilds = Some ( 100 ) ;
777+ config. rebuild_up_to_date = Some ( NaiveDate :: from_ymd_opt ( 2024 , 1 , 1 ) . unwrap ( ) ) ;
778+ } ) ;
779+
780+ env. async_fake_release ( )
781+ . await
782+ . name ( "foo" )
783+ . version ( "0.1.0" )
784+ . builds ( vec ! [ FakeBuild :: default ( )
785+ . rustc_version( "rustc 1.84.0-nightly (e7c0d2750 2020-10-15)" ) ] )
786+ . create_async ( )
787+ . await ?;
788+
789+ let build_queue = env. async_build_queue ( ) . await ;
790+ assert ! ( build_queue. queued_crates( ) . await ?. is_empty( ) ) ;
791+
792+ let mut conn = env. async_db ( ) . await . async_conn ( ) . await ;
793+ queue_rebuilds ( & mut conn, & env. config ( ) , & build_queue) . await ?;
794+
795+ let queue = build_queue. queued_crates ( ) . await ?;
796+ assert_eq ! ( queue. len( ) , 1 ) ;
797+ assert_eq ! ( queue[ 0 ] . name, "foo" ) ;
798+ assert_eq ! ( queue[ 0 ] . version, "0.1.0" ) ;
799+ assert_eq ! ( queue[ 0 ] . priority, REBUILD_PRIORITY ) ;
800+
801+ Ok ( ( ) )
802+ } )
803+ }
804+
805+ #[ test]
806+ fn test_dont_rebuild_when_full ( ) {
807+ crate :: test:: async_wrapper ( |env| async move {
808+ env. override_config ( |config| {
809+ config. max_queued_rebuilds = Some ( 1 ) ;
810+ config. rebuild_up_to_date = Some ( NaiveDate :: from_ymd_opt ( 2024 , 1 , 1 ) . unwrap ( ) ) ;
811+ } ) ;
812+
813+ let build_queue = env. async_build_queue ( ) . await ;
814+ build_queue
815+ . add_crate ( "foo1" , "0.1.0" , REBUILD_PRIORITY , None )
816+ . await ?;
817+ build_queue
818+ . add_crate ( "foo2" , "0.1.0" , REBUILD_PRIORITY , None )
819+ . await ?;
820+
821+ env. async_fake_release ( )
822+ . await
823+ . name ( "foo" )
824+ . version ( "0.1.0" )
825+ . builds ( vec ! [ FakeBuild :: default ( )
826+ . rustc_version( "rustc 1.84.0-nightly (e7c0d2750 2020-10-15)" ) ] )
827+ . create_async ( )
828+ . await ?;
829+
830+ let build_queue = env. async_build_queue ( ) . await ;
831+ assert_eq ! ( build_queue. queued_crates( ) . await ?. len( ) , 2 ) ;
832+
833+ let mut conn = env. async_db ( ) . await . async_conn ( ) . await ;
834+ queue_rebuilds ( & mut conn, & env. config ( ) , & build_queue) . await ?;
835+
836+ assert_eq ! ( build_queue. queued_crates( ) . await ?. len( ) , 2 ) ;
837+
838+ Ok ( ( ) )
839+ } )
840+ }
841+
661842 #[ test]
662843 fn test_add_duplicate_doesnt_fail_last_priority_wins ( ) {
663844 crate :: test:: async_wrapper ( |env| async move {
0 commit comments