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
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 == null ? Optional.empty() : txGenerator;
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