@@ -807,4 +807,35 @@ mod tests {
807807
808808 assert_eq ! ( solution. capacity_scaling_iterations, 1 ) ;
809809 }
810+
811+ #[ test]
812+ fn test_shard_fragmentation_when_iterating ( ) {
813+ // Create a problem where affinity constraints cause suboptimal placement
814+ // requiring iterative scaling despite initial capacity scaling.
815+ let mut problem =
816+ SchedulingProblem :: with_indexer_cpu_capacities ( vec ! [ mcpu( 3000 ) , mcpu( 3000 ) ] ) ;
817+ problem. add_source ( 1 , NonZeroU32 :: new ( 1000 ) . unwrap ( ) ) ;
818+ problem. add_source ( 1 , NonZeroU32 :: new ( 1000 ) . unwrap ( ) ) ;
819+ problem. add_source ( 1 , NonZeroU32 :: new ( 1000 ) . unwrap ( ) ) ;
820+ let empty_solution = problem. new_solution ( ) ;
821+ let first_solution = solve ( problem, empty_solution) ;
822+
823+ let mut updated_problem =
824+ SchedulingProblem :: with_indexer_cpu_capacities ( vec ! [ mcpu( 3000 ) , mcpu( 3000 ) ] ) ;
825+ updated_problem. add_source ( 2 , NonZeroU32 :: new ( 1000 ) . unwrap ( ) ) ;
826+ updated_problem. add_source ( 2 , NonZeroU32 :: new ( 1000 ) . unwrap ( ) ) ;
827+ updated_problem. add_source ( 2 , NonZeroU32 :: new ( 1000 ) . unwrap ( ) ) ;
828+
829+ let second_solution = solve ( updated_problem, first_solution) ;
830+
831+ for source in 0 ..2 {
832+ let num_shards_per_indexer = second_solution
833+ . indexer_assignments
834+ . iter ( )
835+ . map ( |indexer_assignment| indexer_assignment. num_shards ( source) )
836+ . collect_vec ( ) ;
837+ assert ! ( num_shards_per_indexer. contains( & 2 ) ) ;
838+ assert ! ( num_shards_per_indexer. contains( & 0 ) ) ;
839+ }
840+ }
810841}
0 commit comments