def load_metadata_for_topics(self, *topics): """ Discover brokers and metadata for a set of topics. This function is called lazily whenever metadata is unavailable. """ request_id = self._next_id() request = KafkaProtocol.encode_metadata_request( self.client_id, request_id, topics) response = self._send_broker_unaware_request(request_id, request) (brokers, topics) = KafkaProtocol.decode_metadata_response(response) log.debug("Broker metadata: %s", brokers) log.debug("Topic metadata: %s", topics) self.brokers = brokers for topic, partitions in topics.items(): self.reset_topic_metadata(topic) if not partitions: log.warning('No partitions for %s', topic) continue self.topic_partitions[topic] = [] for partition, meta in partitions.items(): self.topic_partitions[topic].append(partition) topic_part = TopicAndPartition(topic, partition) if meta.leader == -1: log.warning('No leader for topic %s partition %s', topic, partition) self.topics_to_brokers[topic_part] = None else: self.topics_to_brokers[topic_part] = brokers[meta.leader]
def test_send_broker_unaware_request_fail(self): 'Tests that call fails when all hosts are unavailable' mocked_conns = { ('kafka01', 9092): MagicMock(), ('kafka02', 9092): MagicMock() } # inject KafkaConnection side effects mocked_conns[('kafka01', 9092)].send.side_effect = RuntimeError("kafka01 went away (unittest)") mocked_conns[('kafka02', 9092)].send.side_effect = RuntimeError("Kafka02 went away (unittest)") def mock_get_conn(host, port): return mocked_conns[(host, port)] # patch to avoid making requests before we want it with patch.object(KafkaClient, 'load_metadata_for_topics'): with patch.object(KafkaClient, '_get_conn', side_effect=mock_get_conn): client = KafkaClient(hosts=['kafka01:9092', 'kafka02:9092']) req = KafkaProtocol.encode_metadata_request(b'client', 0) with self.assertRaises(KafkaUnavailableError): client._send_broker_unaware_request(payloads=['fake request'], encoder_fn=MagicMock(return_value='fake encoded message'), decoder_fn=lambda x: x) for key, conn in six.iteritems(mocked_conns): conn.send.assert_called_with(ANY, 'fake encoded message')
def load_metadata_for_topics(self, *topics): """ Discover brokers and metadata for a set of topics. This function is called lazily whenever metadata is unavailable. """ request_id = self._next_id() request = KafkaProtocol.encode_metadata_request(self.client_id, request_id, topics) response = self._send_broker_unaware_request(request_id, request) (brokers, topics) = KafkaProtocol.decode_metadata_response(response) log.debug("Broker metadata: %s", brokers) log.debug("Topic metadata: %s", topics) self.brokers = brokers for topic, partitions in topics.items(): self.reset_topic_metadata(topic) if not partitions: log.warning('No partitions for %s', topic) continue self.topic_partitions[topic] = [] for partition, meta in partitions.items(): self.topic_partitions[topic].append(partition) topic_part = TopicAndPartition(topic, partition) if meta.leader == -1: log.warning('No leader for topic %s partition %s', topic, partition) self.topics_to_brokers[topic_part] = None else: self.topics_to_brokers[topic_part] = brokers[meta.leader]
def test_send_broker_unaware_request_fail(self): 'Tests that call fails when all hosts are unavailable' mocked_conns = { ('kafka01', 9092): MagicMock(), ('kafka02', 9092): MagicMock() } # inject KafkaConnection side effects mocked_conns[('kafka01', 9092)].send.side_effect = RuntimeError( "kafka01 went away (unittest)") mocked_conns[('kafka02', 9092)].send.side_effect = RuntimeError( "Kafka02 went away (unittest)") def mock_get_conn(host, port): return mocked_conns[(host, port)] # patch to avoid making requests before we want it with patch.object(KafkaClient, 'load_metadata_for_topics'): with patch.object(KafkaClient, '_get_conn', side_effect=mock_get_conn): client = KafkaClient(hosts=['kafka01:9092', 'kafka02:9092']) req = KafkaProtocol.encode_metadata_request(b'client', 0) with self.assertRaises(KafkaUnavailableError): client._send_broker_unaware_request( payloads=['fake request'], encoder_fn=MagicMock( return_value='fake encoded message'), decoder_fn=lambda x: x) for key, conn in six.iteritems(mocked_conns): conn.send.assert_called_with(ANY, 'fake encoded message')
def test_encode_metadata_request_no_topics(self): expected = "".join([ struct.pack(">i", 17), # Total length of the request struct.pack('>h', 3), # API key metadata fetch struct.pack('>h', 0), # API version struct.pack('>i', 4), # Correlation ID struct.pack('>h3s', 3, "cid"), # The client ID struct.pack('>i', 0), # No topics, give all the data! ]) encoded = KafkaProtocol.encode_metadata_request("cid", 4) self.assertEqual(encoded, expected)
def test_encode_metadata_request_no_topics(self): expected = b"".join([ struct.pack(">i", 17), # Total length of the request struct.pack('>h', 3), # API key metadata fetch struct.pack('>h', 0), # API version struct.pack('>i', 4), # Correlation ID struct.pack('>h3s', 3, b"cid"),# The client ID struct.pack('>i', 0), # No topics, give all the data! ]) encoded = KafkaProtocol.encode_metadata_request(b"cid", 4) self.assertEqual(encoded, expected)
def test_encode_metadata_request_with_topics(self): expected = "".join([ struct.pack(">i", 25), # Total length of the request struct.pack('>h', 3), # API key metadata fetch struct.pack('>h', 0), # API version struct.pack('>i', 4), # Correlation ID struct.pack('>h3s', 3, "cid"), # The client ID struct.pack('>i', 2), # Number of topics in the request struct.pack('>h2s', 2, "t1"), # Topic "t1" struct.pack('>h2s', 2, "t2"), # Topic "t2" ]) encoded = KafkaProtocol.encode_metadata_request("cid", 4, ["t1", "t2"]) self.assertEqual(encoded, expected)
def test_encode_metadata_request_with_topics(self): expected = b"".join([ struct.pack(">i", 25), # Total length of the request struct.pack('>h', 3), # API key metadata fetch struct.pack('>h', 0), # API version struct.pack('>i', 4), # Correlation ID struct.pack('>h3s', 3, b"cid"),# The client ID struct.pack('>i', 2), # Number of topics in the request struct.pack('>h2s', 2, b"t1"), # Topic "t1" struct.pack('>h2s', 2, b"t2"), # Topic "t2" ]) encoded = KafkaProtocol.encode_metadata_request(b"cid", 4, [b"t1", b"t2"]) self.assertEqual(encoded, expected)
def _load_metadata_for_topics(self, *topics): """ Discover brokers and metadata for a set of topics. This method will recurse in the event of a retry. """ requestId = self._next_id() request = KafkaProtocol.encode_metadata_request( self.client_id, requestId, topics) response = self._send_broker_unaware_request(requestId, request) if response is None: raise Exception("All servers failed to process request") (brokers, topics) = KafkaProtocol.decode_metadata_response(response) log.debug("Broker metadata: %s", brokers) log.debug("Topic metadata: %s", topics) self.brokers = brokers self.topics_to_brokers = {} for topic, partitions in topics.items(): # Clear the list once before we add it. This removes stale entries # and avoids duplicates self.topic_partitions.pop(topic, None) if not partitions: log.warn( "Partition is unassigned, delay for 1s and retry. Have you created {} on zookeeper?" .format(topic)) time.sleep(1) self._load_metadata_for_topics(topic) break for partition, meta in partitions.items(): if meta.leader == -1: log.info("Partition is unassigned, delay for 1s and retry") time.sleep(1) self._load_metadata_for_topics(topic) else: topic_part = TopicAndPartition(topic, partition) self.topics_to_brokers[topic_part] = brokers[meta.leader] self.topic_partitions[topic].append(partition)
def _load_metadata_for_topics(self, *topics): """ Discover brokers and metadata for a set of topics. This method will recurse in the event of a retry. """ request_id = self._next_id() request = KafkaProtocol.encode_metadata_request(self.client_id, request_id, topics) response = self._send_broker_unaware_request(request_id, request) if response is None: raise Exception("All servers failed to process request") (brokers, topics) = KafkaProtocol.decode_metadata_response(response) log.debug("Broker metadata: %s", brokers) log.debug("Topic metadata: %s", topics) self.brokers = brokers self.topics_to_brokers = {} for topic, partitions in topics.items(): # Clear the list once before we add it. This removes stale entries # and avoids duplicates self.topic_partitions.pop(topic, None) if not partitions: log.info("Partition is unassigned, delay for 1s and retry") time.sleep(1) self._load_metadata_for_topics(topic) break for partition, meta in partitions.items(): if meta.leader == -1: log.info("Partition is unassigned, delay for 1s and retry") time.sleep(1) self._load_metadata_for_topics(topic) else: topic_part = TopicAndPartition(topic, partition) self.topics_to_brokers[topic_part] = brokers[meta.leader] self.topic_partitions[topic].append(partition)
def test_send_broker_unaware_request_fail(self, load_metadata, conn): mocked_conns = { ('kafka01', 9092): MagicMock(), ('kafka02', 9092): MagicMock() } for val in mocked_conns.values(): mock_conn(val, success=False) def mock_get_conn(host, port, afi): return mocked_conns[(host, port)] conn.side_effect = mock_get_conn client = SimpleClient(hosts=['kafka01:9092', 'kafka02:9092']) req = KafkaProtocol.encode_metadata_request() with self.assertRaises(KafkaUnavailableError): client._send_broker_unaware_request(payloads=['fake request'], encoder_fn=MagicMock(return_value='fake encoded message'), decoder_fn=lambda x: x) for key, conn in six.iteritems(mocked_conns): conn.send.assert_called_with('fake encoded message')
def test_encode_metadata_request_with_topics(self): encoded = KafkaProtocol.encode_metadata_request("cid", 4, ["t1", "t2"]) self.assertEqual(encoded, '\x00\x00\x00\x19\x00\x03\x00\x00\x00\x00' '\x00\x04\x00\x03cid\x00\x00\x00\x02\x00\x02' 't1\x00\x02t2')
def test_encode_metadata_request_no_topics(self): encoded = KafkaProtocol.encode_metadata_request("cid", 4) self.assertEqual(encoded, '\x00\x00\x00\x11\x00\x03\x00\x00\x00\x00' '\x00\x04\x00\x03cid\x00\x00\x00\x00')
def test_encode_metadata_request_with_topics(self): encoded = KafkaProtocol.encode_metadata_request("cid", 4, ["t1", "t2"]) self.assertEqual( encoded, '\x00\x00\x00\x19\x00\x03\x00\x00\x00\x00' '\x00\x04\x00\x03cid\x00\x00\x00\x02\x00\x02' 't1\x00\x02t2')
def test_encode_metadata_request_no_topics(self): encoded = KafkaProtocol.encode_metadata_request("cid", 4) self.assertEqual( encoded, '\x00\x00\x00\x11\x00\x03\x00\x00\x00\x00' '\x00\x04\x00\x03cid\x00\x00\x00\x00')