@@ -859,11 +859,19 @@ func (js *jetStream) apiDispatch(sub *subscription, c *client, acc *Account, sub
859859 // Copy the state. Note the JSAPI only uses the hdr index to piece apart the
860860 // header from the msg body. No other references are needed.
861861 // Check pending and warn if getting backed up.
862- pending , _ := s .jsAPIRoutedReqs .push (& jsAPIRoutedReq {jsub , sub , acc , subject , reply , copyBytes (rmsg ), c .pa })
863- limit := atomic .LoadInt64 (& js .queueLimit )
862+ var queue * ipQueue [* jsAPIRoutedReq ]
863+ var limit int64
864+ if js .infoSubs .HasInterest (subject ) {
865+ queue = s .jsAPIRoutedInfoReqs
866+ limit = atomic .LoadInt64 (& js .infoQueueLimit )
867+ } else {
868+ queue = s .jsAPIRoutedReqs
869+ limit = atomic .LoadInt64 (& js .queueLimit )
870+ }
871+ pending , _ := queue .push (& jsAPIRoutedReq {jsub , sub , acc , subject , reply , copyBytes (rmsg ), c .pa })
864872 if pending >= int (limit ) {
865- s .rateLimitFormatWarnf ("JetStream API queue limit reached, dropping %d requests" , pending )
866- drained := int64 (s . jsAPIRoutedReqs .drain ())
873+ s .rateLimitFormatWarnf ("%s limit reached, dropping %d requests" , queue . name , pending )
874+ drained := int64 (queue .drain ())
867875 atomic .AddInt64 (& js .apiInflight , - drained )
868876
869877 s .publishAdvisory (nil , JSAdvisoryAPILimitReached , JSAPILimitReachedAdvisory {
@@ -883,29 +891,45 @@ func (s *Server) processJSAPIRoutedRequests() {
883891 defer s .grWG .Done ()
884892
885893 s .mu .RLock ()
886- queue := s .jsAPIRoutedReqs
894+ queue , infoqueue := s .jsAPIRoutedReqs , s . jsAPIRoutedInfoReqs
887895 client := & client {srv : s , kind : JETSTREAM }
888896 s .mu .RUnlock ()
889897
890898 js := s .getJetStream ()
891899
900+ processFromQueue := func (ipq * ipQueue [* jsAPIRoutedReq ]) {
901+ // Only pop one item at a time here, otherwise if the system is recovering
902+ // from queue buildup, then one worker will pull off all the tasks and the
903+ // others will be starved of work.
904+ if r , ok := ipq .popOne (); ok && r != nil {
905+ client .pa = r .pa
906+ start := time .Now ()
907+ r .jsub .icb (r .sub , client , r .acc , r .subject , r .reply , r .msg )
908+ if dur := time .Since (start ); dur >= readLoopReportThreshold {
909+ s .Warnf ("Internal subscription on %q took too long: %v" , r .subject , dur )
910+ }
911+ atomic .AddInt64 (& js .apiInflight , - 1 )
912+ }
913+ }
914+
892915 for {
916+ // First select case is prioritizing queue, we will only fall through
917+ // to the second select case that considers infoqueue if queue is empty.
918+ // This effectively means infos are deprioritized.
893919 select {
894920 case <- queue .ch :
895- // Only pop one item at a time here, otherwise if the system is recovering
896- // from queue buildup, then one worker will pull off all the tasks and the
897- // others will be starved of work.
898- for r , ok := queue .popOne (); ok && r != nil ; r , ok = queue .popOne () {
899- client .pa = r .pa
900- start := time .Now ()
901- r .jsub .icb (r .sub , client , r .acc , r .subject , r .reply , r .msg )
902- if dur := time .Since (start ); dur >= readLoopReportThreshold {
903- s .Warnf ("Internal subscription on %q took too long: %v" , r .subject , dur )
904- }
905- atomic .AddInt64 (& js .apiInflight , - 1 )
906- }
921+ processFromQueue (queue )
907922 case <- s .quitCh :
908923 return
924+ default :
925+ select {
926+ case <- infoqueue .ch :
927+ processFromQueue (infoqueue )
928+ case <- queue .ch :
929+ processFromQueue (queue )
930+ case <- s .quitCh :
931+ return
932+ }
909933 }
910934 }
911935}
@@ -924,7 +948,8 @@ func (s *Server) setJetStreamExportSubs() error {
924948 if mp > maxProcs {
925949 mp = maxProcs
926950 }
927- s .jsAPIRoutedReqs = newIPQueue [* jsAPIRoutedReq ](s , "Routed JS API Requests" )
951+ s .jsAPIRoutedReqs = newIPQueue [* jsAPIRoutedReq ](s , "JetStream API queue" )
952+ s .jsAPIRoutedInfoReqs = newIPQueue [* jsAPIRoutedReq ](s , "JetStream API info queue" )
928953 for i := 0 ; i < mp ; i ++ {
929954 s .startGoRoutine (s .processJSAPIRoutedRequests )
930955 }
@@ -940,16 +965,13 @@ func (s *Server) setJetStreamExportSubs() error {
940965 }
941966
942967 // API handles themselves.
968+ // infopairs are deprioritized compared to pairs in processJSAPIRoutedRequests.
943969 pairs := []struct {
944970 subject string
945971 handler msgHandler
946972 }{
947- {JSApiAccountInfo , s .jsAccountInfoRequest },
948973 {JSApiStreamCreate , s .jsStreamCreateRequest },
949974 {JSApiStreamUpdate , s .jsStreamUpdateRequest },
950- {JSApiStreams , s .jsStreamNamesRequest },
951- {JSApiStreamList , s .jsStreamListRequest },
952- {JSApiStreamInfo , s .jsStreamInfoRequest },
953975 {JSApiStreamDelete , s .jsStreamDeleteRequest },
954976 {JSApiStreamPurge , s .jsStreamPurgeRequest },
955977 {JSApiStreamSnapshot , s .jsStreamSnapshotRequest },
@@ -962,23 +984,40 @@ func (s *Server) setJetStreamExportSubs() error {
962984 {JSApiConsumerCreateEx , s .jsConsumerCreateRequest },
963985 {JSApiConsumerCreate , s .jsConsumerCreateRequest },
964986 {JSApiDurableCreate , s .jsConsumerCreateRequest },
965- {JSApiConsumers , s .jsConsumerNamesRequest },
966- {JSApiConsumerList , s .jsConsumerListRequest },
967- {JSApiConsumerInfo , s .jsConsumerInfoRequest },
968987 {JSApiConsumerDelete , s .jsConsumerDeleteRequest },
969988 {JSApiConsumerPause , s .jsConsumerPauseRequest },
970989 {JSApiConsumerUnpin , s .jsConsumerUnpinRequest },
971990 }
991+ infopairs := []struct {
992+ subject string
993+ handler msgHandler
994+ }{
995+ {JSApiAccountInfo , s .jsAccountInfoRequest },
996+ {JSApiStreams , s .jsStreamNamesRequest },
997+ {JSApiStreamList , s .jsStreamListRequest },
998+ {JSApiStreamInfo , s .jsStreamInfoRequest },
999+ {JSApiConsumers , s .jsConsumerNamesRequest },
1000+ {JSApiConsumerList , s .jsConsumerListRequest },
1001+ {JSApiConsumerInfo , s .jsConsumerInfoRequest },
1002+ }
9721003
9731004 js .mu .Lock ()
9741005 defer js .mu .Unlock ()
9751006
976- for _ , p := range pairs {
1007+ // As well as populating js.apiSubs for the dispatch function to use, we
1008+ // will also populate js.infoSubs, so that the dispatch function can
1009+ // decide quickly whether or not the request is an info request or not.
1010+ for _ , p := range append (infopairs , pairs ... ) {
9771011 sub := & subscription {subject : []byte (p .subject ), icb : p .handler }
9781012 if err := js .apiSubs .Insert (sub ); err != nil {
9791013 return err
9801014 }
9811015 }
1016+ for _ , p := range infopairs {
1017+ if err := js .infoSubs .Insert (p .subject , struct {}{}); err != nil {
1018+ return err
1019+ }
1020+ }
9821021
9831022 return nil
9841023}
0 commit comments