@@ -779,4 +779,135 @@ describe("FairDequeuingStrategy", () => {
779779 expect ( mixed [ "queue-1" ] [ 0 ] ) . toBeGreaterThan ( mixed [ "queue-3" ] [ 0 ] ) ; // Older preferred
780780 expect ( mixed [ "queue-3" ] [ 0 ] ) . toBeGreaterThan ( 0 ) ; // But newer still gets chances
781781 } ) ;
782+
783+ redisTest (
784+ "should respect maximumOrgCount and select orgs based on queue ages" ,
785+ async ( { redis } ) => {
786+ const keyProducer = createKeyProducer ( "test" ) ;
787+ const strategy = new FairDequeuingStrategy ( {
788+ tracer,
789+ redis,
790+ keys : keyProducer ,
791+ defaultOrgConcurrency : 10 ,
792+ defaultEnvConcurrency : 5 ,
793+ parentQueueLimit : 100 ,
794+ checkForDisabledOrgs : true ,
795+ seed : "test-seed-max-orgs" ,
796+ maximumOrgCount : 2 , // Only select top 2 orgs
797+ } ) ;
798+
799+ const now = Date . now ( ) ;
800+
801+ // Setup 4 orgs with different queue age profiles
802+ const orgSetups = [
803+ {
804+ orgId : "org-1" ,
805+ queues : [
806+ { age : 1000 } , // Average age: 1000
807+ ] ,
808+ } ,
809+ {
810+ orgId : "org-2" ,
811+ queues : [
812+ { age : 5000 } , // Average age: 5000
813+ { age : 5000 } ,
814+ ] ,
815+ } ,
816+ {
817+ orgId : "org-3" ,
818+ queues : [
819+ { age : 2000 } , // Average age: 2000
820+ { age : 2000 } ,
821+ ] ,
822+ } ,
823+ {
824+ orgId : "org-4" ,
825+ queues : [
826+ { age : 500 } , // Average age: 500
827+ { age : 500 } ,
828+ ] ,
829+ } ,
830+ ] ;
831+
832+ // Setup queues and concurrency for each org
833+ for ( const setup of orgSetups ) {
834+ await setupConcurrency ( {
835+ redis,
836+ keyProducer,
837+ org : { id : setup . orgId , currentConcurrency : 0 , limit : 10 } ,
838+ env : { id : "env-1" , currentConcurrency : 0 , limit : 5 } ,
839+ } ) ;
840+
841+ for ( let i = 0 ; i < setup . queues . length ; i ++ ) {
842+ await setupQueue ( {
843+ redis,
844+ keyProducer,
845+ parentQueue : "parent-queue" ,
846+ score : now - setup . queues [ i ] . age ,
847+ queueId : `queue-${ setup . orgId } -${ i } ` ,
848+ orgId : setup . orgId ,
849+ envId : "env-1" ,
850+ } ) ;
851+ }
852+ }
853+
854+ // Run multiple iterations to verify consistent behavior
855+ const iterations = 100 ;
856+ const selectedOrgCounts : Record < string , number > = { } ;
857+
858+ for ( let i = 0 ; i < iterations ; i ++ ) {
859+ const result = await strategy . distributeFairQueuesFromParentQueue (
860+ "parent-queue" ,
861+ `consumer-${ i } `
862+ ) ;
863+
864+ // Track which orgs were included in the result
865+ const selectedOrgs = new Set ( result . map ( ( queueId ) => keyProducer . orgIdFromQueue ( queueId ) ) ) ;
866+
867+ // Verify we never get more than maximumOrgCount orgs
868+ expect ( selectedOrgs . size ) . toBeLessThanOrEqual ( 2 ) ;
869+
870+ for ( const orgId of selectedOrgs ) {
871+ selectedOrgCounts [ orgId ] = ( selectedOrgCounts [ orgId ] || 0 ) + 1 ;
872+ }
873+ }
874+
875+ console . log ( "Organization selection counts:" , selectedOrgCounts ) ;
876+
877+ // org-2 should be selected most often (highest average age)
878+ expect ( selectedOrgCounts [ "org-2" ] ) . toBeGreaterThan ( selectedOrgCounts [ "org-4" ] || 0 ) ;
879+
880+ // org-4 should be selected least often (lowest average age)
881+ const org4Count = selectedOrgCounts [ "org-4" ] || 0 ;
882+ expect ( org4Count ) . toBeLessThan ( selectedOrgCounts [ "org-2" ] ) ;
883+
884+ // Verify that orgs with higher average queue age are selected more frequently
885+ const sortedOrgs = Object . entries ( selectedOrgCounts ) . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] ) ;
886+ console . log ( "Sorted organization frequencies:" , sortedOrgs ) ;
887+
888+ // The top 2 most frequently selected orgs should be org-2 and org-3
889+ // as they have the highest average queue ages
890+ const topTwoOrgs = new Set ( [ sortedOrgs [ 0 ] [ 0 ] , sortedOrgs [ 1 ] [ 0 ] ] ) ;
891+ expect ( topTwoOrgs ) . toContain ( "org-2" ) ; // Highest average age
892+ expect ( topTwoOrgs ) . toContain ( "org-3" ) ; // Second highest average age
893+
894+ // Calculate selection percentages
895+ const totalSelections = Object . values ( selectedOrgCounts ) . reduce ( ( a , b ) => a + b , 0 ) ;
896+ const selectionPercentages = Object . entries ( selectedOrgCounts ) . reduce (
897+ ( acc , [ orgId , count ] ) => {
898+ acc [ orgId ] = ( count / totalSelections ) * 100 ;
899+ return acc ;
900+ } ,
901+ { } as Record < string , number >
902+ ) ;
903+
904+ console . log ( "Organization selection percentages:" , selectionPercentages ) ;
905+
906+ // Verify that org-2 (highest average age) gets selected in at least 40% of iterations
907+ expect ( selectionPercentages [ "org-2" ] ) . toBeGreaterThan ( 40 ) ;
908+
909+ // Verify that org-4 (lowest average age) gets selected in less than 20% of iterations
910+ expect ( selectionPercentages [ "org-4" ] || 0 ) . toBeLessThan ( 20 ) ;
911+ }
912+ ) ;
782913} ) ;
0 commit comments