Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fea6c1b
First commit for replacement for replication test suite. All are curr…
Jul 13, 2015
069b9d7
Fixed soft failure tests.
Jul 14, 2015
0a1efd3
Produce/consume from multiple topics.
Jul 14, 2015
4dd513e
Added one test with acks=1 and one using compression.
Jul 14, 2015
99ae12f
Replaced previous replication_test.py with the new suite of replicati…
Jul 14, 2015
d6294fa
Merged in KAFKA-2276
Jul 20, 2015
77d07a3
Merged in KAFKA-2276
Jul 22, 2015
61b7a2a
Update provisioning so that iptables can be used in creating network …
Jul 23, 2015
a2ad18b
Merged in upstream trunk.
Jul 27, 2015
a62fb6c
fixed checkstyle errors
Jul 27, 2015
1a4d211
Many small changes per review. Also, improved performance by making k…
Jul 28, 2015
5a548e6
Fixed command adding LOG_DIR to env before starting kafka
Jul 28, 2015
57386de
KAFKA-2301: Warn ConsumerOffsetChecker as deprecated; reviewed by Ewe…
Jul 28, 2015
269c240
KAFKA-2381: Fix concurrent modification on assigned partition while l…
Jul 28, 2015
3df46bf
KAFKA-2347: Add setConsumerRebalanceListener method to ZookeeperConsu…
Jul 28, 2015
594b963
KAFKA-2275: Add ListTopics() API to the Java consumer; reviewed by Ja…
Jul 28, 2015
f4101ab
KAFKA-2089: Fix transient MetadataTest failure; reviewed by Jiangjie …
rajinisivaram Jul 28, 2015
86ec39c
Merged in upstream trunk.
Jul 29, 2015
9bb1037
Fixed some of the wait_until conditions which were causing problems w…
Jul 29, 2015
3791086
Merged in final KAFKA-2276
Jul 29, 2015
499ceca
Added unit-test-like sanity checks to help with validating and debugg…
Jul 29, 2015
c9edf92
Added crude network partition test which approximately replicates the…
Jul 29, 2015
b5a44dc
Added check in kafka.stop_node to kill -9 if SIGTERM did not work.
Jul 29, 2015
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
2 changes: 1 addition & 1 deletion checkstyle/import-control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
<subpackage name="tools">
<allow pkg="org.apache.kafka.clients.producer" />
<allow pkg="org.apache.kafka.clients.consumer" />
<allow pkg="com.fasterxml.jackson.core" />
<allow pkg="com.fasterxml.jackson" />
<allow pkg="net.sourceforge.argparse4j" />
</subpackage>
</subpackage>
Expand Down
29 changes: 26 additions & 3 deletions clients/src/main/java/org/apache/kafka/clients/ClientRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,43 @@ public final class ClientRequest {
private final boolean expectResponse;
private final RequestSend request;
private final RequestCompletionHandler callback;
private final boolean isInitiatedByNetworkClient;

/**
* @param createdMs The unix timestamp in milliseconds for the time at which this request was created.
* @param expectResponse Should we expect a response message or is this request complete once it is sent?
* @param request The request
* @param callback A callback to execute when the response has been received (or null if no callback is necessary)
*/
public ClientRequest(long createdMs, boolean expectResponse, RequestSend request, RequestCompletionHandler callback) {
public ClientRequest(long createdMs, boolean expectResponse, RequestSend request,
RequestCompletionHandler callback) {
this(createdMs, expectResponse, request, callback, false);
}

/**
* @param createdMs The unix timestamp in milliseconds for the time at which this request was created.
* @param expectResponse Should we expect a response message or is this request complete once it is sent?
* @param request The request
* @param callback A callback to execute when the response has been received (or null if no callback is necessary)
* @param isInitiatedByNetworkClient Is request initiated by network client, if yes, its
* response will be consumed by network client
*/
public ClientRequest(long createdMs, boolean expectResponse, RequestSend request,
RequestCompletionHandler callback, boolean isInitiatedByNetworkClient) {
this.createdMs = createdMs;
this.callback = callback;
this.request = request;
this.expectResponse = expectResponse;
this.isInitiatedByNetworkClient = isInitiatedByNetworkClient;
}

@Override
public String toString() {
return "ClientRequest(expectResponse=" + expectResponse + ", callback=" + callback + ", request=" + request
+ ")";
return "ClientRequest(expectResponse=" + expectResponse +
", callback=" + callback +
", request=" + request +
(isInitiatedByNetworkClient ? ", isInitiatedByNetworkClient" : "") +
")";
}

public boolean expectResponse() {
Expand All @@ -63,4 +82,8 @@ public long createdTime() {
return createdMs;
}

public boolean isInitiatedByNetworkClient() {
return isInitiatedByNetworkClient;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ private void handleCompletedReceives(List<ClientResponse> responses, long now) {
short apiKey = req.request().header().apiKey();
Struct body = (Struct) ProtoUtils.currentResponseSchema(apiKey).read(receive.payload());
correlate(req.request().header(), header);
if (apiKey == ApiKeys.METADATA.id) {
if (apiKey == ApiKeys.METADATA.id && req.isInitiatedByNetworkClient()) {
handleMetadataResponse(req.request().header(), body, now);
} else {
// need to add body/header to response here
Expand Down Expand Up @@ -454,7 +454,7 @@ private void correlate(RequestHeader requestHeader, ResponseHeader responseHeade
private ClientRequest metadataRequest(long now, String node, Set<String> topics) {
MetadataRequest metadata = new MetadataRequest(new ArrayList<String>(topics));
RequestSend send = new RequestSend(node, nextRequestHeader(ApiKeys.METADATA), metadata.toStruct());
return new ClientRequest(now, true, send, null);
return new ClientRequest(now, true, send, null, true);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ public interface Consumer<K, V> extends Closeable {
*/
public List<PartitionInfo> partitionsFor(String topic);

/**
* @see KafkaConsumer#listTopics()
*/
public Map<String, List<PartitionInfo>> listTopics();

/**
* @see KafkaConsumer#close()
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,22 @@ public List<PartitionInfo> partitionsFor(String topic) {
}
}

/**
* Get metadata about partitions for all topics. This method will issue a remote call to the
* server.
*
* @return The map of topics and its partitions
*/
@Override
public Map<String, List<PartitionInfo>> listTopics() {
acquire();
try {
return fetcher.getAllTopics(requestTimeoutMs);
} finally {
release();
}
}

@Override
public void close() {
acquire();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ public synchronized List<PartitionInfo> partitionsFor(String topic) {
return parts;
}

@Override
public Map<String, List<PartitionInfo>> listTopics() {
ensureNotClosed();
return partitions;
}

public synchronized void updatePartitions(String topic, List<PartitionInfo> partitions) {
ensureNotClosed();
this.partitions.put(topic, partitions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import org.apache.kafka.common.requests.FetchResponse;
import org.apache.kafka.common.requests.ListOffsetRequest;
import org.apache.kafka.common.requests.ListOffsetResponse;
import org.apache.kafka.common.requests.MetadataRequest;
import org.apache.kafka.common.requests.MetadataResponse;
import org.apache.kafka.common.serialization.Deserializer;
import org.apache.kafka.common.utils.Time;
import org.apache.kafka.common.utils.Utils;
Expand Down Expand Up @@ -160,6 +162,48 @@ public void updateFetchPositions(Set<TopicPartition> partitions) {
}
}



/**
* Get metadata for all topics present in Kafka cluster
*
* @param timeout time for which getting all topics is attempted
* @return The map of topics and its partitions
*/
public Map<String, List<PartitionInfo>> getAllTopics(long timeout) {
final HashMap<String, List<PartitionInfo>> topicsPartitionInfos = new HashMap<>();
long startTime = time.milliseconds();

while (time.milliseconds() - startTime < timeout) {
final Node node = client.leastLoadedNode();
if (node != null) {
MetadataRequest metadataRequest = new MetadataRequest(Collections.<String>emptyList());
final RequestFuture<ClientResponse> requestFuture =
client.send(node, ApiKeys.METADATA, metadataRequest);

client.poll(requestFuture);

if (requestFuture.succeeded()) {
MetadataResponse response =
new MetadataResponse(requestFuture.value().responseBody());

for (String topic : response.cluster().topics())
topicsPartitionInfos.put(
topic, response.cluster().availablePartitionsForTopic(topic));

return topicsPartitionInfos;
}

if (!requestFuture.isRetriable())
throw requestFuture.exception();
}

Utils.sleep(retryBackoffMs);
}

return topicsPartitionInfos;
}

/**
* Reset offsets for the given partition using the offset reset strategy.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.apache.kafka.clients.consumer.OffsetResetStrategy;
import org.apache.kafka.common.TopicPartition;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -83,7 +84,8 @@ public void unsubscribe(String topic) {
throw new IllegalStateException("Topic " + topic + " was never subscribed to.");
this.subscribedTopics.remove(topic);
this.needsPartitionAssignment = true;
for (TopicPartition tp: assignedPartitions())
final List<TopicPartition> existingAssignedPartitions = new ArrayList<>(assignedPartitions());
for (TopicPartition tp: existingAssignedPartitions)
if (topic.equals(tp.topic()))
clearPartition(tp);
}
Expand Down
18 changes: 13 additions & 5 deletions clients/src/test/java/org/apache/kafka/clients/MetadataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/
package org.apache.kafka.clients;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.kafka.common.Cluster;
import org.apache.kafka.common.errors.TimeoutException;
Expand All @@ -27,11 +27,11 @@ public class MetadataTest {
private long refreshBackoffMs = 100;
private long metadataExpireMs = 1000;
private Metadata metadata = new Metadata(refreshBackoffMs, metadataExpireMs);
private AtomicBoolean backgroundError = new AtomicBoolean(false);
private AtomicReference<String> backgroundError = new AtomicReference<String>();

@After
public void tearDown() {
assertFalse(backgroundError.get());
assertNull("Exception in background thread : " + backgroundError.get(), backgroundError.get());
}

@Test
Expand All @@ -48,7 +48,15 @@ public void testMetadata() throws Exception {
Thread t2 = asyncFetch(topic);
assertTrue("Awaiting update", t1.isAlive());
assertTrue("Awaiting update", t2.isAlive());
metadata.update(TestUtils.singletonCluster(topic, 1), time);
// Perform metadata update when an update is requested on the async fetch thread
// This simulates the metadata update sequence in KafkaProducer
while (t1.isAlive() || t2.isAlive()) {
if (metadata.timeToNextUpdate(time) == 0) {
metadata.update(TestUtils.singletonCluster(topic, 1), time);
time += refreshBackoffMs;
}
Thread.sleep(1);
}
t1.join();
t2.join();
assertFalse("No update needed.", metadata.timeToNextUpdate(time) == 0);
Expand Down Expand Up @@ -106,7 +114,7 @@ public void run() {
try {
metadata.awaitUpdate(metadata.requestUpdate(), refreshBackoffMs);
} catch (Exception e) {
backgroundError.set(true);
backgroundError.set(e.toString());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
import org.apache.kafka.clients.consumer.OffsetResetStrategy;
import org.apache.kafka.common.Cluster;
import org.apache.kafka.common.Node;
import org.apache.kafka.common.PartitionInfo;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.metrics.Metrics;
import org.apache.kafka.common.protocol.Errors;
import org.apache.kafka.common.protocol.types.Struct;
import org.apache.kafka.common.record.CompressionType;
import org.apache.kafka.common.record.MemoryRecords;
import org.apache.kafka.common.requests.FetchResponse;
import org.apache.kafka.common.requests.MetadataResponse;
import org.apache.kafka.common.serialization.ByteArrayDeserializer;
import org.apache.kafka.common.utils.MockTime;
import org.apache.kafka.test.TestUtils;
Expand Down Expand Up @@ -180,6 +182,17 @@ public void testFetchOutOfRange() {
assertEquals(null, subscriptions.consumed(tp));
}

@Test
public void testGetAllTopics() throws InterruptedException {
// sending response before request, as getAllTopics is a blocking call
client.prepareResponse(
new MetadataResponse(cluster, Collections.<String, Errors>emptyMap()).toStruct());

Map<String, List<PartitionInfo>> allTopics = fetcher.getAllTopics(5000L);

assertEquals(cluster.topics().size(), allTopics.size());
}

private Struct fetchResponse(ByteBuffer buffer, short error, long hw) {
FetchResponse response = new FetchResponse(Collections.singletonMap(tp, new FetchResponse.PartitionData(error, hw, buffer)));
return response.toStruct();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,27 @@ public void topicSubscription() {
assertAllPositions(tp0, null);
assertEquals(Collections.singleton(tp1), state.assignedPartitions());
}

@Test
public void topicUnsubscription() {
final String topic = "test";
state.subscribe(topic);
assertEquals(1, state.subscribedTopics().size());
assertTrue(state.assignedPartitions().isEmpty());
assertTrue(state.partitionsAutoAssigned());
state.changePartitionAssignment(asList(tp0));
state.committed(tp0, 1);
state.fetched(tp0, 1);
state.consumed(tp0, 1);
assertAllPositions(tp0, 1L);
state.changePartitionAssignment(asList(tp1));
assertAllPositions(tp0, null);
assertEquals(Collections.singleton(tp1), state.assignedPartitions());

state.unsubscribe(topic);
assertEquals(0, state.subscribedTopics().size());
assertTrue(state.assignedPartitions().isEmpty());
}

@Test(expected = IllegalArgumentException.class)
public void cantChangeFetchPositionForNonAssignedPartition() {
Expand Down
2 changes: 2 additions & 0 deletions config/server.properties
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,5 @@ zookeeper.connect=localhost:2181

# Timeout in ms for connecting to zookeeper
zookeeper.connection.timeout.ms=6000

auto.create.topics.enable=true
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ public interface ConsumerConnector {
*/
public void commitOffsets(Map<TopicAndPartition, OffsetAndMetadata> offsetsToCommit, boolean retryOnFailure);

/**
* Wire in a consumer rebalance listener to be executed when consumer rebalance occurs.
* @param listener The consumer rebalance listener to wire in
*/
public void setConsumerRebalanceListener(ConsumerRebalanceListener listener);

/**
* Shut down the connector
*/
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/kafka/tools/ConsumerOffsetChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ object ConsumerOffsetChecker extends Logging {
}

def main(args: Array[String]) {
warn("WARNING: ConsumerOffsetChecker is deprecated and will be dropped in releases following 0.9.0. Use ConsumerGroupCommand instead.")

val parser = new OptionParser()

val zkConnectOpt = parser.accepts("zookeeper", "ZooKeeper connect string.").
Expand Down
37 changes: 37 additions & 0 deletions core/src/test/scala/integration/kafka/api/ConsumerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,24 @@ class ConsumerTest extends IntegrationTestHarness with Logging {
assertNull(this.consumers(0).partitionsFor("non-exist-topic"))
}

def testListTopics() {
val numParts = 2
val topic1: String = "part-test-topic-1"
val topic2: String = "part-test-topic-2"
val topic3: String = "part-test-topic-3"
TestUtils.createTopic(this.zkClient, topic1, numParts, 1, this.servers)
TestUtils.createTopic(this.zkClient, topic2, numParts, 1, this.servers)
TestUtils.createTopic(this.zkClient, topic3, numParts, 1, this.servers)

val topics = this.consumers.head.listTopics()
assertNotNull(topics)
assertEquals(5, topics.size())
assertEquals(5, topics.keySet().size())
assertEquals(2, topics.get(topic1).length)
assertEquals(2, topics.get(topic2).length)
assertEquals(2, topics.get(topic3).length)
}

def testPartitionReassignmentCallback() {
val callback = new TestConsumerReassignmentCallback()
this.consumerConfig.setProperty(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "100"); // timeout quickly to avoid slow test
Expand Down Expand Up @@ -217,6 +235,25 @@ class ConsumerTest extends IntegrationTestHarness with Logging {
consumer0.close()
}

def testUnsubscribeTopic() {
val callback = new TestConsumerReassignmentCallback()
this.consumerConfig.setProperty(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "100"); // timeout quickly to avoid slow test
val consumer0 = new KafkaConsumer(this.consumerConfig, callback, new ByteArrayDeserializer(), new ByteArrayDeserializer())

try {
consumer0.subscribe(topic)

// the initial subscription should cause a callback execution
while (callback.callsToAssigned == 0)
consumer0.poll(50)

consumer0.unsubscribe(topic)
assertEquals(0, consumer0.subscriptions.size())
} finally {
consumer0.close()
}
}

private class TestConsumerReassignmentCallback extends ConsumerRebalanceCallback {
var callsToAssigned = 0
var callsToRevoked = 0
Expand Down
Loading