Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b80dac3
added jmh, counters, benchmark wrapper
anjy7 Jun 24, 2026
109f229
added election benchmark, per op counters, fixes
anjy7 Jun 25, 2026
3401523
fix
anjy7 Jun 25, 2026
f9b46f9
bump iterations + fork for SingleShotTime
anjy7 Jun 25, 2026
39c0ebb
fix
anjy7 Jun 26, 2026
e34b656
fix
anjy7 Jun 26, 2026
081bf74
fix
anjy7 Jun 30, 2026
81bc1f1
fix
anjy7 Jun 30, 2026
4dac22f
improvements
anjy7 Jun 30, 2026
f9e0440
fix
anjy7 Jul 6, 2026
e793a03
improvements
anjy7 Jul 8, 2026
e721ac5
improvements
anjy7 Jul 8, 2026
14dfb72
improvements
anjy7 Jul 8, 2026
ca12220
add benchmarks ci scripts
anjy7 Jul 10, 2026
6baa163
fix
anjy7 Jul 10, 2026
ebd9cb2
improvements
anjy7 Jul 13, 2026
c91ba77
removed ci files
anjy7 Jul 13, 2026
4d888ad
fix
anjy7 Jul 16, 2026
5b872f0
fix
anjy7 Jul 16, 2026
cda567c
lint fix
anjy7 Jul 17, 2026
be5dfc0
comment improvements
anjy7 Jul 20, 2026
28a449f
added enums for rpc calls
anjy7 Jul 24, 2026
daa550b
improvements
anjy7 Jul 24, 2026
e23255c
fix
anjy7 Jul 24, 2026
9a2b02f
fix 100 characters/line
anjy7 Jul 27, 2026
f13e13d
teardown + decoupling deliverRequest
anjy7 Jul 27, 2026
7b9bed4
improvements
anjy7 Jul 27, 2026
748e300
improvements
anjy7 Jul 27, 2026
99bd5e2
election benchmark avg time
anjy7 Jul 28, 2026
325a8bd
removed expectedRequest
anjy7 Jul 28, 2026
b9f27cc
improvements
anjy7 Jul 28, 2026
3932d9c
fix
anjy7 Jul 28, 2026
23ddc74
type fix
anjy7 Jul 29, 2026
1b5e4e5
optimizations
anjy7 Aug 5, 2026
0fece7a
improvements
anjy7 Aug 5, 2026
4bb300a
add fetch for hwm increment
anjy7 Aug 5, 2026
9e07463
fix
anjy7 Aug 5, 2026
2e4699b
added follower benchmarks
anjy7 Aug 6, 2026
c17e8fd
Merge branch 'apache:trunk' into KAFKA-20338-2
anjy7 Aug 7, 2026
29b51b0
removed waiting pollUntil
anjy7 Aug 6, 2026
75c29c2
added more election benchmarks
anjy7 Aug 7, 2026
4ffb7fa
added prospectiveToUnattachedOnTimeout
anjy7 Aug 7, 2026
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
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3578,6 +3578,12 @@ project(':jmh-benchmarks') {
archiveBaseName = 'kafka-jmh-benchmarks'
}

// Benchmarks should not spend time formatting log messages. Exclude the (location-aware) log4j
// slf4j binding so slf4j falls back to its no-op provider.
configurations.all {
exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl'
}

