3333import org .apache .solr .client .solrj .SolrRequest ;
3434import org .apache .solr .client .solrj .SolrServerException ;
3535import org .apache .solr .client .solrj .request .GenericSolrRequest ;
36+ import org .apache .solr .client .solrj .request .GenericV2SolrRequest ;
3637import org .apache .solr .client .solrj .response .InputStreamResponseParser ;
3738import org .apache .solr .cloud .ZkController ;
3839import org .apache .solr .common .SolrException ;
40+ import org .apache .solr .common .params .CommonParams ;
3941import org .apache .solr .common .params .ModifiableSolrParams ;
4042import org .apache .solr .common .params .SolrParams ;
4143import org .apache .solr .common .util .NamedList ;
4244import org .apache .solr .core .CoreContainer ;
4345import org .apache .solr .request .SolrQueryRequest ;
4446import org .apache .solr .response .SolrQueryResponse ;
47+ import org .apache .solr .util .stats .MetricUtils ;
4548import org .slf4j .Logger ;
4649import org .slf4j .LoggerFactory ;
4750
@@ -55,17 +58,34 @@ public class AdminHandlersProxy {
5558 private static final String PARAM_NODE = "node" ;
5659 private static final long PROMETHEUS_FETCH_TIMEOUT_SECONDS = 10 ;
5760
58- /** Proxy this request to a different remote node if 'node' or 'nodes' parameter is provided */
61+ /**
62+ * Proxy this request to a different remote node's V1 API if 'node' or 'nodes' parameter is
63+ * provided. For V2, use {@link AdminHandlersProxy#maybeProxyToNodes(String, SolrQueryRequest,
64+ * SolrQueryResponse, CoreContainer)}
65+ */
5966 public static boolean maybeProxyToNodes (
6067 SolrQueryRequest req , SolrQueryResponse rsp , CoreContainer container )
6168 throws IOException , SolrServerException , InterruptedException {
69+ return maybeProxyToNodes ("V1" , req , rsp , container );
70+ }
71+
72+ /**
73+ * Proxy this request to a different remote node's selected API version if 'node' or 'nodes'
74+ * parameter is provided
75+ */
76+ public static boolean maybeProxyToNodes (
77+ String apiVersion , SolrQueryRequest req , SolrQueryResponse rsp , CoreContainer container )
78+ throws IOException , SolrServerException , InterruptedException {
6279
6380 String pathStr = req .getPath ();
6481 ModifiableSolrParams params = new ModifiableSolrParams (req .getParams ());
6582
6683 // Check if response format is Prometheus/OpenMetrics
67- String wt = params .get ("wt" );
68- boolean isPrometheusFormat = "prometheus" .equals (wt ) || "openmetrics" .equals (wt );
84+ String wt = params .get (CommonParams .WT );
85+ boolean isPrometheusFormat =
86+ MetricUtils .PROMETHEUS_METRICS_WT .equals (wt )
87+ || MetricUtils .OPEN_METRICS_WT .equals (wt )
88+ || (wt == null && pathStr .endsWith ("/metrics" ));
6989
7090 if (isPrometheusFormat ) {
7191 // Prometheus format: use singular 'node' parameter for single-node proxy
@@ -75,7 +95,7 @@ public static boolean maybeProxyToNodes(
7595 }
7696
7797 params .remove (PARAM_NODE );
78- handlePrometheusSingleNode (nodeName , pathStr , params , container , rsp );
98+ handlePrometheusSingleNode (apiVersion , nodeName , pathStr , params , container , rsp );
7999 } else {
80100 // Other formats (JSON/XML): use plural 'nodes' parameter for multi-node aggregation
81101 String nodeNames = req .getParams ().get (PARAM_NODES );
@@ -85,14 +105,15 @@ public static boolean maybeProxyToNodes(
85105
86106 params .remove (PARAM_NODES );
87107 Set <String > nodes = resolveNodes (nodeNames , container );
88- handleNamedListFormat (nodes , pathStr , params , container .getZkController (), rsp );
108+ handleNamedListFormat (apiVersion , nodes , pathStr , params , container .getZkController (), rsp );
89109 }
90110
91111 return true ;
92112 }
93113
94114 /** Handle non-Prometheus formats using the existing NamedList approach. */
95115 private static void handleNamedListFormat (
116+ String apiVersion ,
96117 Set <String > nodes ,
97118 String pathStr ,
98119 SolrParams params ,
@@ -101,7 +122,7 @@ private static void handleNamedListFormat(
101122
102123 Map <String , Future <NamedList <Object >>> responses = new LinkedHashMap <>();
103124 for (String node : nodes ) {
104- responses .put (node , callRemoteNode (node , pathStr , params , zkController ));
125+ responses .put (node , callRemoteNode (apiVersion , node , pathStr , params , zkController ));
105126 }
106127
107128 for (Map .Entry <String , Future <NamedList <Object >>> entry : responses .entrySet ()) {
@@ -125,8 +146,12 @@ private static void handleNamedListFormat(
125146 }
126147
127148 /** Makes a remote request asynchronously. */
128- public static CompletableFuture <NamedList <Object >> callRemoteNode (
129- String nodeName , String uriPath , SolrParams params , ZkController zkController ) {
149+ private static CompletableFuture <NamedList <Object >> callRemoteNode (
150+ String apiVersion ,
151+ String nodeName ,
152+ String uriPath ,
153+ SolrParams params ,
154+ ZkController zkController ) {
130155
131156 // Validate that the node exists in the cluster
132157 if (!zkController .zkStateReader .getClusterState ().getLiveNodes ().contains (nodeName )) {
@@ -137,13 +162,17 @@ public static CompletableFuture<NamedList<Object>> callRemoteNode(
137162
138163 log .debug ("Proxying {} request to node {}" , uriPath , nodeName );
139164 URI baseUri = URI .create (zkController .zkStateReader .getBaseUrlForNodeName (nodeName ));
140- SolrRequest <?> proxyReq = new GenericSolrRequest (SolrRequest .METHOD .GET , uriPath , params );
165+
166+ SolrRequest <?> proxyReq = createRequest (apiVersion , uriPath , params );
141167
142168 // Set response parser based on wt parameter to ensure correct format is used
143- String wt = params .get ("wt" );
144- if ("prometheus" . equals (wt ) || "openmetrics" .equals (wt )) {
169+ String wt = params .get (CommonParams . WT );
170+ if (MetricUtils . PROMETHEUS_METRICS_WT . equals (wt ) || MetricUtils . OPEN_METRICS_WT .equals (wt )) {
145171 proxyReq .setResponseParser (new InputStreamResponseParser (wt ));
146172 }
173+ if (wt == null && uriPath .endsWith ("/metrics" )) {
174+ proxyReq .setResponseParser (new InputStreamResponseParser (MetricUtils .PROMETHEUS_METRICS_WT ));
175+ }
147176
148177 try {
149178 return zkController
@@ -195,6 +224,7 @@ private static Set<String> resolveNodes(String nodeNames, CoreContainer containe
195224 * @param rsp the response to populate
196225 */
197226 private static void handlePrometheusSingleNode (
227+ String apiVersion ,
198228 String nodeName ,
199229 String pathStr ,
200230 ModifiableSolrParams params ,
@@ -205,7 +235,7 @@ private static void handlePrometheusSingleNode(
205235 // Keep wt=prometheus for the remote request so MetricsHandler accepts it
206236 // The InputStreamResponseParser will return the Prometheus text in a "stream" key
207237 Future <NamedList <Object >> response =
208- callRemoteNode (nodeName , pathStr , params , container .getZkController ());
238+ callRemoteNode (apiVersion , nodeName , pathStr , params , container .getZkController ());
209239
210240 try {
211241 try {
@@ -220,4 +250,12 @@ private static void handlePrometheusSingleNode(
220250 throw new SolrException (SolrException .ErrorCode .SERVER_ERROR , t );
221251 }
222252 }
253+
254+ private static SolrRequest <?> createRequest (
255+ String apiVersion , String uriPath , SolrParams params ) {
256+ if (apiVersion .equalsIgnoreCase ("V1" )) {
257+ return new GenericSolrRequest (SolrRequest .METHOD .GET , uriPath , params );
258+ }
259+ return new GenericV2SolrRequest (SolrRequest .METHOD .GET , uriPath , params );
260+ }
223261}
0 commit comments