Skip to content

Commit c4816e0

Browse files
committed
Merge remote-tracking branch 'upstream/main' into mnagaraj/addKeyCountMetric
2 parents ffcdc2f + 6574798 commit c4816e0

194 files changed

Lines changed: 15907 additions & 1181 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ Gemfile.lock
4141
docs/vendor/
4242
docs/_site/
4343

44+
.worktrees/

build.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,6 @@ subprojects {
329329
// when actually using the new protocol. Example to pin KME to v12 when introducing v13:
330330
// project(':internal:venice-common').file('src/main/resources/avro/KafkaMessageEnvelope/v12', PathValidation.DIRECTORY)
331331
def versionOverrides = [
332-
project(':internal:venice-common').file('src/main/resources/avro/StoreMetaValue/v40', PathValidation.DIRECTORY),
333-
project(':services:venice-controller').file('src/main/resources/avro/AdminOperation/v95', PathValidation.DIRECTORY)
334332
]
335333

336334
def schemaDirs = [sourceDir]
@@ -645,7 +643,10 @@ spotless {
645643
}
646644
format 'markdown', {
647645
target '**/*.md'
648-
targetExclude '**/build/**', '**/target/**', '**/.gradle/**', 'tests/venice-feature-matrix-test/**'
646+
// docs/operations/** and docs/contributing/** are MkDocs website content formatted by Prettier
647+
// via the MkDocs build pipeline, not by Spotless. Excluding them avoids a known issue where
648+
// `clean check` deletes the spotless npm working directory, causing npm to fail on re-install.
649+
targetExclude '**/build/**', '**/target/**', '**/.gradle/**', 'tests/venice-feature-matrix-test/**', 'docs/**'
649650
prettier(['prettier': '2.8.8', 'prettier-plugin-java': '2.2.0'])
650651
.config(['proseWrap': 'always', 'printWidth': 120])
651652
}

clients/da-vinci-client/src/main/java/com/linkedin/davinci/client/StatsAvroGenericDaVinciClient.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.util.Set;
1212
import java.util.concurrent.CompletableFuture;
1313
import java.util.function.Supplier;
14-
import org.apache.http.HttpStatus;
1514

1615

1716
/**
@@ -65,9 +64,7 @@ private static <T> CompletableFuture<T> trackRequest(
6564
}
6665
});
6766
} catch (Exception e) {
68-
stats.emitUnhealthyRequestMetrics(
69-
LatencyUtils.getElapsedTimeFromNSToMS(startTimeInNS),
70-
HttpStatus.SC_INTERNAL_SERVER_ERROR);
67+
stats.emitUnhealthyRequestMetricsForDavinciClient(LatencyUtils.getElapsedTimeFromNSToMS(startTimeInNS));
7168
throw e;
7269
}
7370
}

clients/da-vinci-client/src/main/java/com/linkedin/davinci/config/VeniceServerConfig.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@
211211
import static com.linkedin.venice.ConfigKeys.SERVER_SSL_HANDSHAKE_THREAD_POOL_SIZE;
212212
import static com.linkedin.venice.ConfigKeys.SERVER_STOP_CONSUMPTION_TIMEOUT_IN_SECONDS;
213213
import static com.linkedin.venice.ConfigKeys.SERVER_STORE_TO_EARLY_TERMINATION_THRESHOLD_MS_MAP;
214+
import static com.linkedin.venice.ConfigKeys.SERVER_STORE_VERSION_METADATA_WAIT_DURING_STATE_TRANSITION_TIME_MS;
214215
import static com.linkedin.venice.ConfigKeys.SERVER_STUCK_CONSUMER_REPAIR_ENABLED;
215216
import static com.linkedin.venice.ConfigKeys.SERVER_STUCK_CONSUMER_REPAIR_INTERVAL_SECOND;
216217
import static com.linkedin.venice.ConfigKeys.SERVER_STUCK_CONSUMER_REPAIR_THRESHOLD_SECOND;
@@ -227,6 +228,7 @@
227228
import static com.linkedin.venice.ConfigKeys.SERVER_USE_HEARTBEAT_LAG_FOR_READY_TO_SERVE_CHECK_ENABLED;
228229
import static com.linkedin.venice.ConfigKeys.SERVER_USE_METRICS_BASED_POSITION_IN_LAG_COMPUTATION;
229230
import static com.linkedin.venice.ConfigKeys.SERVER_USE_UPSTREAM_PUBSUB_POSITIONS;
231+
import static com.linkedin.venice.ConfigKeys.SERVER_VERSION_SWAP_DISK_SIZE_DROP_ALERT_THRESHOLD;
230232
import static com.linkedin.venice.ConfigKeys.SERVER_ZSTD_DICT_COMPRESSION_LEVEL;
231233
import static com.linkedin.venice.ConfigKeys.SEVER_CALCULATE_QUOTA_USAGE_BASED_ON_PARTITIONS_ASSIGNMENT_ENABLED;
232234
import static com.linkedin.venice.ConfigKeys.SORTED_INPUT_DRAINER_SIZE;
@@ -432,6 +434,8 @@ public class VeniceServerConfig extends VeniceClusterConfig {
432434

433435
private final double diskFullThreshold;
434436

437+
private final double versionSwapDiskSizeDropAlertThreshold;
438+
435439
private final int partitionGracefulDropDelaySeconds;
436440

437441
private final int stopConsumptionTimeoutInSeconds;
@@ -468,6 +472,8 @@ public class VeniceServerConfig extends VeniceClusterConfig {
468472

469473
private final Duration serverMaxWaitForVersionInfo;
470474

475+
private final long storeVersionMetadataWaitDuringStateTransitionTimeMs;
476+
471477
private final boolean computeFastAvroEnabled;
472478

473479
private final long participantMessageConsumptionDelayMs;
@@ -871,6 +877,8 @@ public VeniceServerConfig(VeniceProperties serverProperties, Map<String, Map<Str
871877
databaseSyncBytesIntervalForDeferredWriteMode =
872878
serverProperties.getSizeInBytes(SERVER_DATABASE_SYNC_BYTES_INTERNAL_FOR_DEFERRED_WRITE_MODE, 60 * 1024 * 1024);
873879
diskFullThreshold = serverProperties.getDouble(SERVER_DISK_FULL_THRESHOLD, 0.95);
880+
versionSwapDiskSizeDropAlertThreshold =
881+
serverProperties.getDouble(SERVER_VERSION_SWAP_DISK_SIZE_DROP_ALERT_THRESHOLD, 0.5);
874882
partitionGracefulDropDelaySeconds = serverProperties.getInt(SERVER_PARTITION_GRACEFUL_DROP_DELAY_IN_SECONDS, 30);
875883
stopConsumptionTimeoutInSeconds = serverProperties.getInt(SERVER_STOP_CONSUMPTION_TIMEOUT_IN_SECONDS, 180);
876884
leakedResourceCleanUpIntervalInMS =
@@ -890,6 +898,8 @@ public VeniceServerConfig(VeniceProperties serverProperties, Map<String, Map<Str
890898
diskHealthCheckServiceEnabled = serverProperties.getBoolean(SERVER_DISK_HEALTH_CHECK_SERVICE_ENABLED, true);
891899
serverMaxWaitForVersionInfo =
892900
Duration.ofMillis(serverProperties.getLong(SERVER_MAX_WAIT_FOR_VERSION_INFO_MS_CONFIG, 5000));
901+
storeVersionMetadataWaitDuringStateTransitionTimeMs =
902+
serverProperties.getLong(SERVER_STORE_VERSION_METADATA_WAIT_DURING_STATE_TRANSITION_TIME_MS, 300_000);
893903
computeFastAvroEnabled = serverProperties.getBoolean(SERVER_COMPUTE_FAST_AVRO_ENABLED, true);
894904
participantMessageConsumptionDelayMs = serverProperties.getLong(PARTICIPANT_MESSAGE_CONSUMPTION_DELAY_MS, 60000);
895905
serverPromotionToLeaderReplicaDelayMs =
@@ -1455,6 +1465,10 @@ public double getDiskFullThreshold() {
14551465
return diskFullThreshold;
14561466
}
14571467

1468+
public double getVersionSwapDiskSizeDropAlertThreshold() {
1469+
return versionSwapDiskSizeDropAlertThreshold;
1470+
}
1471+
14581472
public int getPartitionGracefulDropDelaySeconds() {
14591473
return partitionGracefulDropDelaySeconds;
14601474
}
@@ -1507,6 +1521,10 @@ public Duration getServerMaxWaitForVersionInfo() {
15071521
return serverMaxWaitForVersionInfo;
15081522
}
15091523

1524+
public long getStoreVersionMetadataWaitDuringStateTransitionTimeMs() {
1525+
return storeVersionMetadataWaitDuringStateTransitionTimeMs;
1526+
}
1527+
15101528
public BlockingQueueType getBlockingQueueType() {
15111529
return blockingQueueType;
15121530
}

clients/da-vinci-client/src/main/java/com/linkedin/davinci/helix/AbstractPartitionStateModel.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
import com.linkedin.venice.pushmonitor.ExecutionStatus;
1616
import com.linkedin.venice.pushmonitor.HybridStoreQuotaStatus;
1717
import com.linkedin.venice.utils.LogContext;
18+
import com.linkedin.venice.utils.RetryUtils;
1819
import com.linkedin.venice.utils.Timer;
1920
import com.linkedin.venice.utils.Utils;
21+
import java.time.Duration;
22+
import java.util.Collections;
2023
import java.util.Optional;
2124
import java.util.concurrent.CompletableFuture;
2225
import java.util.concurrent.TimeUnit;
@@ -223,10 +226,45 @@ public void reset() {
223226
}
224227
}
225228

229+
/**
230+
* Waits for the version metadata to become available in the store repository using exponential backoff.
231+
* During OFFLINE->STANDBY transitions, version info may not have propagated from ZK yet.
232+
* This must be called before the storage engine is created so that the correct partition type
233+
* (with or without replication metadata) is used.
234+
*/
235+
protected void waitForVersionToBeAvailable() {
236+
String storeVersion = Version.composeKafkaTopic(storeName, versionNumber);
237+
long waitTimeMs = storeAndServerConfigs.getStoreVersionMetadataWaitDuringStateTransitionTimeMs();
238+
try {
239+
RetryUtils.executeWithMaxAttemptAndExponentialBackoff(() -> {
240+
Version version = storeRepository.getStoreOrThrow(storeName).getVersion(versionNumber);
241+
if (version == null) {
242+
throw new VeniceException(storeVersion + " not yet available in store repository");
243+
}
244+
},
245+
Integer.MAX_VALUE,
246+
Duration.ofMillis(10),
247+
Duration.ofMillis(200),
248+
Duration.ofMillis(waitTimeMs),
249+
Collections.singletonList(VeniceException.class));
250+
} catch (Exception e) {
251+
String errorMsg = storeVersion + " did not become available in store repository within " + waitTimeMs + " ms.";
252+
logger.error(errorMsg, e);
253+
throw new VeniceException(errorMsg, e);
254+
}
255+
}
256+
226257
/**
227258
* set up a new store partition and start the ingestion
228259
*/
229260
protected void setupNewStorePartition() {
261+
/**
262+
* Wait for version metadata to become available in the store repository before opening the
263+
* storage engine. This ensures the correct partition type (with or without replication metadata)
264+
* is determined based on version-level config rather than falling back to store-level config.
265+
*/
266+
waitForVersionToBeAvailable();
267+
230268
/**
231269
* Waiting for push accessor to get initialized before starting ingestion.
232270
* Otherwise, it's possible that store ingestion starts without having the

clients/da-vinci-client/src/main/java/com/linkedin/davinci/kafka/consumer/LeaderFollowerStoreIngestionTask.java

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,11 +2069,10 @@ protected void produceToLocalKafka(
20692069
partition,
20702070
kafkaUrl,
20712071
beforeProcessingRecordTimestampNs,
2072-
leaderMetadataWrapper,
2073-
leaderProducedRecordContext);
2072+
kafkaClusterId);
20742073
}
20752074
} catch (Exception e) {
2076-
LOGGER.error("Failed to send Global RT DIV message", e); // don't fail ingestion if sending Global RT DIV fails
2075+
LOGGER.error("Failed to send Global RT DIV message", e);
20772076
}
20782077
}
20792078

@@ -2732,9 +2731,13 @@ protected Iterable<DefaultPubSubMessage> validateAndFilterOutDuplicateMessagesFr
27322731
/**
27332732
* TODO: An improvement can be made to fail all future versions for fatal DIV exceptions after EOP.
27342733
*/
2734+
// shouldProduceToVersionTopic() means this leader is consuming from a non-local-VT source
2735+
// (e.g. RT, remote VT in NR mode, or stream-reprocessing topic) and producing to local VT.
2736+
// For RT, use REALTIME_TOPIC_TYPE so segments are tracked per broker URL in rtSegments.
2737+
// For all other cases keep VERSION_TOPIC so segments are tracked in vtSegments and synced to OffsetRecord.
27352738
TopicType topicType = PartitionTracker.VERSION_TOPIC;
2736-
// shouldProduceToVersionTopic() ensures this is a LEADER that is consuming from RT or remote VT
2737-
if (isGlobalRtDivEnabled() && shouldProduceToVersionTopic(pcs)) {
2739+
if (isGlobalRtDivEnabled() && shouldProduceToVersionTopic(pcs)
2740+
&& topicPartition.getPubSubTopic().isRealTime()) {
27382741
topicType = TopicType.of(REALTIME_TOPIC_TYPE, kafkaUrl);
27392742
}
27402743
validateMessage(topicType, consumerDiv, record, pcs, false);
@@ -3158,12 +3161,20 @@ void syncOffsetFromSnapshotIfNeeded(DefaultPubSubMessage record, PubSubTopicPart
31583161

31593162
try {
31603163
// VT DIV contains the latest consumed VT position (LCVP)
3161-
PartitionTracker vtDiv = consumerDiv.cloneVtProducerStates(partition, true);
3164+
long latestMessageTimeInMs = pcs.getLatestMessageTimeInMs();
3165+
PartitionTracker vtDiv = getConsumerDiv().cloneVtProducerStates(partition, true, latestMessageTimeInMs);
3166+
3167+
// Skip sync if no real VT progress has been made yet. Syncing with EARLIEST would persist
3168+
// EARLIEST to the OffsetRecord, causing the consumer to re-subscribe from EARLIEST on retry.
3169+
if (PubSubSymbolicPosition.EARLIEST.equals(vtDiv.getLatestConsumedVtPosition())) {
3170+
return;
3171+
}
3172+
31623173
CompletableFuture<Void> lastFuture = pcs.getLastQueuedRecordPersistedFuture();
31633174
storeBufferService.execSyncOffsetFromSnapshotAsync(topicPartition, vtDiv, lastFuture, this);
31643175
// Reset consumer-side VT bytes so the size-based condition in shouldSyncOffsetFromSnapshot does not keep
31653176
// firing for every subsequent record.
3166-
getConsumedBytesSinceLastSync().put(getVersionTopic().getName(), 0L);
3177+
pcs.resetConsumedBytesSinceLastGlobalRtDivSync(getVersionTopic().getName());
31673178

31683179
// TODO: remove. this is a temporary log for debugging while the feature is in its infancy
31693180
LOGGER.info(
@@ -3199,7 +3210,7 @@ boolean shouldSyncOffsetFromSnapshot(DefaultPubSubMessage consumerRecord, Partit
31993210

32003211
// must be greater than the interval in shouldSendGlobalRtDiv() to not interfere
32013212
final long syncBytesInterval = getSyncBytesInterval(pcs); // size-based sync condition
3202-
long vtConsumedBytesSinceLastSync = getConsumedBytesSinceLastSync().getOrDefault(getVersionTopic().getName(), 0L);
3213+
long vtConsumedBytesSinceLastSync = pcs.getConsumedBytesSinceLastGlobalRtDivSync(getVersionTopic().getName());
32033214
return syncBytesInterval > 0 && (vtConsumedBytesSinceLastSync >= 2 * syncBytesInterval);
32043215
}
32053216

@@ -3897,24 +3908,27 @@ public byte[] getGlobalRtDivKeyBytes(int partitionId, String brokerUrl) {
38973908
* Upon completion, the {@link LeaderProducerCallback} will write the {@link GlobalRtDivState} to the StorageEngine.
38983909
* When the drainer receives a Global RT DIV, that is the signal to sync the VT DIV to the OffsetRecord.
38993910
* NOTE: This method is called per-broker. The broker url is included in the key.
3900-
* @param previousMessage the last message validated and produced to kafka before this GlobalRtDiv will be produced
3911+
* @param previousMessage the last RT message that was validated and produced to kafka before this GlobalRtDiv
3912+
* will be produced.
39013913
*/
39023914
void sendGlobalRtDivMessage(
39033915
DefaultPubSubMessage previousMessage,
39043916
PartitionConsumptionState pcs,
39053917
int partition,
39063918
String brokerUrl,
39073919
long beforeProcessingRecordTimestampNs,
3908-
LeaderMetadataWrapper leaderMetadataWrapper,
3909-
LeaderProducedRecordContext context) {
3920+
int kafkaClusterId) {
39103921
final byte[] keyBytes = getGlobalRtDivKeyBytes(partition, brokerUrl);
39113922
final PubSubTopicPartition topicPartition = previousMessage.getTopicPartition();
39123923
TopicType realTimeTopicType = TopicType.of(REALTIME_TOPIC_TYPE, brokerUrl);
3924+
LeaderMetadataWrapper leaderMetadataWrapper =
3925+
new LeaderMetadataWrapper(previousMessage.getPosition(), kafkaClusterId, DEFAULT_TERM_ID);
39133926

39143927
// Snapshot the RT DIV (single broker URL) in preparation to be produced
39153928
// VT DIV contains the latest consumed VT position (LCVP)
3916-
PartitionTracker vtDiv = consumerDiv.cloneVtProducerStates(partition, true);
3917-
PartitionTracker rtDiv = consumerDiv.cloneRtProducerStates(partition, brokerUrl);
3929+
long latestMessageTimeInMs = pcs.getLatestMessageTimeInMs();
3930+
PartitionTracker vtDiv = consumerDiv.cloneVtProducerStates(partition, true, latestMessageTimeInMs);
3931+
PartitionTracker rtDiv = consumerDiv.cloneRtProducerStates(partition, brokerUrl, latestMessageTimeInMs);
39183932
Map<CharSequence, ProducerPartitionState> rtDivPartitionStates = rtDiv.getPartitionStates(realTimeTopicType);
39193933

39203934
// Create GlobalRtDivState (RT DIV + LCRP) and serialize into a byte array. Try compression.
@@ -3927,7 +3941,7 @@ void sendGlobalRtDivMessage(
39273941
partition,
39283942
brokerUrl,
39293943
beforeProcessingRecordTimestampNs,
3930-
context,
3944+
kafkaClusterId,
39313945
keyBytes,
39323946
valueBytes,
39333947
topicPartition,
@@ -3971,7 +3985,7 @@ void sendGlobalRtDivMessage(
39713985
null,
39723986
true);
39733987

3974-
consumedBytesSinceLastSync.put(brokerUrl, 0L); // reset the timer for the next sync, since RT DIV was just synced
3988+
pcs.resetConsumedBytesSinceLastGlobalRtDivSync(brokerUrl);
39753989
}
39763990

39773991
private byte[] createGlobalRtDivValueBytes(
@@ -4000,7 +4014,7 @@ private LeaderProducerCallback createGlobalRtDivCallback(
40004014
int partition,
40014015
String brokerUrl,
40024016
long beforeProcessingRecordTimestampNs,
4003-
LeaderProducedRecordContext prevContext,
4017+
int kafkaClusterId,
40044018
byte[] keyBytes,
40054019
byte[] valueBytes,
40064020
PubSubTopicPartition topicPartition,
@@ -4027,8 +4041,8 @@ private LeaderProducerCallback createGlobalRtDivCallback(
40274041
prevMessage.getPosition(),
40284042
System.currentTimeMillis(),
40294043
divKey.getKeyLength() + valueBytes.length);
4030-
LeaderProducedRecordContext context = LeaderProducedRecordContext
4031-
.newPutRecord(prevContext.getConsumedKafkaClusterId(), prevContext.getConsumedPosition(), keyBytes, put);
4044+
LeaderProducedRecordContext context =
4045+
LeaderProducedRecordContext.newPutRecord(kafkaClusterId, prevMessage.getPosition(), keyBytes, put);
40324046
LeaderProducerCallback divCallback =
40334047
createProducerCallback(divMessage, pcs, context, partition, brokerUrl, beforeProcessingRecordTimestampNs);
40344048

clients/da-vinci-client/src/main/java/com/linkedin/davinci/kafka/consumer/PartitionConsumptionState.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ enum LatchStatus {
164164
*/
165165
private long processedRecordSizeSinceLastSync;
166166

167+
/**
168+
* Tracks bytes consumed per source key (VT name or RT broker URL) since the last Global RT DIV sync.
169+
* Stored per-partition so that each partition's sync cadence is independent.
170+
*/
171+
private final Map<String, Long> consumedBytesSinceLastGlobalRtDivSync = new VeniceConcurrentHashMap<>();
172+
167173
/** Minimum lgK supported by DataSketches HllSketch (mirrors package-private HllUtil.MIN_LOG_K). */
168174
static final int HLL_MIN_LOG_K = 4;
169175
/** Maximum lgK supported by DataSketches HllSketch (mirrors package-private HllUtil.MAX_LOG_K). */
@@ -629,6 +635,10 @@ public OffsetRecord getOffsetRecord() {
629635
return this.offsetRecord;
630636
}
631637

638+
public long getLatestMessageTimeInMs() {
639+
return this.offsetRecord.calculateLatestMessageTimeInMs();
640+
}
641+
632642
public void setDeferredWrite(boolean deferredWrite) {
633643
this.deferredWrite = deferredWrite;
634644
}
@@ -760,6 +770,8 @@ public String toString() {
760770
.append(leaderFollowerState)
761771
.append(", leaderCompleteState=")
762772
.append(leaderCompleteState)
773+
.append(", lastLeaderCompleteStateUpdateInMs=")
774+
.append(lastLeaderCompleteStateUpdateInMs)
763775
.append(", consumeRemotely=")
764776
.append(consumeRemotely)
765777
.append(", latestMessageConsumedTimestampInMs=")
@@ -782,6 +794,21 @@ public void resetProcessedRecordSizeSinceLastSync() {
782794
this.processedRecordSizeSinceLastSync = 0;
783795
}
784796

797+
public long getConsumedBytesSinceLastGlobalRtDivSync(String key) {
798+
return consumedBytesSinceLastGlobalRtDivSync.getOrDefault(key, 0L);
799+
}
800+
801+
public void addConsumedBytesSinceLastGlobalRtDivSync(String key, long bytes) {
802+
if (bytes <= 0) {
803+
return;
804+
}
805+
consumedBytesSinceLastGlobalRtDivSync.merge(key, bytes, Long::sum);
806+
}
807+
808+
public void resetConsumedBytesSinceLastGlobalRtDivSync(String key) {
809+
consumedBytesSinceLastGlobalRtDivSync.put(key, 0L);
810+
}
811+
785812
public void setLeaderFollowerState(LeaderFollowerStateType state) {
786813
this.leaderFollowerState = state;
787814
}

0 commit comments

Comments
 (0)