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