kafka-python 2.1.5__py2.py3-none-any.whl → 2.2.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
kafka/admin/client.py CHANGED
@@ -15,7 +15,7 @@ from kafka.client_async import KafkaClient, selectors
15
15
  from kafka.coordinator.protocol import ConsumerProtocolMemberMetadata, ConsumerProtocolMemberAssignment, ConsumerProtocol
16
16
  import kafka.errors as Errors
17
17
  from kafka.errors import (
18
- IncompatibleBrokerVersion, KafkaConfigurationError, NotControllerError, UnknownTopicOrPartitionError,
18
+ IncompatibleBrokerVersion, KafkaConfigurationError, UnknownTopicOrPartitionError,
19
19
  UnrecognizedBrokerVersion, IllegalArgumentError)
20
20
  from kafka.metrics import MetricConfig, Metrics
21
21
  from kafka.protocol.admin import (
@@ -411,7 +411,7 @@ class KafkaAdminClient(object):
411
411
  # extra values (usually the error_message)
412
412
  for topic, error_code in map(lambda e: e[:2], topic_error_tuples):
413
413
  error_type = Errors.for_code(error_code)
414
- if tries and error_type is NotControllerError:
414
+ if tries and error_type is Errors.NotControllerError:
415
415
  # No need to inspect the rest of the errors for
416
416
  # non-retriable errors because NotControllerError should
417
417
  # either be thrown for all errors or no errors.
@@ -431,13 +431,13 @@ class KafkaAdminClient(object):
431
431
  for topic, partition_results in response.replication_election_results:
432
432
  for partition_id, error_code in map(lambda e: e[:2], partition_results):
433
433
  error_type = Errors.for_code(error_code)
434
- if tries and error_type is NotControllerError:
434
+ if tries and error_type is Errors.NotControllerError:
435
435
  # No need to inspect the rest of the errors for
436
436
  # non-retriable errors because NotControllerError should
437
437
  # either be thrown for all errors or no errors.
438
438
  self._refresh_controller_id()
439
439
  return False
440
- elif error_type not in [Errors.NoError, Errors.ElectionNotNeeded]:
440
+ elif error_type not in (Errors.NoError, Errors.ElectionNotNeededError):
441
441
  raise error_type(
442
442
  "Request '{}' failed with response '{}'."
443
443
  .format(request, response))
@@ -1460,9 +1460,9 @@ class KafkaAdminClient(object):
1460
1460
  list: List of tuples of Consumer Groups.
1461
1461
 
1462
1462
  Raises:
1463
- GroupCoordinatorNotAvailableError: The coordinator is not
1463
+ CoordinatorNotAvailableError: The coordinator is not
1464
1464
  available, so cannot process requests.
1465
- GroupLoadInProgressError: The coordinator is loading and
1465
+ CoordinatorLoadInProgressError: The coordinator is loading and
1466
1466
  hence can't process requests.
1467
1467
  """
1468
1468
  # While we return a list, internally use a set to prevent duplicates
kafka/client_async.py CHANGED
@@ -27,7 +27,7 @@ from kafka.metrics.stats import Avg, Count, Rate
27
27
  from kafka.metrics.stats.rate import TimeUnit
28
28
  from kafka.protocol.broker_api_versions import BROKER_API_VERSIONS
29
29
  from kafka.protocol.metadata import MetadataRequest
30
- from kafka.util import Dict, WeakMethod, ensure_valid_topic_name
30
+ from kafka.util import Dict, WeakMethod, ensure_valid_topic_name, timeout_ms_fn
31
31
  # Although this looks unused, it actually monkey-patches socket.socketpair()
32
32
  # and should be left in as long as we're using socket.socketpair() in this file
33
33
  from kafka.vendor import socketpair # noqa: F401
@@ -400,6 +400,11 @@ class KafkaClient(object):
400
400
  return True
401
401
  return False
402
402
 
403
+ def connection_failed(self, node_id):
404
+ if node_id not in self._conns:
405
+ return False
406
+ return self._conns[node_id].connect_failed()
407
+
403
408
  def _should_recycle_connection(self, conn):
404
409
  # Never recycle unless disconnected
405
410
  if not conn.disconnected():
@@ -1110,7 +1115,7 @@ class KafkaClient(object):
1110
1115
  return version
1111
1116
 
1112
1117
  def wakeup(self):
1113
- if self._waking or self._wake_w is None:
1118
+ if self._closed or self._waking or self._wake_w is None:
1114
1119
  return
1115
1120
  with self._wake_lock:
1116
1121
  try:
@@ -1157,6 +1162,39 @@ class KafkaClient(object):
1157
1162
  else:
1158
1163
  return False
1159
1164
 
1165
+ def await_ready(self, node_id, timeout_ms=30000):
1166
+ """
1167
+ Invokes `poll` to discard pending disconnects, followed by `client.ready` and 0 or more `client.poll`
1168
+ invocations until the connection to `node` is ready, the timeoutMs expires or the connection fails.
1169
+
1170
+ It returns `true` if the call completes normally or `false` if the timeoutMs expires. If the connection fails,
1171
+ an `IOException` is thrown instead. Note that if the `NetworkClient` has been configured with a positive
1172
+ connection timeoutMs, it is possible for this method to raise an `IOException` for a previous connection which
1173
+ has recently disconnected.
1174
+
1175
+ This method is useful for implementing blocking behaviour on top of the non-blocking `NetworkClient`, use it with
1176
+ care.
1177
+ """
1178
+ inner_timeout_ms = timeout_ms_fn(timeout_ms, None)
1179
+ self.poll(timeout_ms=0)
1180
+ if self.is_ready(node_id):
1181
+ return True
1182
+
1183
+ while not self.is_ready(node_id) and inner_timeout_ms() > 0:
1184
+ if self.connection_failed(node_id):
1185
+ raise Errors.KafkaConnectionError("Connection to %s failed." % (node_id,))
1186
+ self.maybe_connect(node_id)
1187
+ self.poll(timeout_ms=inner_timeout_ms())
1188
+ return self.is_ready(node_id)
1189
+
1190
+ def send_and_receive(self, node_id, request):
1191
+ future = self.send(node_id, request)
1192
+ self.poll(future=future)
1193
+ assert future.is_done
1194
+ if future.failed():
1195
+ raise future.exception
1196
+ return future.value
1197
+
1160
1198
 
1161
1199
  # OrderedDict requires python2.7+
1162
1200
  try:
kafka/cluster.py CHANGED
@@ -47,7 +47,7 @@ class ClusterMetadata(object):
47
47
  self._brokers = {} # node_id -> BrokerMetadata
48
48
  self._partitions = {} # topic -> partition -> PartitionMetadata
49
49
  self._broker_partitions = collections.defaultdict(set) # node_id -> {TopicPartition...}
50
- self._groups = {} # group_name -> node_id
50
+ self._coordinators = {} # (coord_type, coord_key) -> node_id
51
51
  self._last_refresh_ms = 0
52
52
  self._last_successful_refresh_ms = 0
53
53
  self._need_update = True
@@ -167,7 +167,7 @@ class ClusterMetadata(object):
167
167
  node_id (int or str) for group coordinator, -1 if coordinator unknown
168
168
  None if the group does not exist.
169
169
  """
170
- return self._groups.get(group)
170
+ return self._coordinators.get(('group', group))
171
171
 
172
172
  def ttl(self):
173
173
  """Milliseconds until metadata should be refreshed"""
@@ -202,6 +202,10 @@ class ClusterMetadata(object):
202
202
  self._future = Future()
203
203
  return self._future
204
204
 
205
+ @property
206
+ def need_update(self):
207
+ return self._need_update
208
+
205
209
  def topics(self, exclude_internal_topics=True):
206
210
  """Get set of known topics.
207
211
 
@@ -364,24 +368,25 @@ class ClusterMetadata(object):
364
368
  """Remove a previously added listener callback"""
365
369
  self._listeners.remove(listener)
366
370
 
367
- def add_group_coordinator(self, group, response):
368
- """Update with metadata for a group coordinator
371
+ def add_coordinator(self, response, coord_type, coord_key):
372
+ """Update with metadata for a group or txn coordinator
369
373
 
370
374
  Arguments:
371
- group (str): name of group from FindCoordinatorRequest
372
375
  response (FindCoordinatorResponse): broker response
376
+ coord_type (str): 'group' or 'transaction'
377
+ coord_key (str): consumer_group or transactional_id
373
378
 
374
379
  Returns:
375
380
  string: coordinator node_id if metadata is updated, None on error
376
381
  """
377
- log.debug("Updating coordinator for %s: %s", group, response)
382
+ log.debug("Updating coordinator for %s/%s: %s", coord_type, coord_key, response)
378
383
  error_type = Errors.for_code(response.error_code)
379
384
  if error_type is not Errors.NoError:
380
385
  log.error("FindCoordinatorResponse error: %s", error_type)
381
- self._groups[group] = -1
386
+ self._coordinators[(coord_type, coord_key)] = -1
382
387
  return
383
388
 
384
- # Use a coordinator-specific node id so that group requests
389
+ # Use a coordinator-specific node id so that requests
385
390
  # get a dedicated connection
386
391
  node_id = 'coordinator-{}'.format(response.coordinator_id)
387
392
  coordinator = BrokerMetadata(
@@ -390,9 +395,9 @@ class ClusterMetadata(object):
390
395
  response.port,
391
396
  None)
392
397
 
393
- log.info("Group coordinator for %s is %s", group, coordinator)
398
+ log.info("Coordinator for %s/%s is %s", coord_type, coord_key, coordinator)
394
399
  self._coordinator_brokers[node_id] = coordinator
395
- self._groups[group] = node_id
400
+ self._coordinators[(coord_type, coord_key)] = node_id
396
401
  return node_id
397
402
 
398
403
  def with_partitions(self, partitions_to_add):
@@ -401,7 +406,7 @@ class ClusterMetadata(object):
401
406
  new_metadata._brokers = copy.deepcopy(self._brokers)
402
407
  new_metadata._partitions = copy.deepcopy(self._partitions)
403
408
  new_metadata._broker_partitions = copy.deepcopy(self._broker_partitions)
404
- new_metadata._groups = copy.deepcopy(self._groups)
409
+ new_metadata._coordinators = copy.deepcopy(self._coordinators)
405
410
  new_metadata.internal_topics = copy.deepcopy(self.internal_topics)
406
411
  new_metadata.unauthorized_topics = copy.deepcopy(self.unauthorized_topics)
407
412
 
@@ -415,5 +420,5 @@ class ClusterMetadata(object):
415
420
  return new_metadata
416
421
 
417
422
  def __str__(self):
418
- return 'ClusterMetadata(brokers: %d, topics: %d, groups: %d)' % \
419
- (len(self._brokers), len(self._partitions), len(self._groups))
423
+ return 'ClusterMetadata(brokers: %d, topics: %d, coordinators: %d)' % \
424
+ (len(self._brokers), len(self._partitions), len(self._coordinators))
kafka/conn.py CHANGED
@@ -813,7 +813,7 @@ class BrokerConnection(object):
813
813
  log.info('%s: %s', self, self._sasl_mechanism.auth_details())
814
814
  return future.success(True)
815
815
  else:
816
- return future.failure(Errors.AuthenticationFailedError('Failed to authenticate via SASL %s' % self.config['sasl_mechanism']))
816
+ return future.failure(Errors.SaslAuthenticationFailedError('Failed to authenticate via SASL %s' % self.config['sasl_mechanism']))
817
817
 
818
818
  def blacked_out(self):
819
819
  """
@@ -934,7 +934,8 @@ class BrokerConnection(object):
934
934
  if self.state is ConnectionStates.DISCONNECTED:
935
935
  return
936
936
  log.log(logging.ERROR if error else logging.INFO, '%s: Closing connection. %s', self, error or '')
937
- self._update_reconnect_backoff()
937
+ if error:
938
+ self._update_reconnect_backoff()
938
939
  self._api_versions_future = None
939
940
  self._sasl_auth_future = None
940
941
  self._init_sasl_mechanism()