Skip to content
Closed
Changes from 5 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
41 changes: 32 additions & 9 deletions core/src/main/scala/kafka/tools/ConsumerPerformance.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,14 @@ object ConsumerPerformance {
val totalBytesRead = new AtomicLong(0)
val consumerTimeout = new AtomicBoolean(false)
var metrics: mutable.Map[MetricName, _ <: Metric] = null
val joinGroupTimeInMs = new AtomicLong(0)
val fetchTimeInMs = new AtomicLong(0)

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.

There doesn't appear to be any reason for fetchTimeInMs to be an AtomicLong. We can use a regular long and just initialize it below.


if (!config.hideHeader) {
if (!config.showDetailedStats)
println("start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec")
else
if (!config.showDetailedStats) {

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.

It's a little odd that we are reporting more information if the detailed option is not set, right? Perhaps we should at least report the additional join statistics down both paths?

if (config.useOldConsumer)
println("start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec")
} else
println("time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec")
}

Expand All @@ -67,7 +70,8 @@ object ConsumerPerformance {
val consumer = new KafkaConsumer[Array[Byte], Array[Byte]](config.props)
consumer.subscribe(Collections.singletonList(config.topic))
startMs = System.currentTimeMillis
consume(consumer, List(config.topic), config.numMessages, 1000, config, totalMessagesRead, totalBytesRead)
consume(consumer, List(config.topic), config.numMessages, 1000,
config, totalMessagesRead, totalBytesRead, joinGroupTimeInMs)
endMs = System.currentTimeMillis

if (config.printMetrics) {
Expand Down Expand Up @@ -98,10 +102,26 @@ object ConsumerPerformance {
consumerConnector.shutdown()
}
val elapsedSecs = (endMs - startMs) / 1000.0

@hachikuji hachikuji Jun 7, 2017

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.

I think the main point of this patch is to compute the throughput statistics based only on the time spent fetching, so maybe we can move this below and just use fetchTimeInMs.get / 1000.0? We could also report MB.sec as two separate values: total.MB.sec which uses the total time, and fetch.MB.sec which uses only the fetch time. Similarly for nMsg.sec.

fetchTimeInMs.set((endMs - startMs) - joinGroupTimeInMs.get)
if (!config.showDetailedStats) {
val totalMBRead = (totalBytesRead.get * 1.0) / (1024 * 1024)
println("%s, %s, %.4f, %.4f, %d, %.4f".format(config.dateFormat.format(startMs), config.dateFormat.format(endMs),
totalMBRead, totalMBRead / elapsedSecs, totalMessagesRead.get, totalMessagesRead.get / elapsedSecs))
if (config.useOldConsumer) {
println(s"%s, %s, %.4f, %.4f, %d, %.4f${if (!config.useOldConsumer) ", %s" else ""}".format(
config.dateFormat.format(startMs), config.dateFormat.format(endMs), totalMBRead,
totalMBRead / elapsedSecs, totalMessagesRead.get, totalMessagesRead.get / elapsedSecs))
} else {
println("---- performance test result ----")

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.

It seems unnecessary to make major changes to the output. Why don't we just keep the current format and add the additional fields? I thought the old patch did this and it seemed fine.

println(s"Start time:${"\t" * 5}" + config.dateFormat.format(startMs))
println(s"End time:${"\t" * 5}" + config.dateFormat.format(startMs))
println(s"Total time:${"\t" * 5}%.4fs [%dms(rebalance time) + %dms(fetch time)]".format(
elapsedSecs, joinGroupTimeInMs.get, fetchTimeInMs.get))
println(s"Total MB read:${"\t" * 4}%.4f".format(totalMBRead))
println(s"Total throughput(MB/s):${"\t" * 2}%.4f".format(totalMBRead / elapsedSecs))
println(s"Fetch throughput(MB/s):${"\t" * 2}%.4f".format(totalMBRead / (fetchTimeInMs.get / 1000.0)))
println(s"Total messages read:${"\t" * 2}%d".format(totalMessagesRead.get))
println(s"Total message rate(msg/s):${"\t"}%.4f".format(totalMessagesRead.get / elapsedSecs))
println(s"Fetch message rate(msg/s):\t%.4f".format(totalMessagesRead.get / (fetchTimeInMs.get / 1000.0)))
}
}

if (metrics != null) {
Expand All @@ -110,25 +130,28 @@ object ConsumerPerformance {

}

def consume(consumer: KafkaConsumer[Array[Byte], Array[Byte]], topics: List[String], count: Long, timeout: Long, config: ConsumerPerfConfig, totalMessagesRead: AtomicLong, totalBytesRead: AtomicLong) {
def consume(consumer: KafkaConsumer[Array[Byte], Array[Byte]], topics: List[String], count: Long, timeout: Long, config: ConsumerPerfConfig, totalMessagesRead: AtomicLong, totalBytesRead: AtomicLong, joinTime: AtomicLong) {
var bytesRead = 0L
var messagesRead = 0L
var lastBytesRead = 0L
var lastMessagesRead = 0L
var joinStart = 0L

// Wait for group join, metadata fetch, etc
val joinTimeout = 10000
val isAssigned = new AtomicBoolean(false)
consumer.subscribe(topics.asJava, new ConsumerRebalanceListener {
def onPartitionsAssigned(partitions: util.Collection[TopicPartition]) {
isAssigned.set(true)
joinTime.addAndGet(System.currentTimeMillis - joinStart)
}
def onPartitionsRevoked(partitions: util.Collection[TopicPartition]) {
isAssigned.set(false)
joinStart = System.currentTimeMillis
}})
val joinStart = System.currentTimeMillis()
val joinStartForFirstTime = System.currentTimeMillis()

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.

Since we are now reporting the join time separately, I don't think we need the loop below anymore. It was initially intended to discount the rebalance overhead, but now we account for that transparently.

while (!isAssigned.get()) {
if (System.currentTimeMillis() - joinStart >= joinTimeout) {
if (System.currentTimeMillis() - joinStartForFirstTime >= joinTimeout) {
throw new Exception("Timed out waiting for initial group join.")
}
consumer.poll(100)
Expand Down