public MetadataResponse.Partition Select(MetadataResponse.Topic topic, ArraySegment <byte> key)
        {
            if (topic == null)
            {
                throw new ArgumentNullException(nameof(topic));
            }
            if (topic.partition_metadata.Count == 0)
            {
                throw new RoutingException($"No partitions to choose on {topic}.");
            }

            if (key.Count == 0)
            {
                return(RoundRobinPartitionSelector.Singleton.Select(topic, key));
            }

            // use key hash
            var partitionId = Crc32.Compute(key) % topic.partition_metadata.Count;
            var partition   = topic.partition_metadata.FirstOrDefault(x => x.partition_id == partitionId);

            if (partition != null)
            {
                return(partition);
            }

            throw new RoutingException($"Hash function return partition {partitionId}, but the available partitions are {string.Join(",", topic.partition_metadata.Select(x => x.partition_id))}");
        }
Esempio n. 2
0
        private void EnsurePartitionPollingThreads()
        {
            try
            {
                if (Interlocked.Increment(ref _ensureOneThread) == 1)
                {
                    _options.Log.Debug(() => LogEvent.Create($"Consumer: Refreshing partitions for topic/{_options.Topic}"));
                    _topic = _options.Router.GetTopicMetadataAsync(_options.Topic, CancellationToken.None).Result;

                    //create one thread per partition, if they are in the white list.
                    foreach (var partition in _topic.Partitions)
                    {
                        var partitionId = partition.PartitionId;
                        if (_options.PartitionWhitelist.Count == 0 || _options.PartitionWhitelist.Any(x => x == partitionId))
                        {
                            _partitionPollingIndex.AddOrUpdate(partitionId,
                                                               i => ConsumeTopicPartitionAsync(_topic.TopicName, partitionId, CancellationToken.None),
                                                               (i, task) => task);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _options.Log.Error(LogEvent.Create(ex, $"Trying to setup consumer for topic/{_options.Topic}"));
            }
            finally
            {
                Interlocked.Decrement(ref _ensureOneThread);
            }
        }
Esempio n. 3
0
        public void SelectorShouldThrowExceptionWhenPartitionsAreEmpty()
        {
            var selector = new PartitionSelector();
            var topic    = new MetadataResponse.Topic("emptyPartition");

            Assert.Throws <RoutingException>(() => selector.Select(topic, CreateKeyForPartition(1)));
        }
Esempio n. 4
0
        public void PartitionSelectionOnEmptyKeyHashShouldNotFail()
        {
            var selector = new PartitionSelector();
            var topic    = new MetadataResponse.Topic("badPartition", partitions: new [] {
                new MetadataResponse.Partition(0, 0),
                new MetadataResponse.Partition(999, 1)
            });

            Assert.That(selector.Select(topic, new ArraySegment <byte>()), Is.Not.Null);
        }
Esempio n. 5
0
        public void KeyHashShouldThrowExceptionWhenChoosesAPartitionIdThatDoesNotExist()
        {
            var selector = new PartitionSelector();
            var topic    = new MetadataResponse.Topic("badPartition", partitions: new [] {
                new MetadataResponse.Partition(0, 0),
                new MetadataResponse.Partition(999, 1)
            });

            Assert.Throws <RoutingException>(() => selector.Select(topic, CreateKeyForPartition(1)));
        }
Esempio n. 6
0
 public void Setup()
 {
     _topicA = new MetadataResponse.Topic("a", ErrorCode.NONE, new [] {
         new MetadataResponse.Partition(0, 0),
         new MetadataResponse.Partition(1, 1),
     });
     _topicB = new MetadataResponse.Topic("b", ErrorCode.NONE, new [] {
         new MetadataResponse.Partition(0, 0),
         new MetadataResponse.Partition(1, 1),
     });
 }
Esempio n. 7
0
        private static MetadataResult ValidateTopic(MetadataResponse.Topic topic)
        {
            var errorCode = topic.ErrorCode;

            if (errorCode == ErrorResponseCode.None)
            {
                return(new MetadataResult(isValid: true));
            }
            if (errorCode.IsRetryable())
            {
                return(new MetadataResult(errorCode, null, $"topic/{topic.TopicName} returned error code of {errorCode}: Retrying"));
            }
            return(new MetadataResult(errorCode, false, $"topic/{topic.TopicName} returned an error of {errorCode}"));
        }
Esempio n. 8
0
        public MetadataResponse.Partition Select(MetadataResponse.Topic topic, ArraySegment <byte> key)
        {
            if (topic == null)
            {
                throw new ArgumentNullException(nameof(topic));
            }
            if (topic.partition_metadata.Count == 0)
            {
                throw new RoutingException($"No partitions to choose on {topic}.");
            }

            var paritionIndex = _tracker.AddOrUpdate(topic.topic, p => 0, (s, i) => (i + 1) % topic.partition_metadata.Count);

            return(topic.partition_metadata[paritionIndex]);
        }
Esempio n. 9
0
        private BrokerRoute GetBrokerRoute(string topicName, int partitionId, MetadataResponse.Topic topic)
        {
            var partition = topic.Partitions.FirstOrDefault(x => x.PartitionId == partitionId);

            if (partition == null)
            {
                throw new CachedMetadataException($"The topic ({topicName}) has no partitionId {partitionId} defined.")
                      {
                          TopicName = topicName,
                          Partition = partitionId
                      }
            }
            ;

            return(GetCachedRoute(topicName, partition));
        }
Esempio n. 10
0
        public MetadataResponse.Partition Select(MetadataResponse.Topic topic, byte[] key)
        {
            if (topic == null)
            {
                throw new ArgumentNullException(nameof(topic));
            }
            if (topic.Partitions.Count <= 0)
            {
                throw new CachedMetadataException($"topic/{topic.TopicName} has no partitions.")
                      {
                          TopicName = topic.TopicName
                      }
            }
            ;

            long partitionId;
            var  partitions = topic.Partitions;

            if (key == null)
            {
                // use round robin
                var paritionIndex = _roundRobinTracker.AddOrUpdate(topic.TopicName, p => 0, (s, i) => (i + 1) % partitions.Count);
                return(partitions[paritionIndex]);
            }
            else
            {
                // use key hash
                partitionId = Crc32Provider.Compute(key) % partitions.Count;
                var partition = partitions.FirstOrDefault(x => x.PartitionId == partitionId);
                if (partition != null)
                {
                    return(partition);
                }
            }

            throw new CachedMetadataException($"Hash function return partition/{partitionId}, but the available partitions are {string.Join(",", partitions.Select(x => x.PartitionId))}")
                  {
                      TopicName = topic.TopicName,
                      Partition = (int)partitionId
                  };
        }
    }
Esempio n. 11
0
        public void RoundRobinShouldEvenlyDistributeAcrossManyPartitions()
        {
            RoundRobinPartitionSelector.Singleton.Reset();
            const int TotalPartitions = 100;
            var       partitions      = new List <MetadataResponse.Partition>();

            for (int i = 0; i < TotalPartitions; i++)
            {
                partitions.Add(new MetadataResponse.Partition(i, i));
            }
            var topic = new MetadataResponse.Topic("a", partitions: partitions);

            var bag = new ConcurrentBag <MetadataResponse.Partition>();

            Parallel.For(0, TotalPartitions * 3, x => bag.Add(RoundRobinPartitionSelector.Singleton.Select(topic, new ArraySegment <byte>())));

            var eachPartitionHasThree = bag.GroupBy(x => x.partition_id).Count();

            Assert.That(eachPartitionHasThree, Is.EqualTo(TotalPartitions), "Each partition should have received three selections.");
        }
Esempio n. 12
0
        public void RoundRobinShouldThrowExceptionWhenPartitionsAreEmpty()
        {
            var topic = new MetadataResponse.Topic("emptyPartition");

            Assert.Throws <RoutingException>(() => RoundRobinPartitionSelector.Singleton.Select(topic, CreateKeyForPartition(1)));
        }
Esempio n. 13
0
        private TopicConnection GetCachedTopicConnection(string topicName, int partitionId, MetadataResponse.Topic topic)
        {
            var partition = topic.partition_metadata.FirstOrDefault(x => x.partition_id == partitionId);

            if (partition == null)
            {
                throw new RoutingException($"The topic ({topicName}) has no partitionId {partitionId} defined.");
            }

            return(GetCachedTopicConnection(topicName, partition));
        }