private bool Rebalance(Cluster cluster) { var myTopicThreadIdsMap = TopicCount.ConstructTopicCount(group, consumerIdString, parent.zkClient) .GetConsumerThreadIdsPerTopic(); var consumersPerTopicMap = ZkUtils.GetConsumersPerTopic(parent.zkClient, group); var brokers = ZkUtils.GetAllBrokersInCluster(parent.zkClient); if (brokers.Count == 0) { // This can happen in a rare case when there are no brokers available in the cluster when the consumer is started. // We log an warning and register for child changes on brokers/id so that rebalance can be triggered when the brokers // are up. Logger.Warn("no brokers found when trying to rebalance."); parent.zkClient.SubscribeChildChanges(ZkUtils.BrokerIdsPath, parent.loadBalancerListener); return(true); } else { var partitionsAssignmentPerTopicMap = ZkUtils.GetPartitionAssignmentForTopics( parent.zkClient, myTopicThreadIdsMap.Keys.ToList()); var partitionsPerTopicMap = partitionsAssignmentPerTopicMap.ToDictionary( p => p.Key, p => p.Value.Keys.OrderBy(x => x).ToList()); /** * fetchers must be stopped to avoid Data duplication, since if the current * rebalancing attempt fails, the partitions that are released could be owned by another consumer. * But if we don't stop the fetchers first, this consumer would continue returning Data for released * partitions in parallel. So, not stopping the fetchers leads to duplicate Data. */ this.CloseFetchers(cluster, (IDictionary <string, IList <KafkaStream <TKey, TValue> > >)KafkaMessageAndMetadataStreams, myTopicThreadIdsMap); this.ReleasePartitionOwnership(parent.topicRegistry); var partitionOwnershipDecision = new Dictionary <Tuple <string, int>, string>(); var currentTopicRegistry = new Pool <string, Pool <int, PartitionTopicInfo> >(); foreach (var topicAndConsumerThreadIsSet in myTopicThreadIdsMap) { var topic = topicAndConsumerThreadIsSet.Key; var consumerThreadIdSet = topicAndConsumerThreadIsSet.Value; currentTopicRegistry[topic] = new Pool <int, PartitionTopicInfo>(); var topicDirs = new ZKGroupTopicDirs(group, topic); var curConsumers = consumersPerTopicMap.Get(topic); var curPartitions = partitionsPerTopicMap.Get(topic); var nPartsPerConsumer = curPartitions.Count / curConsumers.Count; var nConsumersWithExtraPart = curPartitions.Count % curConsumers.Count; Logger.InfoFormat("Consumer {0} rebalancing the following partitions: {1} for topic {2} with consumers: {3}", consumerIdString, string.Join(",", curPartitions), topic, string.Join(",", curConsumers)); foreach (var consumerThreadId in consumerThreadIdSet) { var myConsumerPosition = curConsumers.IndexOf(consumerThreadId); Contract.Assert(myConsumerPosition >= 0); var startPart = (nPartsPerConsumer * myConsumerPosition) + Math.Min(nConsumersWithExtraPart, myConsumerPosition); var nParts = nPartsPerConsumer + (myConsumerPosition + 1 > nConsumersWithExtraPart ? 0 : 1); /** * Range-partition the sorted partitions to consumers for better locality. * The first few consumers pick up an extra partition, if any. */ if (nParts <= 0) { Logger.WarnFormat( "No broker partitions consumed by consumer thread {0} for topic {1}", consumerThreadId, topic); } else { for (var i = startPart; i < startPart + nParts; i++) { var partition = curPartitions[i]; Logger.InfoFormat("{0} attempting to claim partition {1}", consumerThreadId, partition); this.AddPartitionTopicInfo(currentTopicRegistry, topicDirs, partition, topic, consumerThreadId); // record the partition ownership decision partitionOwnershipDecision[Tuple.Create(topic, partition)] = consumerThreadId; } } } } /** * move the partition ownership here, since that can be used to indicate a truly successful rebalancing attempt * A rebalancing attempt is completed successfully only after the fetchers have been started correctly */ if (this.ReflectPartitionOwnershipDecision(partitionOwnershipDecision)) { Logger.Info("Updating the cache"); Logger.Debug("Partitions per topic cache " + JObject.FromObject(partitionsPerTopicMap).ToString(Formatting.None)); Logger.Debug("Consumers per topic cache " + JObject.FromObject(consumersPerTopicMap).ToString(Formatting.None)); parent.topicRegistry = currentTopicRegistry; this.UpdateFetcher(cluster); return(true); } else { return(false); } } }