You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ml...@apache.org on 2026/03/26 04:01:49 UTC
(solr) branch branch_10x updated: [SOLR-18114] CloudSolrClient fails deleteById with directUpdatesToLeadersOnly but no route passed (#4219)
This is an automated email from the ASF dual-hosted git repository.
mlbiscoc pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_10x by this push:
new 8299ae01ac2 [SOLR-18114] CloudSolrClient fails deleteById with directUpdatesToLeadersOnly but no route passed (#4219)
8299ae01ac2 is described below
commit 8299ae01ac2573f692290c3dd7a500c37fd62ebf
Author: Matthew Biscocho <ml...@apache.org>
AuthorDate: Wed Mar 25 22:42:58 2026 -0400
[SOLR-18114] CloudSolrClient fails deleteById with directUpdatesToLeadersOnly but no route passed (#4219)
Fixed a bug where CloudSolrClient throws an exception when deleteById is sent a to collection that has router field configured and has directUpdatesToLeadersOnly=true but is sent without routing info. Also added a log.warn when request is sent but no routing info is passed causing request to be sent in the general unoptimized route.
---
changelog/unreleased/SOLR-18114.yml | 8 +++
.../solr/client/solrj/impl/CloudSolrClient.java | 17 +++++++
.../solrj/impl/CloudSolrClientRoutingTest.java | 59 ++++++++++++++++++++++
3 files changed, 84 insertions(+)
diff --git a/changelog/unreleased/SOLR-18114.yml b/changelog/unreleased/SOLR-18114.yml
new file mode 100644
index 00000000000..cd0e66a96cb
--- /dev/null
+++ b/changelog/unreleased/SOLR-18114.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Fixed CloudSolrClient deleteById failure when routing info is not passed with compositeId router, router.field, and directUpdatesToLeadersOnly enabled
+type: fixed # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+ - name: Matthew Biscocho
+links:
+ - name: SOLR-18114
+ url: https://issues.apache.org/jira/browse/SOLR-18114
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
index 60a80e15634..4f71b0cba79 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
@@ -771,6 +771,9 @@ public abstract class CloudSolrClient extends SolrClient {
"directUpdatesToLeadersOnly==true but could not find leader(s)");
} else {
// we could not find a leader or routes yet - use unoptimized general path
+ log.warn(
+ "No routing info found for update to collection '{}', broadcasting to all shards.",
+ collection);
return null;
}
}
@@ -1801,6 +1804,10 @@ public abstract class CloudSolrClient extends SolrClient {
return results;
}
+ /**
+ * Determines whether an UpdateRequest contains sufficient routing information to identify shard
+ * leaders for direct updates when directUpdatesToLeadersOnly is enabled.
+ */
private static boolean hasInfoToFindLeaders(UpdateRequest updateRequest, String idField) {
final Map<SolrInputDocument, Map<String, Object>> documents = updateRequest.getDocumentsMap();
final Map<String, Map<String, Object>> deleteById = updateRequest.getDeleteByIdMap();
@@ -1823,6 +1830,16 @@ public abstract class CloudSolrClient extends SolrClient {
}
}
+ if (deleteById != null) {
+ for (final Map.Entry<String, Map<String, Object>> entry : deleteById.entrySet()) {
+ final Map<String, Object> params = entry.getValue();
+ if (params == null || params.get(ShardParams._ROUTE_) == null) {
+ // deleteById entry lacks explicit route parameter, can't find leader for it
+ return false;
+ }
+ }
+ }
+
return true;
}
}
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRoutingTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRoutingTest.java
index 242bfd4261e..a2693167833 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRoutingTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRoutingTest.java
@@ -18,17 +18,22 @@ package org.apache.solr.client.solrj.impl;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Optional;
+import org.apache.lucene.tests.util.TestUtil;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.SolrQuery;
+import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ShardParams;
import org.apache.solr.common.params.UpdateParams;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.util.LogListener;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -130,4 +135,58 @@ public final class CloudSolrClientRoutingTest extends SolrCloudTestCase {
assertEquals(0, numForwardedWithRoute);
assertEquals(100, numForwardedWithoutRoute);
}
+
+ @Test
+ public void testDeleteWithoutRouteWithDirectUpdatesToLeadersOnly() throws Exception {
+ final String collectionName = "delete_without_route_collection";
+
+ final String docId = String.valueOf(random().nextInt(1000));
+ final String routeValue = TestUtil.randomRealisticUnicodeString(random());
+
+ CollectionAdminRequest.createCollection(collectionName, "conf", 2, 1)
+ .setRouterField("router_field_s")
+ .process(cluster.getSolrClient());
+ cluster.waitForActiveCollection(collectionName, 2, 2);
+
+ try (CloudSolrClient client =
+ new CloudSolrClient.Builder(
+ Collections.singletonList(cluster.getZkServer().getZkAddress()), Optional.empty())
+ .withDefaultCollection(collectionName)
+ .sendUpdatesOnlyToShardLeaders()
+ .sendDirectUpdatesToShardLeadersOnly()
+ .build()) {
+
+ UpdateRequest addRequest = new UpdateRequest();
+ addRequest.add("id", docId, "router_field_s", routeValue);
+ addRequest.process(client);
+ client.commit();
+
+ assertEquals(1, client.query(new SolrQuery("id:" + docId)).getResults().getNumFound());
+
+ try (LogListener warnLog =
+ LogListener.warn(CloudSolrClient.class)
+ .substring(
+ "No routing info found for update to collection '"
+ + collectionName
+ + "', broadcasting to all shards.")) {
+
+ // Delete by ID without providing explicit route
+ // Should still delete via sending to all shards and log a warning
+ UpdateRequest deleteRequest = new UpdateRequest();
+ deleteRequest.deleteById(docId);
+ deleteRequest.process(client);
+ client.commit();
+
+ assertEquals(0, client.query(new SolrQuery("id:" + docId)).getResults().getNumFound());
+
+ // Verify the warning was logged when routing info was missing
+ assertNotNull(
+ "Expected at least one warning about missing routing info for deleteById",
+ warnLog.pollMessage());
+
+ // Clear any remaining messages
+ warnLog.clearQueue();
+ }
+ }
+ }
}