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()); + } }
