Skip to content

Commit 07092b9

Browse files
committed
[Controller] add rt topic creation config to set unclean leader election
1 parent 803ded4 commit 07092b9

7 files changed

Lines changed: 112 additions & 4 deletions

File tree

internal/venice-common/src/main/java/com/linkedin/venice/ConfigKeys.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,17 @@ private ConfigKeys() {
310310
*/
311311
public static final String KAFKA_REPLICATION_FACTOR_RT_TOPICS = "kafka.replication.factor.rt.topics";
312312

313+
/**
314+
* Disable unclean leader election for real-time buffer topics.
315+
*
316+
* If set to false, unclean leader election will be disabled for RT topics,
317+
* which prevents data loss at the cost of potential unavailability.
318+
*
319+
* Will use the Kafka cluster's default if not set.
320+
*/
321+
public static final String KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE_RT_TOPICS =
322+
"kafka.unclean.leader.election.enable.rt.topics";
323+
313324
/**
314325
* Cluster-level config to enable active-active replication for new hybrid stores.
315326
*/

internal/venice-common/src/main/java/com/linkedin/venice/pubsub/PubSubTopicConfiguration.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,36 @@ public class PubSubTopicConfiguration implements Cloneable {
1212
Long minLogCompactionLagMs;
1313
Optional<Long> maxLogCompactionLagMs;
1414
Optional<Integer> minInSyncReplicas;
15+
Optional<Boolean> uncleanLeaderElectionEnable;
1516

1617
public PubSubTopicConfiguration(
1718
Optional<Long> retentionInMs,
1819
boolean isLogCompacted,
1920
Optional<Integer> minInSyncReplicas,
2021
Long minLogCompactionLagMs,
2122
Optional<Long> maxLogCompactionLagMs) {
23+
this(
24+
retentionInMs,
25+
isLogCompacted,
26+
minInSyncReplicas,
27+
minLogCompactionLagMs,
28+
maxLogCompactionLagMs,
29+
Optional.empty());
30+
}
31+
32+
public PubSubTopicConfiguration(
33+
Optional<Long> retentionInMs,
34+
boolean isLogCompacted,
35+
Optional<Integer> minInSyncReplicas,
36+
Long minLogCompactionLagMs,
37+
Optional<Long> maxLogCompactionLagMs,
38+
Optional<Boolean> uncleanLeaderElectionEnable) {
2239
this.retentionInMs = retentionInMs;
2340
this.isLogCompacted = isLogCompacted;
2441
this.minInSyncReplicas = minInSyncReplicas;
2542
this.minLogCompactionLagMs = minLogCompactionLagMs;
2643
this.maxLogCompactionLagMs = maxLogCompactionLagMs;
44+
this.uncleanLeaderElectionEnable = uncleanLeaderElectionEnable;
2745
}
2846

2947
/**
@@ -93,15 +111,30 @@ public void setMaxLogCompactionLagMs(Optional<Long> maxLogCompactionLagMs) {
93111
this.maxLogCompactionLagMs = maxLogCompactionLagMs;
94112
}
95113

114+
/**
115+
* @return whether unclean leader election is enabled for this topic
116+
*/
117+
public Optional<Boolean> getUncleanLeaderElectionEnable() {
118+
return uncleanLeaderElectionEnable;
119+
}
120+
121+
/**
122+
* @param uncleanLeaderElectionEnable whether unclean leader election is enabled for this topic
123+
*/
124+
public void setUncleanLeaderElectionEnable(Optional<Boolean> uncleanLeaderElectionEnable) {
125+
this.uncleanLeaderElectionEnable = uncleanLeaderElectionEnable;
126+
}
127+
96128
@Override
97129
public String toString() {
98130
return String.format(
99-
"TopicConfiguration(retentionInMs = %s, isLogCompacted = %s, minInSyncReplicas = %s, minLogCompactionLagMs = %s, maxLogCompactionLagMs = %s)",
131+
"TopicConfiguration(retentionInMs = %s, isLogCompacted = %s, minInSyncReplicas = %s, minLogCompactionLagMs = %s, maxLogCompactionLagMs = %s, uncleanLeaderElectionEnable = %s)",
100132
retentionInMs.isPresent() ? retentionInMs.get() : "not set",
101133
isLogCompacted,
102134
minInSyncReplicas.isPresent() ? minInSyncReplicas.get() : "not set",
103135
minLogCompactionLagMs,
104-
maxLogCompactionLagMs.isPresent() ? maxLogCompactionLagMs.get() : " not set");
136+
maxLogCompactionLagMs.isPresent() ? maxLogCompactionLagMs.get() : "not set",
137+
uncleanLeaderElectionEnable.isPresent() ? uncleanLeaderElectionEnable.get() : "not set");
105138
}
106139

107140
@Override

internal/venice-common/src/main/java/com/linkedin/venice/pubsub/adapter/kafka/admin/ApacheKafkaAdminAdapter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,16 @@ private PubSubTopicConfiguration marshallProperties(Config config) {
401401
Optional<Long> maxLogCompactionLagMs = properties.containsKey(TopicConfig.MAX_COMPACTION_LAG_MS_CONFIG)
402402
? Optional.of(Long.parseLong(properties.getProperty(TopicConfig.MAX_COMPACTION_LAG_MS_CONFIG)))
403403
: Optional.empty();
404+
Optional<Boolean> uncleanLeaderElectionEnable =
405+
Optional.ofNullable(properties.getProperty(TopicConfig.UNCLEAN_LEADER_ELECTION_ENABLE_CONFIG))
406+
.map(Boolean::parseBoolean);
404407
return new PubSubTopicConfiguration(
405408
retentionMs,
406409
isLogCompacted,
407410
minInSyncReplicas,
408411
minLogCompactionLagMs,
409-
maxLogCompactionLagMs);
412+
maxLogCompactionLagMs,
413+
uncleanLeaderElectionEnable);
410414
}
411415

