@@ -779,4 +779,135 @@ describe("FairDequeuingStrategy", () => {
779
779
expect ( mixed [ "queue-1" ] [ 0 ] ) . toBeGreaterThan ( mixed [ "queue-3" ] [ 0 ] ) ; // Older preferred
780
780
expect ( mixed [ "queue-3" ] [ 0 ] ) . toBeGreaterThan ( 0 ) ; // But newer still gets chances
781
781
} ) ;
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
+ ) ;
782
913
} ) ;
0 commit comments