This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 91908f28a964e06dbf0747bc4d5ab29016683359
Author: Andrzej BiaƂecki <[email protected]>
AuthorDate: Fri Sep 4 13:23:04 2026 +0200

    SOLR-18410: CrossDC Common: multivalued params and per-entry params don't 
survive the serialization round-trip (#4867)
    
    (cherry picked from commit 9197e3681df2c302019ec76cecec4800bb4571b5)
---
 changelog/unreleased/solr-18410.yml                |   8 ++
 .../common/MirroredSolrRequestSerializer.java      |  44 ++++++--
 .../common/MirroredSolrRequestSerializerTest.java  | 111 +++++++++++++++++++++
 3 files changed, 157 insertions(+), 6 deletions(-)

diff --git a/changelog/unreleased/solr-18410.yml 
b/changelog/unreleased/solr-18410.yml
new file mode 100644
index 00000000000..7ef53e52860
--- /dev/null
+++ b/changelog/unreleased/solr-18410.yml
@@ -0,0 +1,8 @@
+title: CrossDC Common multivalued params and per-entry params don't survive 
the serialization round-trip
+  
+type: fixed
+authors:
+  - name: Andrzej Bialecki
+links:
+  - name: SOLR-18410
+    url: https://issues.apache.org/jira/browse/SOLR-18410
diff --git 
a/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/common/MirroredSolrRequestSerializer.java
 
b/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/common/MirroredSolrRequestSerializer.java
index d35d1743338..4bac11fea92 100644
--- 
a/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/common/MirroredSolrRequestSerializer.java
+++ 
b/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/common/MirroredSolrRequestSerializer.java
@@ -29,14 +29,16 @@ import org.apache.kafka.common.serialization.Serializer;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrResponse;
 import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.params.CollectionParams;
 import org.apache.solr.common.params.CoreAdminParams;
-import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.ShardParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.CollectionUtil;
 import org.apache.solr.common.util.ContentStream;
 import org.apache.solr.common.util.JavaBinCodec;
+import org.apache.solr.common.util.NamedList;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -84,7 +86,7 @@ public class MirroredSolrRequestSerializer
         
Long.parseLong(String.valueOf(requestMap.getOrDefault("submitTimeNanos", 
"-1")));
     SolrParams params;
     if (requestMap.get("params") != null) {
-      params = new MapSolrParams((Map<String, String>) 
requestMap.get("params"));
+      params = new NamedList<>((Map<String, Object>) 
requestMap.get("params")).toSolrParams();
     } else {
       params = new ModifiableSolrParams();
     }
@@ -93,7 +95,18 @@ public class MirroredSolrRequestSerializer
       UpdateRequest updateRequest = (UpdateRequest) request;
       List docs = (List) requestMap.get("docs");
       if (docs != null) {
-        updateRequest.add(docs);
+        List<Map<String, Object>> docsParams =
+            (List<Map<String, Object>>) requestMap.get("docsParams");
+        if (docsParams.size() != docs.size()) {
+          throw new RuntimeException("docs and docsParams size mismatch");
+        }
+        for (int i = 0; i < docs.size(); i++) {
+          Map<String, Object> docParams = docsParams.get(i);
+          updateRequest.add(
+              (SolrInputDocument) docs.get(i),
+              docParams == null ? null : (Integer) 
docParams.get(UpdateRequest.COMMIT_WITHIN),
+              docParams == null ? null : (Boolean) 
docParams.get(UpdateRequest.OVERWRITE));
+        }
       } else {
         updateRequest.add("id", "1"); // TODO huh?
         updateRequest.getDocumentsMap().clear();
@@ -101,7 +114,18 @@ public class MirroredSolrRequestSerializer
 
       List<String> deletes = (List<String>) requestMap.get("deletes");
       if (deletes != null) {
-        updateRequest.deleteById(deletes);
+        List<Map<String, Object>> deletesParams =
+            (List<Map<String, Object>>) requestMap.get("deletesParams");
+        if (deletesParams.size() != deletes.size()) {
+          throw new RuntimeException("deletes and deletesParams size 
mismatch");
+        }
+        for (int i = 0; i < deletes.size(); i++) {
+          Map<String, Object> deleteParams = deletesParams.get(i);
+          updateRequest.deleteById(
+              deletes.get(i),
+              deleteParams == null ? null : (String) 
deleteParams.get(ShardParams._ROUTE_),
+              deleteParams == null ? null : (Long) 
deleteParams.get(UpdateRequest.VER));
+        }
       }
 
       List<String> deletesQuery = (List<String>) requestMap.get("deleteQuery");
@@ -185,8 +209,16 @@ public class MirroredSolrRequestSerializer
       map.put("params", solrRequest.getParams());
       map.put("type", request.getType().toString());
       if (solrRequest instanceof UpdateRequest update) {
-        map.put("docs", update.getDocuments());
-        map.put("deletes", update.getDeleteById());
+        Map<SolrInputDocument, Map<String, Object>> docsMap = 
update.getDocumentsMap();
+        if (docsMap != null && !docsMap.isEmpty()) {
+          map.put("docs", docsMap.keySet());
+          map.put("docsParams", update.getDocumentsMap().values());
+        }
+        Map<String, Map<String, Object>> deletes = update.getDeleteByIdMap();
+        if (deletes != null && !deletes.isEmpty()) {
+          map.put("deletes", deletes.keySet());
+          map.put("deletesParams", deletes.values());
+        }
         map.put("deleteQuery", update.getDeleteQuery());
       } else if (solrRequest instanceof 
MirroredSolrRequest.MirroredConfigSetRequest config) {
         map.put("method", config.getMethod().toString());
diff --git 
a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/MirroredSolrRequestSerializerTest.java
 
b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/MirroredSolrRequestSerializerTest.java
index e0afa71cd33..0b211b8bc32 100644
--- 
a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/MirroredSolrRequestSerializerTest.java
+++ 
b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/MirroredSolrRequestSerializerTest.java
@@ -17,10 +17,15 @@
 package org.apache.solr.crossdc.common;
 
 import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
 import org.apache.lucene.tests.util.TestUtil;
 import org.apache.solr.SolrTestCase;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.common.params.SolrParams;
 import org.junit.Test;
 
 public class MirroredSolrRequestSerializerTest extends SolrTestCase {
@@ -55,4 +60,110 @@ public class MirroredSolrRequestSerializerTest extends 
SolrTestCase {
       assertEquals(fieldValue, deserValue);
     }
   }
+
+  @Test
+  public void testMultivaluedParamsRoundTrip() {
+    MirroredSolrRequestSerializer serializer = new 
MirroredSolrRequestSerializer();
+    UpdateRequest req = new UpdateRequest();
+    SolrInputDocument doc = new SolrInputDocument();
+    doc.setField("id", "1");
+    req.add(doc);
+
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.set("q", "single-value");
+    params.add("fq", "a", "b", "c");
+    req.setParams(params);
+
+    MirroredSolrRequest<?> mirroredRequest = new MirroredSolrRequest<>(req);
+    byte[] data = serializer.serialize("test", mirroredRequest);
+    MirroredSolrRequest<?> deserialized = serializer.deserialize("test", data);
+
+    SolrParams deserializedParams = deserialized.getSolrRequest().getParams();
+    assertEquals("single-value", deserializedParams.get("q"));
+    assertArrayEquals(new String[] {"a", "b", "c"}, 
deserializedParams.getParams("fq"));
+  }
+
+  @Test
+  public void testDocsParamsRoundTrip() {
+    MirroredSolrRequestSerializer serializer = new 
MirroredSolrRequestSerializer();
+    UpdateRequest req = new UpdateRequest();
+    SolrInputDocument doc1 = new SolrInputDocument();
+    doc1.setField("id", "1");
+    SolrInputDocument doc2 = new SolrInputDocument();
+    doc2.setField("id", "2");
+    req.add(doc1, 5000, true);
+    req.add(doc2, 1000, false);
+
+    MirroredSolrRequest<?> mirroredRequest = new MirroredSolrRequest<>(req);
+    byte[] data = serializer.serialize("test", mirroredRequest);
+    MirroredSolrRequest<?> deserialized = serializer.deserialize("test", data);
+
+    UpdateRequest deserializedReq = (UpdateRequest) 
deserialized.getSolrRequest();
+    Map<SolrInputDocument, Map<String, Object>> docsMap = 
deserializedReq.getDocumentsMap();
+    assertEquals(2, docsMap.size());
+    for (Map.Entry<SolrInputDocument, Map<String, Object>> entry : 
docsMap.entrySet()) {
+      String id = (String) entry.getKey().getFieldValue("id");
+      Map<String, Object> docParams = entry.getValue();
+      if ("1".equals(id)) {
+        assertEquals(5000, docParams.get(UpdateRequest.COMMIT_WITHIN));
+        assertEquals(Boolean.TRUE, docParams.get(UpdateRequest.OVERWRITE));
+      } else if ("2".equals(id)) {
+        assertEquals(1000, docParams.get(UpdateRequest.COMMIT_WITHIN));
+        assertEquals(Boolean.FALSE, docParams.get(UpdateRequest.OVERWRITE));
+      } else {
+        fail("Unexpected document id: " + id);
+      }
+    }
+  }
+
+  @Test
+  public void testDeletesParamsRoundTrip() {
+    MirroredSolrRequestSerializer serializer = new 
MirroredSolrRequestSerializer();
+    UpdateRequest req = new UpdateRequest();
+    req.deleteById("1", "shard1", 100L);
+    req.deleteById("2", "shard2", 200L);
+
+    MirroredSolrRequest<?> mirroredRequest = new MirroredSolrRequest<>(req);
+    byte[] data = serializer.serialize("test", mirroredRequest);
+    MirroredSolrRequest<?> deserialized = serializer.deserialize("test", data);
+
+    UpdateRequest deserializedReq = (UpdateRequest) 
deserialized.getSolrRequest();
+    Map<String, Map<String, Object>> deleteByIdMap = 
deserializedReq.getDeleteByIdMap();
+    assertEquals(2, deleteByIdMap.size());
+    Map<String, Object> params1 = deleteByIdMap.get("1");
+    assertEquals("shard1", params1.get(ShardParams._ROUTE_));
+    assertEquals(100L, params1.get(UpdateRequest.VER));
+    Map<String, Object> params2 = deleteByIdMap.get("2");
+    assertEquals("shard2", params2.get(ShardParams._ROUTE_));
+    assertEquals(200L, params2.get(UpdateRequest.VER));
+  }
+
+  @Test
+  public void testBothDocsAndDeletesParamsRoundTrip() {
+    MirroredSolrRequestSerializer serializer = new 
MirroredSolrRequestSerializer();
+    UpdateRequest req = new UpdateRequest();
+    SolrInputDocument doc = new SolrInputDocument();
+    doc.setField("id", "1");
+    req.add(doc, 2000, true);
+    req.deleteById("2", "shard1", 50L);
+    req.deleteByQuery("field:value");
+
+    MirroredSolrRequest<?> mirroredRequest = new MirroredSolrRequest<>(req);
+    byte[] data = serializer.serialize("test", mirroredRequest);
+    MirroredSolrRequest<?> deserialized = serializer.deserialize("test", data);
+
+    UpdateRequest deserializedReq = (UpdateRequest) 
deserialized.getSolrRequest();
+    Map<SolrInputDocument, Map<String, Object>> docsMap = 
deserializedReq.getDocumentsMap();
+    assertEquals(1, docsMap.size());
+    Map<String, Object> docParams = docsMap.values().iterator().next();
+    assertEquals(2000, docParams.get(UpdateRequest.COMMIT_WITHIN));
+    assertEquals(Boolean.TRUE, docParams.get(UpdateRequest.OVERWRITE));
+
+    Map<String, Map<String, Object>> deleteByIdMap = 
deserializedReq.getDeleteByIdMap();
+    Map<String, Object> deleteParams = deleteByIdMap.get("2");
+    assertEquals("shard1", deleteParams.get(ShardParams._ROUTE_));
+    assertEquals(50L, deleteParams.get(UpdateRequest.VER));
+
+    assertEquals(List.of("field:value"), deserializedReq.getDeleteQuery());
+  }
 }

Reply via email to