Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ public void onPartitionBecomeBootstrapFromPreBootstrap(String partitionName) {

@Override
public void onPartitionBecomeBootstrapFromOffline(String partitionName) {
long transitionStartMs = System.currentTimeMillis();
try {
if (this.blockStateTransitionLatch != null && this.blockStateTransitionLatch.getCount() > 0) {
logger.info("Bootstrapping is waiting for blockStateTransitionLatch...");
Expand Down Expand Up @@ -960,10 +961,13 @@ public void onPartitionBecomeBootstrapFromOffline(String partitionName) {
logger.error("Waiting for state transition to be unblocked was interrupted", e);
} catch (Exception e) {
localPartitionAndState.put(partitionName, ReplicaState.ERROR);
participantMetrics.recordBootstrapFailure(partitionName);
throw e;
}
logger.info("Before setting partition {} to bootstrap", partitionName);
localPartitionAndState.put(partitionName, ReplicaState.BOOTSTRAP);
participantMetrics.recordOfflineToBootstrapDuration(System.currentTimeMillis() - transitionStartMs);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the wait is interrupted, we still record a bootstrap duration. Should we return before these success metrics?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing InterruptedException catch (line 960) intentionally falls through without throw/return — partition still enters BOOTSTRAP state, so recording the duration is correct.

participantMetrics.recordBootstrapStart(partitionName);
participantMetrics.decStateTransitionMetric(partitionName, ReplicaState.OFFLINE, ReplicaState.BOOTSTRAP);
}

Expand All @@ -989,13 +993,16 @@ public void onPartitionBecomeStandbyFromBootstrap(String partitionName) {
} catch (InterruptedException e) {
logger.error("Bootstrap was interrupted on partition {}", partitionName);
localPartitionAndState.put(partitionName, ReplicaState.ERROR);
participantMetrics.recordBootstrapFailure(partitionName);
throw new StateTransitionException("Bootstrap failed or was interrupted", BootstrapFailure);
} catch (StateTransitionException e) {
logger.error("Bootstrap didn't complete on partition {}", partitionName, e);
localPartitionAndState.put(partitionName, ReplicaState.ERROR);
participantMetrics.recordBootstrapFailure(partitionName);
throw e;
}
localPartitionAndState.put(partitionName, ReplicaState.STANDBY);
participantMetrics.recordBootstrapComplete(partitionName);
participantMetrics.decStateTransitionMetric(partitionName, ReplicaState.BOOTSTRAP, ReplicaState.STANDBY);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@

import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
Expand All @@ -38,6 +40,16 @@ class HelixParticipantMetrics {

public final Counter updateDiskCapacityCounter;

// --- State Transition Latency Metrics ---
// Tracks when each partition entered BOOTSTRAP state (epoch ms)
private final ConcurrentHashMap<String, Long> bootstrapStartTimeMs = new ConcurrentHashMap<>();
// Histogram of successful BOOTSTRAP→STANDBY durations in milliseconds
final Histogram bootstrapToStandbyDurationMs;
// Histogram of OFFLINE→BOOTSTRAP transition durations in milliseconds
final Histogram offlineToBootstrapDurationMs;
// Counter for bootstrap failures (partition went to ERROR from BOOTSTRAP)
final Counter bootstrapFailureCount;

final Map<String, Counter> partitionTransitionToCount;

HelixParticipantMetrics(MetricRegistry metricRegistry, String zkConnectStr,
Expand Down Expand Up @@ -72,6 +84,17 @@ class HelixParticipantMetrics {
updateDiskCapacityCounter =
metricRegistry.counter(MetricRegistry.name(HelixParticipant.class, "updateDiskCapacityCount"));
partitionTransitionToCount = new HashMap<>();

// State transition latency metrics
bootstrapToStandbyDurationMs = metricRegistry.histogram(
MetricRegistry.name(HelixParticipant.class, "bootstrapToStandbyDurationMs" + zkSuffix));
offlineToBootstrapDurationMs = metricRegistry.histogram(
MetricRegistry.name(HelixParticipant.class, "offlineToBootstrapDurationMs" + zkSuffix));
bootstrapFailureCount = metricRegistry.counter(
MetricRegistry.name(HelixParticipant.class, "bootstrapFailureCount" + zkSuffix));
Gauge<Long> maxTimeInBootstrap = this::computeMaxTimeInBootstrap;
registry.gauge(MetricRegistry.name(HelixParticipant.class, "maxTimeInBootstrapMs" + zkSuffix),
() -> maxTimeInBootstrap);
}

/**
Expand All @@ -95,6 +118,54 @@ private int getReplicaCountInState(ReplicaState state) {
return replicaCountByState.get(state);
}

/**
* Record that a partition has entered BOOTSTRAP state.
* @param partitionName the partition that entered BOOTSTRAP
*/
void recordBootstrapStart(String partitionName) {
bootstrapStartTimeMs.put(partitionName, System.currentTimeMillis());
}

/**
* Record that a partition has completed BOOTSTRAP→STANDBY transition successfully.
* @param partitionName the partition that reached STANDBY
*/
void recordBootstrapComplete(String partitionName) {
Long startTime = bootstrapStartTimeMs.remove(partitionName);
if (startTime != null) {
bootstrapToStandbyDurationMs.update(System.currentTimeMillis() - startTime);
}
}

/**
* Record that a partition failed during BOOTSTRAP (went to ERROR).
* @param partitionName the partition that failed
*/
void recordBootstrapFailure(String partitionName) {
bootstrapStartTimeMs.remove(partitionName);
bootstrapFailureCount.inc();
}

/**
* Record the duration of an OFFLINE→BOOTSTRAP transition.
* @param durationMs time in milliseconds the transition took
*/
void recordOfflineToBootstrapDuration(long durationMs) {
offlineToBootstrapDurationMs.update(durationMs);
}

/**
* Compute the maximum time any partition has been in BOOTSTRAP state.
*/
private long computeMaxTimeInBootstrap() {
long now = System.currentTimeMillis();
long maxDuration = 0;
for (Map.Entry<String, Long> entry : bootstrapStartTimeMs.entrySet()) {
maxDuration = Math.max(maxDuration, now - entry.getValue());
}
return maxDuration;
}

/**
* Creates and increments the metric object for given partition's state transition
* @param partitionName partition name
Expand Down
Loading