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

sigram 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 be283cc89ab SOLR-18409: CrossDC Producer: shouldMirror not whitelisted 
for distrib update requests (#4864)
be283cc89ab is described below

commit be283cc89ab84b2063d0e01635f43c8bacd1ee9e
Author: Andrzej BiaƂecki <[email protected]>
AuthorDate: Mon Sep 7 12:49:40 2026 +0200

    SOLR-18409: CrossDC Producer: shouldMirror not whitelisted for distrib 
update requests (#4864)
---
 changelog/unreleased/solr-18409.yml                |  8 +++
 .../processor/DistributedUpdateProcessor.java      |  2 +-
 .../MirroringUpdateRequestProcessorFactory.java    | 20 +++++-
 ...MirroringUpdateRequestProcessorFactoryTest.java | 71 ++++++++++++++++++++++
 4 files changed, 98 insertions(+), 3 deletions(-)

diff --git a/changelog/unreleased/solr-18409.yml 
b/changelog/unreleased/solr-18409.yml
new file mode 100644
index 00000000000..9a2fc67cdb8
--- /dev/null
+++ b/changelog/unreleased/solr-18409.yml
@@ -0,0 +1,8 @@
+title: SOLR-18409 CrossDC Producer shouldMirror not whitelisted for distrib 
update requests
+  
+type: fixed
+authors:
+  - name: Andrzej Bialecki
+links:
+  - name: SOLR-18409
+    url: https://issues.apache.org/jira/browse/SOLR-18409
diff --git 
a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
 
b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
index fe6f44501e7..787b3292d42 100644
--- 
a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
+++ 
b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
@@ -70,7 +70,7 @@ import org.slf4j.LoggerFactory;
 // asked for
 public class DistributedUpdateProcessor extends UpdateRequestProcessor {
 
-  static final String PARAM_WHITELIST_CTX_KEY =
+  public static final String PARAM_WHITELIST_CTX_KEY =
       DistributedUpdateProcessor.class + "PARAM_WHITELIST_CTX_KEY";
   public static final String DISTRIB_FROM_SHARD = "distrib.from.shard";
   public static final String DISTRIB_FROM_COLLECTION = 
"distrib.from.collection";
diff --git 
a/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/update/processor/MirroringUpdateRequestProcessorFactory.java
 
b/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/update/processor/MirroringUpdateRequestProcessorFactory.java
index b159d060fc0..d82c1053c11 100644
--- 
a/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/update/processor/MirroringUpdateRequestProcessorFactory.java
+++ 
b/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/update/processor/MirroringUpdateRequestProcessorFactory.java
@@ -30,6 +30,7 @@ import static 
org.apache.solr.update.processor.DistributedUpdateProcessor.DISTRI
 import static 
org.apache.solr.update.processor.DistributedUpdateProcessor.DistribPhase;
 import static 
org.apache.solr.update.processor.DistributingUpdateProcessorFactory.DISTRIB_UPDATE_PARAM;
 
+import com.google.common.annotations.VisibleForTesting;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.HashMap;
@@ -49,6 +50,7 @@ import org.apache.solr.crossdc.common.KafkaCrossDcConf;
 import org.apache.solr.crossdc.common.KafkaMirroringSink;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.update.processor.DistributedUpdateProcessorFactory;
 import 
org.apache.solr.update.processor.DocBasedVersionConstraintsProcessorFactory;
 import org.apache.solr.update.processor.UpdateRequestProcessor;
 import org.apache.solr.update.processor.UpdateRequestProcessorFactory;
@@ -207,7 +209,7 @@ public class MirroringUpdateRequestProcessorFactory extends 
UpdateRequestProcess
     // 
core.getResourceLoader().newInstance(RequestMirroringHandler.class.getName(),
     // KafkaRequestMirroringHandler.class);
 
-    conf = new KafkaCrossDcConf(properties);
+    setKafkaCrossDcConf(new KafkaCrossDcConf(properties));
 
     KafkaMirroringSink sink = new KafkaMirroringSink(conf);
 
@@ -215,7 +217,17 @@ public class MirroringUpdateRequestProcessorFactory 
extends UpdateRequestProcess
     core.addCloseHook(new MyCloseHook(closer));
 
     producerMetrics = new 
ProducerMetrics(core.getSolrMetricsContext().getChildContext(this), core);
-    mirroringHandler = new KafkaRequestMirroringHandler(sink);
+    setMirroringHandler(new KafkaRequestMirroringHandler(sink));
+  }
+
+  @VisibleForTesting
+  void setKafkaCrossDcConf(KafkaCrossDcConf conf) {
+    this.conf = conf;
+  }
+
+  @VisibleForTesting
+  void setMirroringHandler(KafkaRequestMirroringHandler mirroringHandler) {
+    this.mirroringHandler = mirroringHandler;
   }
 
   @Override
@@ -231,6 +243,10 @@ public class MirroringUpdateRequestProcessorFactory 
extends UpdateRequestProcess
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, 
"mirroringHandler is null");
     }
 
+    // allow distributed forwarding to other replicas/shards
+    DistributedUpdateProcessorFactory.addParamToDistributedRequestWhitelist(
+        req, SERVER_SHOULD_MIRROR);
+
     // Check if mirroring is disabled in request params, defaults to true
     boolean doMirroring = req.getParams().getBool(SERVER_SHOULD_MIRROR, true);
     boolean mirrorCommits = conf.getBool(MIRROR_COMMITS);
diff --git 
a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/update/processor/MirroringUpdateRequestProcessorFactoryTest.java
 
b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/update/processor/MirroringUpdateRequestProcessorFactoryTest.java
new file mode 100644
index 00000000000..b202c6991fc
--- /dev/null
+++ 
b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/update/processor/MirroringUpdateRequestProcessorFactoryTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.crossdc.update.processor;
+
+import static 
org.apache.solr.crossdc.update.processor.MirroringUpdateRequestProcessorFactory.SERVER_SHOULD_MIRROR;
+import static 
org.apache.solr.update.processor.DistributedUpdateProcessor.PARAM_WHITELIST_CTX_KEY;
+import static org.mockito.Mockito.mock;
+
+import java.util.HashMap;
+import java.util.Set;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.crossdc.common.KafkaCrossDcConf;
+import org.apache.solr.request.SolrQueryRequestBase;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.update.processor.UpdateRequestProcessor;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class MirroringUpdateRequestProcessorFactoryTest extends SolrTestCaseJ4 
{
+
+  @BeforeClass
+  public static void ensureWorkingMockito() {
+    assumeWorkingMockito();
+  }
+
+  /**
+   * getInstance() must whitelist SERVER_SHOULD_MIRROR on the request so that 
when the add/delete is
+   * subsequently forwarded to other replicas/shards by 
DistributedUpdateProcessor, the param is
+   * preserved instead of being stripped, which previously caused the 
receiving replica to re-decide
+   * (and potentially re-mirror) independently. See SOLR-18409.
+   */
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testShouldMirrorParamWhitelists() {
+    MirroringUpdateRequestProcessorFactory factory = new 
MirroringUpdateRequestProcessorFactory();
+    factory.setMirroringHandler(mock(KafkaRequestMirroringHandler.class));
+    factory.setKafkaCrossDcConf(new KafkaCrossDcConf(new HashMap<>()));
+
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.set(SERVER_SHOULD_MIRROR, "false");
+    SolrQueryRequestBase req = new SolrQueryRequestBase(mock(SolrCore.class), 
params) {};
+    SolrQueryResponse rsp = new SolrQueryResponse();
+    UpdateRequestProcessor next = mock(UpdateRequestProcessor.class);
+
+    // this should add the SERVER_SHOULD_MIRROR param to the request context 
whitelist
+    factory.getInstance(req, rsp, next);
+
+    Set<String> whitelist = (Set<String>) 
req.getContext().get(PARAM_WHITELIST_CTX_KEY);
+    assertNotNull(
+        "shouldMirror param must be added to the distributed request 
whitelist", whitelist);
+    assertTrue(
+        "shouldMirror param missing from distributed request whitelist",
+        whitelist.contains(SERVER_SHOULD_MIRROR));
+  }
+}

Reply via email to