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
The following commit(s) were added to refs/heads/branch_10x by this push:
new 2c426f9a943 SOLR-18062: Support arbitrary Kafka properties via env and
sysprops (#4185)
2c426f9a943 is described below
commit 2c426f9a94336930c44c6af4f1760e8b76cab405
Author: Gaurav Singh <[email protected]>
AuthorDate: Mon Aug 31 09:07:04 2026 -0700
SOLR-18062: Support arbitrary Kafka properties via env and sysprops (#4185)
Co-authored-by: openworld-maker
<[email protected]>
Co-authored-by: Eric Pugh <[email protected]>
(cherry picked from commit 4551969baf1dd3b1ff2fcbf1affb0e17ae800085)
---
.../unreleased/{solr-18062.yml => SOLR-18062.yml} | 0
.../org/apache/solr/crossdc/common/ConfUtil.java | 76 +++++++++++++++-----
.../apache/solr/crossdc/common/ConfUtilTest.java | 81 +++++++++++++++++++++-
.../pages/cross-dc-replication.adoc | 4 ++
4 files changed, 144 insertions(+), 17 deletions(-)
diff --git a/changelog/unreleased/solr-18062.yml
b/changelog/unreleased/SOLR-18062.yml
similarity index 100%
rename from changelog/unreleased/solr-18062.yml
rename to changelog/unreleased/SOLR-18062.yml
diff --git
a/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/common/ConfUtil.java
b/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/common/ConfUtil.java
index 924f2be802b..ab831845096 100644
---
a/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/common/ConfUtil.java
+++
b/solr/modules/cross-dc/src/java/org/apache/solr/crossdc/common/ConfUtil.java
@@ -22,6 +22,7 @@ import static
org.apache.solr.crossdc.common.KafkaCrossDcConf.TOPIC_NAME;
import java.io.ByteArrayInputStream;
import java.lang.invoke.MethodHandles;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
@@ -53,12 +54,7 @@ public class ConfUtil {
}
}
// fill in aux Kafka env with prefix
- env.forEach(
- (key, val) -> {
- if (key.startsWith(KAFKA_ENV_PREFIX)) {
- properties.put(normalizeKafkaEnvKey(key), val);
- }
- });
+ addAdditionalKafkaProperties(properties, env, System.getProperties());
// fill in from system properties
for (ConfigProperty configKey : KafkaCrossDcConf.CONFIG_PROPERTIES) {
@@ -67,14 +63,6 @@ public class ConfUtil {
properties.put(configKey.getKey(), val);
}
}
- // fill in aux Kafka system properties with prefix
- System.getProperties()
- .forEach(
- (key, val) -> {
- if (key.toString().startsWith(KAFKA_PROP_PREFIX)) {
- properties.put(normalizeKafkaSysPropKey(key.toString()), val);
- }
- });
Properties zkProps = new Properties();
if (solrClient != null) {
@@ -117,15 +105,56 @@ public class ConfUtil {
Set<String> keys = new HashSet<>(properties.keySet());
keys.forEach(
key -> {
+ Object value = properties.get(key);
if (key.startsWith(KAFKA_ENV_PREFIX)) {
- properties.put(normalizeKafkaEnvKey(key),
properties.remove(key));
+ properties.remove(key);
+ putIfNonBlankAndMissing(properties, normalizeKafkaEnvKey(key),
value);
} else if (key.startsWith(KAFKA_PROP_PREFIX)) {
- properties.put(normalizeKafkaSysPropKey(key),
properties.remove(key));
+ properties.remove(key);
+ putIfNonBlankAndMissing(properties,
normalizeKafkaSysPropKey(key), value);
}
});
}
}
+ // System properties override environment variables for pass-through Kafka
properties.
+ // Existing explicit keys in properties are preserved.
+ static void addAdditionalKafkaProperties(
+ Map<String, Object> properties, Map<String, String> env, Properties
sysProps) {
+ Set<String> envDerivedKeys = new LinkedHashSet<>();
+ env.forEach(
+ (key, val) -> {
+ if (!key.startsWith(KAFKA_ENV_PREFIX)) {
+ return;
+ }
+ String normalized = normalizeKafkaEnvKey(key);
+ if (!isValidAdditionalProperty(normalized, val)) {
+ return;
+ }
+ Object existingValue = properties.get(normalized);
+ if (isBlankValue(existingValue)) {
+ properties.put(normalized, val);
+ envDerivedKeys.add(normalized);
+ }
+ });
+
+ sysProps.forEach(
+ (key, val) -> {
+ String propKey = key.toString();
+ if (!propKey.startsWith(KAFKA_PROP_PREFIX)) {
+ return;
+ }
+ String normalized = normalizeKafkaSysPropKey(propKey);
+ if (!isValidAdditionalProperty(normalized, val)) {
+ return;
+ }
+ Object existingValue = properties.get(normalized);
+ if (isBlankValue(existingValue) ||
envDerivedKeys.contains(normalized)) {
+ properties.put(normalized, val.toString());
+ }
+ });
+ }
+
public static String normalizeKafkaEnvKey(String key) {
if (key.startsWith(KAFKA_ENV_PREFIX)) {
return
key.substring(KAFKA_ENV_PREFIX.length()).toLowerCase(Locale.ROOT).replace('_',
'.');
@@ -142,6 +171,21 @@ public class ConfUtil {
}
}
+ private static boolean isValidAdditionalProperty(String key, Object value) {
+ return key != null && !key.isBlank() && !isBlankValue(value);
+ }
+
+ private static boolean isBlankValue(Object value) {
+ return value == null || (value instanceof String && ((String)
value).isBlank());
+ }
+
+ private static void putIfNonBlankAndMissing(
+ Map<String, Object> properties, String key, Object value) {
+ if (isValidAdditionalProperty(key, value) && properties.get(key) == null) {
+ properties.put(key, value);
+ }
+ }
+
public static void verifyProperties(Map<String, Object> properties) {
if (properties.get(BOOTSTRAP_SERVERS) == null) {
log.error(
diff --git
a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/ConfUtilTest.java
b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/ConfUtilTest.java
index 53713cdfc12..6afcc35ae2a 100644
---
a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/ConfUtilTest.java
+++
b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/ConfUtilTest.java
@@ -150,7 +150,7 @@ public class ConfUtilTest extends SolrTestCaseJ4 {
public void testFillProperties_PriorityOrder() throws Exception {
Map<String, Object> properties = new HashMap<>();
- // Set up ZooKeeper with lowest priority
+ // Set up ZooKeeper with the lowest priority
Properties zkProps = new Properties();
zkProps.setProperty(KafkaCrossDcConf.BOOTSTRAP_SERVERS, "zk-kafka:9092");
zkProps.setProperty(KafkaCrossDcConf.TOPIC_NAME, "zk-topic");
@@ -347,6 +347,85 @@ public class ConfUtilTest extends SolrTestCaseJ4 {
assertEquals("zk-value", properties.get("zk.only.property"));
}
+ @Test
+ public void testAddAdditionalKafkaProperties_MapsEnvKey() {
+ Map<String, Object> properties = new HashMap<>();
+ Map<String, String> env = Map.of("SOLR_CROSSDC_KAFKA_FOO_BAR",
"env-value");
+
+ ConfUtil.addAdditionalKafkaProperties(properties, env, new Properties());
+
+ assertEquals("env-value", properties.get("foo.bar"));
+ }
+
+ @Test
+ public void testAddAdditionalKafkaProperties_MapsSysPropKey() {
+ Map<String, Object> properties = new HashMap<>();
+ Properties sysProps = new Properties();
+ sysProps.setProperty("solr.crossdc.kafka.foo.bar", "sys-value");
+
+ ConfUtil.addAdditionalKafkaProperties(properties, Map.of(), sysProps);
+
+ assertEquals("sys-value", properties.get("foo.bar"));
+ }
+
+ @Test
+ public void testAddAdditionalKafkaProperties_SysPropsOverrideEnv() {
+ Map<String, Object> properties = new HashMap<>();
+ Map<String, String> env = Map.of("SOLR_CROSSDC_KAFKA_FOO_BAR",
"env-value");
+ Properties sysProps = new Properties();
+ sysProps.setProperty("solr.crossdc.kafka.foo.bar", "sys-value");
+
+ ConfUtil.addAdditionalKafkaProperties(properties, env, sysProps);
+
+ assertEquals("sys-value", properties.get("foo.bar"));
+ }
+
+ @Test
+ public void
testAddAdditionalKafkaProperties_DoesNotOverrideExplicitProperty() {
+ Map<String, Object> properties = new HashMap<>();
+ properties.put("foo.bar", "explicit");
+ Map<String, String> env = Map.of("SOLR_CROSSDC_KAFKA_FOO_BAR",
"env-value");
+ Properties sysProps = new Properties();
+ sysProps.setProperty("solr.crossdc.kafka.foo.bar", "sys-value");
+
+ ConfUtil.addAdditionalKafkaProperties(properties, env, sysProps);
+
+ assertEquals("explicit", properties.get("foo.bar"));
+ }
+
+ @Test
+ public void
testAddAdditionalKafkaProperties_IgnoresBlankValuesAndEmptyKeys() {
+ Map<String, Object> properties = new HashMap<>();
+ Map<String, String> env = new HashMap<>();
+ env.put("SOLR_CROSSDC_KAFKA_", "ignored-empty-key");
+ env.put("SOLR_CROSSDC_KAFKA_FOO_BAR", "");
+ env.put("SOLR_CROSSDC_KAFKA_BAZ_QUX", " ");
+ Properties sysProps = new Properties();
+ sysProps.setProperty("solr.crossdc.kafka.", "ignored-empty-key");
+ sysProps.setProperty("solr.crossdc.kafka.foo.bar", "");
+ sysProps.setProperty("solr.crossdc.kafka.baz.qux", " ");
+
+ ConfUtil.addAdditionalKafkaProperties(properties, env, sysProps);
+
+ assertTrue(properties.isEmpty());
+ }
+
+ @Test
+ public void
testFillProperties_PassThroughPreservedInKafkaCrossDcConfAdditionalProperties()
{
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(KafkaCrossDcConf.BOOTSTRAP_SERVERS, "localhost:9092");
+ properties.put(KafkaCrossDcConf.TOPIC_NAME, "test-topic");
+ Map<String, String> env = Map.of("SOLR_CROSSDC_KAFKA_MAX_POLL_RECORDS",
"500");
+ Properties sysProps = new Properties();
+ sysProps.setProperty("solr.crossdc.kafka.compression.type", "lz4");
+
+ ConfUtil.addAdditionalKafkaProperties(properties, env, sysProps);
+ KafkaCrossDcConf conf = new KafkaCrossDcConf(properties);
+
+ assertEquals(500, conf.getAdditionalProperties().get("max.poll.records"));
+ assertEquals("lz4",
conf.getAdditionalProperties().get("compression.type"));
+ }
+
// we can't easily modify envvars, test just the key conversion in properties
@Test
public void testUnderscoreToDotsConversion() {
diff --git
a/solr/solr-ref-guide/modules/deployment-guide/pages/cross-dc-replication.adoc
b/solr/solr-ref-guide/modules/deployment-guide/pages/cross-dc-replication.adoc
index 4a00d24fde7..6b19d64dd43 100644
---
a/solr/solr-ref-guide/modules/deployment-guide/pages/cross-dc-replication.adoc
+++
b/solr/solr-ref-guide/modules/deployment-guide/pages/cross-dc-replication.adoc
@@ -144,6 +144,10 @@ environment variables starting with `SOLR_CROSSDC_KAFKA_`
or system properties s
`solr.crossdc.kafka.`. For example, to adjust the `max.poll.records` property
specific to the Kafka client
library you can set either `SOLR_CROSSDC_KAFKA_MAX_POLL_RECORDS` environment
variable or `solr.crossdc.kafka.max.poll.records`
system property.
+Property names from environment variables are normalized by stripping the
prefix, converting to lowercase, and replacing `_` with `.`.
+Values from `solr.crossdc.kafka.` system properties override values from
`SOLR_CROSSDC_KAFKA_` environment variables when they map to the same Kafka key.
+Already-populated explicit CrossDC settings are preserved and are not
overridden by pass-through values.
+Blank values and empty normalized keys are ignored.
=== CrossDC Manager