This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new f22faf07af1 Fix v2 API 404 when a /config request is proxied to
another node (#5178)
f22faf07af1 is described below
commit f22faf07af110c783aaf079ef68b4455b59dba66
Author: Eric Pugh <[email protected]>
AuthorDate: Tue Oct 20 07:08:56 2026 -0400
Fix v2 API 404 when a /config request is proxied to another node (#5178)
Co-authored-by: Claude Sonnet 5 <[email protected]>
---
.../org/apache/solr/client/api/util/Constants.java | 12 +++++++++-
.../handler/admin/api/GetConfigSolrCloudTest.java | 28 +++++++++++++++++++++-
2 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/solr/api/src/java/org/apache/solr/client/api/util/Constants.java
b/solr/api/src/java/org/apache/solr/client/api/util/Constants.java
index 3e3c7443c18..13f8009fbb4 100644
--- a/solr/api/src/java/org/apache/solr/client/api/util/Constants.java
+++ b/solr/api/src/java/org/apache/solr/client/api/util/Constants.java
@@ -24,8 +24,18 @@ public class Constants {
public static final String INDEX_TYPE_PATH_PARAMETER = "indexType";
public static final String INDEX_NAME_PATH_PARAMETER = "indexName";
+
+ // "c" is accepted alongside "collections" because Solr internally forwards
a v2 request to the
+ // node actually hosting a collection using the short "/c/{collection}/..."
alias (see
+ // HttpSolrCall#sendRemoteProxy and V2HttpCall's "c"/"collections"
handling); without it, any
+ // JAX-RS resource built on this prefix 404s with "Cannot find API for the
path" whenever the
+ // request has to be proxied to another node.
public static final String INDEX_PATH_PREFIX =
- "/{" + INDEX_TYPE_PATH_PARAMETER + ":cores|collections}/{" +
INDEX_NAME_PATH_PARAMETER + "}";
+ "/{"
+ + INDEX_TYPE_PATH_PARAMETER
+ + ":cores|collections|c}/{"
+ + INDEX_NAME_PATH_PARAMETER
+ + "}";
public static final String CORE_NAME_PATH_PARAMETER = "coreName";
diff --git
a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
index 19b29380afd..dd51d21ddea 100644
---
a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
+++
b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
@@ -19,9 +19,12 @@ package org.apache.solr.handler.admin.api;
import static org.apache.solr.core.CoreContainer.ALLOW_PATHS_SYSPROP;
import org.apache.solr.client.api.model.IndexType;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.ConfigApi;
import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.embedded.JettySolrRunner;
import org.apache.solr.util.ExternalPaths;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -34,7 +37,9 @@ public class GetConfigSolrCloudTest extends SolrCloudTestCase
{
@BeforeClass
public static void setupCluster() throws Exception {
System.setProperty(ALLOW_PATHS_SYSPROP,
ExternalPaths.SERVER_HOME.toAbsolutePath().toString());
- configureCluster(1).addConfig("conf",
configset("cloud-minimal")).configure();
+ // 2 nodes so that a request can land on a node that does *not* host the
collection's only
+ // replica, forcing Solr to internally proxy the request to the node that
does.
+ configureCluster(2).addConfig("conf",
configset("cloud-minimal")).configure();
CollectionAdminRequest.createCollection(COLLECTION_NAME, "conf", 1, 1)
.process(cluster.getSolrClient());
}
@@ -77,6 +82,27 @@ public class GetConfigSolrCloudTest extends
SolrCloudTestCase {
assertTrue(response.znodeVersion.params >= -1);
}
+ @Test
+ public void testGetOverlayFromCollectionViaNonHostingNode() throws Exception
{
+ Replica replica =
getCollectionState(COLLECTION_NAME).replicaStream().findFirst().orElseThrow();
+ JettySolrRunner hostingJetty = cluster.getReplicaJetty(replica);
+ JettySolrRunner otherJetty =
+ cluster.getJettySolrRunners().stream()
+ .filter(jetty -> !jetty.equals(hostingJetty))
+ .findFirst()
+ .orElseThrow();
+
+ try (var client = new
HttpJettySolrClient.Builder(otherJetty.getBaseUrl().toString()).build()) {
+ var request = new ConfigApi.GetOverlay(IndexType.COLLECTION,
COLLECTION_NAME);
+ var response = request.process(client);
+
+ assertNotNull(response);
+ assertNull(response.error);
+ assertNotNull(response.overlay);
+ assertTrue(response.overlay.containsKey("znodeVersion"));
+ }
+ }
+
@Test
public void testGetConfigComponentFromCollection() throws Exception {
for (String component : GetConfig.MIGRATED_CONFIG_COMPONENTS) {