dependencies {
implementation(project(':core')) {
// jmh requires jopt 4.x while `core` depends on 5.0, they are not binary compatible
Expand All @@ -3603,6 +3609,7 @@ project(':jmh-benchmarks') {
implementation testFixtures(project(':clients'))
implementation testFixtures(project(':server-common'))
implementation testFixtures(project(':metadata'))
implementation testFixtures(project(':raft'))

implementation libs.jmhCore
annotationProcessor libs.jmhGeneratorAnnProcess
Expand Down
3 changes: 3 additions & 0 deletions checkstyle/import-control-jmh-benchmarks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@
<subpackage name="metadata">
<allow class="org.apache.kafka.raft.KRaftConfigs"/>
</subpackage>
<subpackage name="raft">
<allow pkg="org.apache.kafka.raft"/>
</subpackage>
</import-control>
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* 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.kafka.jmh.raft;

import org.apache.kafka.common.protocol.ApiKeys;
import org.apache.kafka.raft.RaftClientBenchmarkContext;
import org.apache.kafka.raft.RaftClientTestContext;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
* Benchmarks for the leader-election path.
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = RaftClientBenchmarkContext.AVERAGE_TIME_WARMUP_ITERATIONS)
@Measurement(iterations = RaftClientBenchmarkContext.AVERAGE_TIME_MEASUREMENT_ITERATIONS)
@Fork(RaftClientBenchmarkContext.AVERAGE_TIME_FORKS)
public class ElectionBenchmarks {

/**
* Starting state: the local node is Unattached in a {@code voterCount}-node cluster.
*/
@State(Scope.Thread)
public static class UnattachedWithMultipleVoters {
@Param({"3", "5"})
public int voterCount;

RaftClientBenchmarkContext benchmark;

@Setup(Level.Iteration)
public void setup() throws IOException {
benchmark = RaftClientBenchmarkContext.unattachedVoter(
voterCount,
RaftClientBenchmarkContext.DEFAULT_KRAFT_VERSION,
RaftClientBenchmarkContext.DEFAULT_RAFT_PROTOCOL);
}
}

/**
* The local node wins an election, commits the epoch it just took, then resigns back to
* Unattached.
*/
@Benchmark
public void electLeader(
UnattachedWithMultipleVoters state,
KRaftBenchmarkingCounters counters
) throws Exception {
state.benchmark.testContext().unattachedToLeader();
state.benchmark.commitEpoch();
state.benchmark.toUnattachedWithHigherEpoch();
counters.recordInvocation(state.benchmark);
}

/**
* The local node times out and starts an election, then a higher-epoch vote request knocks it
* back to Unattached for the next invocation.
*/
@Benchmark
public void unattachedToProspective(
UnattachedWithMultipleVoters state,
KRaftBenchmarkingCounters counters
) throws Exception {
state.benchmark.unattachedToProspective();
state.benchmark.toUnattachedWithHigherEpoch();
state.benchmark.drainSentRequests(); // discard the abandoned pre-votes
counters.recordInvocation(state.benchmark);
}

/**
* The local node times out and starts an election, then that election times out without a
* winner, and it falls back to Unattached for the next invocation.
*/
@Benchmark
public void prospectiveToUnattachedOnTimeout(
UnattachedWithMultipleVoters state,
KRaftBenchmarkingCounters counters
) throws Exception {
state.benchmark.unattachedToProspective();
state.benchmark.prospectiveToUnattached();
counters.recordInvocation(state.benchmark);
}

/**
* Starting state: the local node is Prospective in a {@code voterCount}-node cluster, with its
* pre-vote requests already on the send queue.
*/
@State(Scope.Thread)
public static class ProspectiveWithMultipleVoters {
@Param({"3", "5"})
public int voterCount;

RaftClientBenchmarkContext benchmark;

@Setup(Level.Iteration)
public void setup() throws Exception {
benchmark = RaftClientBenchmarkContext.unattachedVoter(
voterCount,
RaftClientBenchmarkContext.DEFAULT_KRAFT_VERSION,
RaftClientBenchmarkContext.DEFAULT_RAFT_PROTOCOL);
benchmark.unattachedToProspective();
// Keep the queued pre-votes (the measured transition answers them); only zero counters.
benchmark.zeroCounters();
}
}

/**
* The local node, a prospective, wins its pre-vote and becomes a candidate, then its election
* timeout returns it to prospective for the next invocation.
*/
@Benchmark
public void prospectiveToCandidate(
ProspectiveWithMultipleVoters state,
KRaftBenchmarkingCounters counters
) throws Exception {
state.benchmark.prospectiveToCandidate();
state.benchmark.drainSentRequests(); // discard the candidate's vote requests
state.benchmark.candidateToProspective();
counters.recordInvocation(state.benchmark);
}

/**
* Starting state: the local node is a follower of another voter in a {@code voterCount}-node
* cluster.
*/
@State(Scope.Thread)
public static class FollowerWithMultipleVoters {
@Param({"3", "5"})
public int voterCount;

RaftClientBenchmarkContext benchmark;

@Setup(Level.Iteration)
public void setup() throws IOException {
benchmark = RaftClientBenchmarkContext.follower(
voterCount,
RaftClientBenchmarkContext.DEFAULT_KRAFT_VERSION,
RaftClientBenchmarkContext.DEFAULT_RAFT_PROTOCOL);
}
}

/**
* The local node, a follower, times out on its leader and starts an election, then a
* BeginQuorumEpoch one epoch ahead returns it to a follower for the next invocation.
*/
@Benchmark
public void followerToProspective(
FollowerWithMultipleVoters state,
KRaftBenchmarkingCounters counters
) throws Exception {
RaftClientTestContext context = state.benchmark.testContext();
state.benchmark.followerToProspective();
state.benchmark.deliverAndAwaitResponse(
context.inboundRequest(
context.beginEpochRequest(context.currentEpoch() + 1, state.benchmark.leaderKey().id())),
Optional.of(ApiKeys.BEGIN_QUORUM_EPOCH));
state.benchmark.drainSentRequests(); // discard abandoned pre-votes + the new fetch
counters.recordInvocation(state.benchmark);
}
}
Loading