412416
private Properties unmarshallProperties(PubSubTopicConfiguration pubSubTopicConfiguration) {
@@ -433,6 +437,10 @@ private Properties unmarshallProperties(PubSubTopicConfiguration pubSubTopicConf
433437
.ifPresent(
434438
minIsrConfig -> topicProperties
435439
.put(TopicConfig.MIN_IN_SYNC_REPLICAS_CONFIG, Integer.toString(minIsrConfig)));
440+
pubSubTopicConfiguration.getUncleanLeaderElectionEnable()
441+
.ifPresent(
442+
uncleanLeaderElection -> topicProperties
443+
.put(TopicConfig.UNCLEAN_LEADER_ELECTION_ENABLE_CONFIG, Boolean.toString(uncleanLeaderElection)));
436444
// Just in case the Kafka cluster isn't configured as expected.
437445
topicProperties.put(TopicConfig.MESSAGE_TIMESTAMP_TYPE_CONFIG, TimestampType.LOG_APPEND_TIME.toString());
438446
return topicProperties;

internal/venice-common/src/main/java/com/linkedin/venice/pubsub/manager/TopicManager.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,42 @@ public void createTopic(
188188
boolean logCompaction,
189189
Optional<Integer> minIsr,
190190
boolean useFastPubSubOperationTimeout) {
191+
createTopic(
192+
topicName,
193+
numPartitions,
194+
replication,
195+
retentionTimeMs,
196+
logCompaction,
197+
minIsr,
198+
Optional.empty(),
199+
useFastPubSubOperationTimeout);
200+
}
201+
202+
/**
203+
* Create a topic, and block until the topic is created, with a default timeout of
204+
* {@value PubSubConstants#PUBSUB_OPERATION_TIMEOUT_MS_DEFAULT_VALUE}, after which this function will throw a VeniceException.
205+
*
206+
* @param topicName Name for the new topic
207+
* @param numPartitions number of partitions
208+
* @param replication replication factor
209+
* @param retentionTimeMs Retention time, in ms, for the topic
210+
* @param logCompaction whether to enable log compaction on the topic
211+
* @param minIsr if present, will apply the specified min.isr to this topic,
212+
* if absent, PubSub cluster defaults will be used
213+
* @param uncleanLeaderElectionEnable if present, will apply the specified unclean.leader.election.enable to this topic,
214+
* if absent, PubSub cluster defaults will be used
215+
* @param useFastPubSubOperationTimeout if false, normal PubSub operation timeout will be used,
216+
* if true, a much shorter timeout will be used to make topic creation non-blocking.
217+
*/
218+
public void createTopic(
219+
PubSubTopic topicName,
220+
int numPartitions,
221+
int replication,
222+
long retentionTimeMs,
223+
boolean logCompaction,
224+
Optional<Integer> minIsr,
225+
Optional<Boolean> uncleanLeaderElectionEnable,
226+
boolean useFastPubSubOperationTimeout) {
191227
long startTimeMs = System.currentTimeMillis();
192228
long deadlineMs = startTimeMs + (useFastPubSubOperationTimeout
193229
? PUBSUB_FAST_OPERATION_TIMEOUT_MS
@@ -197,7 +233,8 @@ public void createTopic(
197233
logCompaction,
198234
minIsr,
199235
topicManagerContext.getTopicMinLogCompactionLagMs(),
200-
Optional.empty());
236+
Optional.empty(),
237+
uncleanLeaderElectionEnable);
201238
logger.info(
202239
"Creating topic: {} partitions: {} replication: {}, configuration: {}",
203240
topicName,

services/venice-controller/src/main/java/com/linkedin/venice/controller/VeniceControllerClusterConfig.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
import static com.linkedin.venice.ConfigKeys.KAFKA_OVER_SSL;
154154
import static com.linkedin.venice.ConfigKeys.KAFKA_REPLICATION_FACTOR;
155155
import static com.linkedin.venice.ConfigKeys.KAFKA_REPLICATION_FACTOR_RT_TOPICS;
156+
import static com.linkedin.venice.ConfigKeys.KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE_RT_TOPICS;
156157
import static com.linkedin.venice.ConfigKeys.KME_REGISTRATION_FROM_MESSAGE_HEADER_ENABLED;
157158
import static com.linkedin.venice.ConfigKeys.LEAKED_PUSH_STATUS_CLEAN_UP_SERVICE_SLEEP_INTERVAL_MS;
158159
import static com.linkedin.venice.ConfigKeys.LEAKED_RESOURCE_ALLOWED_LINGER_TIME_MS;
@@ -561,6 +562,7 @@ public class VeniceControllerClusterConfig {
561562
private final Optional<Integer> minInSyncReplicas;
562563
private final Optional<Integer> minInSyncReplicasRealTimeTopics;
563564
private final Optional<Integer> minInSyncReplicasAdminTopics;
565+
private final Optional<Boolean> uncleanLeaderElectionEnableRTTopics;
564566
private final boolean kafkaLogCompactionForHybridStores;
565567

566568
/**
@@ -699,6 +701,9 @@ public VeniceControllerClusterConfig(VeniceProperties props) {
699701
this.minInSyncReplicas = props.getOptionalInt(KAFKA_MIN_IN_SYNC_REPLICAS);
700702
this.minInSyncReplicasRealTimeTopics = props.getOptionalInt(KAFKA_MIN_IN_SYNC_REPLICAS_RT_TOPICS);
701703
this.minInSyncReplicasAdminTopics = props.getOptionalInt(KAFKA_MIN_IN_SYNC_REPLICAS_ADMIN_TOPICS);
704+
this.uncleanLeaderElectionEnableRTTopics = props.containsKey(KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE_RT_TOPICS)
705+
? Optional.of(props.getBoolean(KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE_RT_TOPICS))
706+
: Optional.empty();
702707
this.kafkaLogCompactionForHybridStores = props.getBoolean(KAFKA_LOG_COMPACTION_FOR_HYBRID_STORES, true);
703708
this.replicationFactor = props.getInt(DEFAULT_REPLICA_FACTOR);
704709
this.minNumberOfPartitions = props.getInt(DEFAULT_NUMBER_OF_PARTITION);
@@ -1503,6 +1508,10 @@ public Optional<Integer> getMinInSyncReplicasAdminTopics() {
15031508
return minInSyncReplicasAdminTopics;
15041509
}
15051510

1511+
public Optional<Boolean> getUncleanLeaderElectionEnableRTTopics() {
1512+
return uncleanLeaderElectionEnableRTTopics;
1513+
}
1514+
15061515
public boolean isKafkaLogCompactionForHybridStoresEnabled() {
15071516
return kafkaLogCompactionForHybridStores;
15081517
}

services/venice-controller/src/main/java/com/linkedin/venice/controller/VeniceHelixAdmin.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3584,6 +3584,7 @@ void createOrUpdateRealTimeTopic(String clusterName, Store store, Version versio
35843584
false,
35853585
// Note: do not enable RT compaction! Might make jobs in Online/Offline model stuck
35863586
clusterConfig.getMinInSyncReplicasRealTimeTopics(),
3587+
clusterConfig.getUncleanLeaderElectionEnableRTTopics(),
35873588
false);
35883589
}
35893590
LOGGER.info(

services/venice-controller/src/main/java/com/linkedin/venice/ingestion/control/RealTimeTopicSwitcher.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static com.linkedin.venice.ConfigKeys.KAFKA_MIN_IN_SYNC_REPLICAS_RT_TOPICS;
44
import static com.linkedin.venice.ConfigKeys.KAFKA_REPLICATION_FACTOR;
55
import static com.linkedin.venice.ConfigKeys.KAFKA_REPLICATION_FACTOR_RT_TOPICS;
6+
import static com.linkedin.venice.ConfigKeys.KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE_RT_TOPICS;
67
import static com.linkedin.venice.VeniceConstants.REWIND_TIME_DECIDED_BY_SERVER;
78
import static com.linkedin.venice.kafka.protocol.enums.ControlMessageType.TOPIC_SWITCH;
89
import static com.linkedin.venice.pubsub.PubSubConstants.DEFAULT_KAFKA_REPLICATION_FACTOR;
@@ -51,6 +52,7 @@ public class RealTimeTopicSwitcher {
5152
private final int kafkaReplicationFactorForRTTopics;
5253
private final int kafkaReplicationFactor;
5354
private final Optional<Integer> minSyncReplicasForRTTopics;
55+
private final Optional<Boolean> uncleanLeaderElectionEnableForRTTopics;
5456

5557
private final PubSubTopicRepository pubSubTopicRepository;
5658

@@ -71,6 +73,10 @@ public RealTimeTopicSwitcher(
7173
this.kafkaReplicationFactorForRTTopics =
7274
veniceProperties.getInt(KAFKA_REPLICATION_FACTOR_RT_TOPICS, kafkaReplicationFactor);
7375
this.minSyncReplicasForRTTopics = veniceProperties.getOptionalInt(KAFKA_MIN_IN_SYNC_REPLICAS_RT_TOPICS);
76+
this.uncleanLeaderElectionEnableForRTTopics =
77+
veniceProperties.containsKey(KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE_RT_TOPICS)
78+
? Optional.of(veniceProperties.getBoolean(KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE_RT_TOPICS))
79+
: Optional.empty();
7480
}
7581

7682
/**
@@ -155,13 +161,16 @@ void createRealTimeTopicIfNeeded(Store store, Version version, PubSubTopic realT
155161
}
156162
int replicationFactor = realTimeTopic.isRealTime() ? kafkaReplicationFactorForRTTopics : kafkaReplicationFactor;
157163
Optional<Integer> minISR = realTimeTopic.isRealTime() ? minSyncReplicasForRTTopics : Optional.empty();
164+
Optional<Boolean> uncleanLeaderElection =
165+
realTimeTopic.isRealTime() ? uncleanLeaderElectionEnableForRTTopics : Optional.empty();
158166
getTopicManager().createTopic(
159167
realTimeTopic,
160168
partitionCount,
161169
replicationFactor,
162170
StoreUtils.getExpectedRetentionTimeInMs(store, store.getHybridStoreConfig()),
163171
false,
164172
minISR,
173+
uncleanLeaderElection,
165174
false);
166175
} else {
167176
/**

0 commit comments

Comments
 (0)