Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ project(':core') {
dependencies {
compile project(':clients')
compile libs.jacksonDatabind
compile libs.jacksonJDK8Datatypes
compile libs.joptSimple
compile libs.metrics
compile libs.scalaLibrary
Expand Down Expand Up @@ -830,6 +831,7 @@ project(':clients') {
compile libs.snappy
compile libs.slf4jApi
compileOnly libs.jacksonDatabind // for SASL/OAUTHBEARER bearer token parsing
compileOnly libs.jacksonJDK8Datatypes

jacksonDatabindConfig libs.jacksonDatabind // to publish as provided scope dependency.

Expand All @@ -839,6 +841,7 @@ project(':clients') {

testRuntime libs.slf4jlog4j
testRuntime libs.jacksonDatabind
testRuntime libs.jacksonJDK8Datatypes
}

task determineCommitId {
Expand Down Expand Up @@ -918,6 +921,7 @@ project(':tools') {
compile project(':log4j-appender')
compile libs.argparse4j
compile libs.jacksonDatabind
compile libs.jacksonJDK8Datatypes
compile libs.slf4jApi

compile libs.jacksonJaxrsJsonProvider
Expand Down Expand Up @@ -1347,6 +1351,7 @@ project(':connect:json') {
dependencies {
compile project(':connect:api')
compile libs.jacksonDatabind
compile libs.jacksonJDK8Datatypes
compile libs.slf4jApi

testCompile libs.easymock
Expand Down
1 change: 1 addition & 0 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ libs += [
bcpkix: "org.bouncycastle:bcpkix-jdk15on:$versions.bcpkix",
easymock: "org.easymock:easymock:$versions.easymock",
jacksonDatabind: "com.fasterxml.jackson.core:jackson-databind:$versions.jackson",
jacksonJDK8Datatypes: "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:$versions.jackson",
jacksonJaxrsJsonProvider: "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$versions.jackson",
jaxbApi: "javax.xml.bind:jaxb-api:$versions.jaxb",
jaxrsApi: "javax.ws.rs:javax.ws.rs-api:$versions.jaxrs",
Expand Down
51 changes: 51 additions & 0 deletions tests/bin/trogdor-run-transactional-produce-bench.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
# 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.

COORDINATOR_ENDPOINT="localhost:8889"
TASK_ID="produce_bench_$RANDOM"
TASK_SPEC=$(
cat <<EOF
{
"id": "$TASK_ID",
"spec": {
"class": "org.apache.kafka.trogdor.workload.ProduceBenchSpec",
"durationMs": 10000000,
"producerNode": "node0",
"bootstrapServers": "localhost:9092",
"targetMessagesPerSec": 100,
"maxMessages": 500,
"transactionGenerator" : {
"type" : "uniform",
"messagesPerTransaction" : 50
},
"activeTopics": {
"foo[1-3]": {
"numPartitions": 3,
"replicationFactor": 1
}
},
"inactiveTopics": {
"foo[4-5]": {
"numPartitions": 3,
"replicationFactor": 1
}
}
}
}
EOF
)
./bin/trogdor.sh client --create-task "${TASK_SPEC}" "${COORDINATOR_ENDPOINT}"
echo "\$TASK_ID = $TASK_ID"
4 changes: 3 additions & 1 deletion tests/kafkatest/services/trogdor/produce_bench_workload.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@
class ProduceBenchWorkloadSpec(TaskSpec):
def __init__(self, start_ms, duration_ms, producer_node, bootstrap_servers,
target_messages_per_sec, max_messages, producer_conf, admin_client_conf,
common_client_conf, inactive_topics, active_topics):
common_client_conf, inactive_topics, active_topics,
transaction_generator=None):
super(ProduceBenchWorkloadSpec, self).__init__(start_ms, duration_ms)
self.message["class"] = "org.apache.kafka.trogdor.workload.ProduceBenchSpec"
self.message["producerNode"] = producer_node
self.message["bootstrapServers"] = bootstrap_servers
self.message["targetMessagesPerSec"] = target_messages_per_sec
self.message["maxMessages"] = max_messages
self.message["producerConf"] = producer_conf
self.message["transactionGenerator"] = transaction_generator
self.message["adminClientConf"] = admin_client_conf
self.message["commonClientConf"] = common_client_conf
self.message["inactiveTopics"] = inactive_topics
Expand Down
29 changes: 25 additions & 4 deletions tests/kafkatest/tests/core/produce_bench_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def __init__(self, test_context):
self.workload_service = ProduceBenchWorkloadService(test_context, self.kafka)
self.trogdor = TrogdorService(context=self.test_context,
client_services=[self.kafka, self.workload_service])
self.active_topics = {"produce_bench_topic[0-1]": {"numPartitions": 1, "replicationFactor": 3}}
self.inactive_topics = {"produce_bench_topic[2-9]": {"numPartitions": 1, "replicationFactor": 3}}

def setUp(self):
self.trogdor.start()
Expand All @@ -43,8 +45,6 @@ def teardown(self):
self.zk.stop()

def test_produce_bench(self):
active_topics={"produce_bench_topic[0-1]":{"numPartitions":1, "replicationFactor":3}}
inactive_topics={"produce_bench_topic[2-9]":{"numPartitions":1, "replicationFactor":3}}
spec = ProduceBenchWorkloadSpec(0, TaskSpec.MAX_DURATION_MS,
self.workload_service.producer_node,
self.workload_service.bootstrap_servers,
Expand All @@ -53,8 +53,29 @@ def test_produce_bench(self):
producer_conf={},
admin_client_conf={},
common_client_conf={},
inactive_topics=inactive_topics,
active_topics=active_topics)
inactive_topics=self.inactive_topics,
active_topics=self.active_topics)
workload1 = self.trogdor.create_task("workload1", spec)
workload1.wait_for_done(timeout_sec=360)
tasks = self.trogdor.tasks()
self.logger.info("TASKS: %s\n" % json.dumps(tasks, sort_keys=True, indent=2))

def test_produce_bench_transactions(self):
spec = ProduceBenchWorkloadSpec(0, TaskSpec.MAX_DURATION_MS,
self.workload_service.producer_node,
self.workload_service.bootstrap_servers,
target_messages_per_sec=1000,
max_messages=100000,
producer_conf={},
admin_client_conf={},
common_client_conf={},
inactive_topics=self.inactive_topics,
active_topics=self.active_topics,
transaction_generator={
# 10 transactions with 10k messages
"type": "uniform",
"messagesPerTransaction": "10000"
})
workload1 = self.trogdor.create_task("workload1", spec)
workload1.wait_for_done(timeout_sec=360)
tasks = self.trogdor.tasks()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;

/**
* Utilities for working with JSON.
Expand All @@ -33,6 +34,7 @@ public class JsonUtil {
JSON_SERDE = new ObjectMapper();
JSON_SERDE.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
JSON_SERDE.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
JSON_SERDE.registerModule(new Jdk8Module());
JSON_SERDE.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,39 @@

import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
* The specification for a benchmark that produces messages to a set of topics.
*
* To configure a transactional producer, a #{@link TransactionGenerator} must be passed in.
* Said generator works in lockstep with the producer by instructing it what action to take next in regards to a transaction.
*
* An example JSON representation which will result in a producer that creates three topics (foo1, foo2, foo3)
* with three partitions each and produces to them:
* #{@code
* {
* "class": "org.apache.kafka.trogdor.workload.ProduceBenchSpec",
* "durationMs": 10000000,
* "producerNode": "node0",
* "bootstrapServers": "localhost:9092",
* "targetMessagesPerSec": 10,
* "maxMessages": 100,
* "activeTopics": {
* "foo[1-3]": {
* "numPartitions": 3,
* "replicationFactor": 1
* }
* },
* "inactiveTopics": {
* "foo[4-5]": {
* "numPartitions": 3,
* "replicationFactor": 1
* }
* }
* }
* }
*/
public class ProduceBenchSpec extends TaskSpec {
private final String producerNode;
Expand All @@ -38,6 +67,7 @@ public class ProduceBenchSpec extends TaskSpec {
private final int maxMessages;
private final PayloadGenerator keyGenerator;
private final PayloadGenerator valueGenerator;
private final Optional<TransactionGenerator> transactionGenerator;
private final Map<String, String> producerConf;
private final Map<String, String> adminClientConf;
private final Map<String, String> commonClientConf;
Expand All @@ -53,6 +83,7 @@ public ProduceBenchSpec(@JsonProperty("startMs") long startMs,
@JsonProperty("maxMessages") int maxMessages,
@JsonProperty("keyGenerator") PayloadGenerator keyGenerator,
@JsonProperty("valueGenerator") PayloadGenerator valueGenerator,
@JsonProperty("transactionGenerator") Optional<TransactionGenerator> txGenerator,
@JsonProperty("producerConf") Map<String, String> producerConf,
@JsonProperty("commonClientConf") Map<String, String> commonClientConf,
@JsonProperty("adminClientConf") Map<String, String> adminClientConf,
Expand All @@ -67,6 +98,7 @@ public ProduceBenchSpec(@JsonProperty("startMs") long startMs,
new SequentialPayloadGenerator(4, 0) : keyGenerator;
this.valueGenerator = valueGenerator == null ?
new ConstantPayloadGenerator(512, new byte[0]) : valueGenerator;
this.transactionGenerator = txGenerator;

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.

We should convert null -> none here. Even if Jackson always fills this in, someone may manually create this object

this.producerConf = configOrEmptyMap(producerConf);
this.commonClientConf = configOrEmptyMap(commonClientConf);
this.adminClientConf = configOrEmptyMap(adminClientConf);
Expand Down Expand Up @@ -106,6 +138,11 @@ public PayloadGenerator valueGenerator() {
return valueGenerator;
}

@JsonProperty
public Optional<TransactionGenerator> transactionGenerator() {
return transactionGenerator;
}

@JsonProperty
public Map<String, String> producerConf() {
return producerConf;
Expand Down
Loading