kafka-python 2.2.4__tar.gz → 2.2.6__tar.gz

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.
Files changed (168) hide show
  1. {kafka_python-2.2.4 → kafka_python-2.2.6}/CHANGES.md +11 -0
  2. {kafka_python-2.2.4 → kafka_python-2.2.6}/PKG-INFO +1 -1
  3. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/base.py +32 -30
  4. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/producer/record_accumulator.py +3 -0
  5. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/producer/sender.py +6 -5
  6. kafka_python-2.2.6/kafka/version.py +1 -0
  7. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka_python.egg-info/PKG-INFO +1 -1
  8. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_fetcher.py +2 -1
  9. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_sender.py +13 -0
  10. kafka_python-2.2.4/kafka/version.py +0 -1
  11. {kafka_python-2.2.4 → kafka_python-2.2.6}/AUTHORS.md +0 -0
  12. {kafka_python-2.2.4 → kafka_python-2.2.6}/LICENSE +0 -0
  13. {kafka_python-2.2.4 → kafka_python-2.2.6}/MANIFEST.in +0 -0
  14. {kafka_python-2.2.4 → kafka_python-2.2.6}/README.rst +0 -0
  15. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/__init__.py +0 -0
  16. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/admin/__init__.py +0 -0
  17. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/admin/acl_resource.py +0 -0
  18. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/admin/client.py +0 -0
  19. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/admin/config_resource.py +0 -0
  20. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/admin/new_partitions.py +0 -0
  21. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/admin/new_topic.py +0 -0
  22. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/benchmarks/__init__.py +0 -0
  23. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/benchmarks/consumer_performance.py +0 -0
  24. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/benchmarks/load_example.py +0 -0
  25. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/benchmarks/producer_performance.py +0 -0
  26. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/benchmarks/record_batch_compose.py +0 -0
  27. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/benchmarks/record_batch_read.py +0 -0
  28. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/benchmarks/varint_speed.py +0 -0
  29. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/client_async.py +0 -0
  30. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/cluster.py +0 -0
  31. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/codec.py +0 -0
  32. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/conn.py +0 -0
  33. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/consumer/__init__.py +0 -0
  34. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/consumer/fetcher.py +0 -0
  35. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/consumer/group.py +0 -0
  36. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/consumer/subscription_state.py +0 -0
  37. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/__init__.py +0 -0
  38. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/assignors/__init__.py +0 -0
  39. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/assignors/abstract.py +0 -0
  40. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/assignors/range.py +0 -0
  41. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/assignors/roundrobin.py +0 -0
  42. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/assignors/sticky/__init__.py +0 -0
  43. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/assignors/sticky/partition_movements.py +0 -0
  44. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/assignors/sticky/sorted_set.py +0 -0
  45. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/assignors/sticky/sticky_assignor.py +0 -0
  46. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/consumer.py +0 -0
  47. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/heartbeat.py +0 -0
  48. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/coordinator/protocol.py +0 -0
  49. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/errors.py +0 -0
  50. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/future.py +0 -0
  51. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/__init__.py +0 -0
  52. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/compound_stat.py +0 -0
  53. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/dict_reporter.py +0 -0
  54. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/kafka_metric.py +0 -0
  55. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/measurable.py +0 -0
  56. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/measurable_stat.py +0 -0
  57. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/metric_config.py +0 -0
  58. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/metric_name.py +0 -0
  59. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/metrics.py +0 -0
  60. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/metrics_reporter.py +0 -0
  61. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/quota.py +0 -0
  62. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stat.py +0 -0
  63. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/__init__.py +0 -0
  64. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/avg.py +0 -0
  65. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/count.py +0 -0
  66. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/histogram.py +0 -0
  67. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/max_stat.py +0 -0
  68. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/min_stat.py +0 -0
  69. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/percentile.py +0 -0
  70. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/percentiles.py +0 -0
  71. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/rate.py +0 -0
  72. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/sampled_stat.py +0 -0
  73. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/sensor.py +0 -0
  74. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/metrics/stats/total.py +0 -0
  75. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/partitioner/__init__.py +0 -0
  76. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/partitioner/default.py +0 -0
  77. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/producer/__init__.py +0 -0
  78. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/producer/future.py +0 -0
  79. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/producer/kafka.py +0 -0
  80. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/producer/transaction_manager.py +0 -0
  81. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/__init__.py +0 -0
  82. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/abstract.py +0 -0
  83. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/add_offsets_to_txn.py +0 -0
  84. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/add_partitions_to_txn.py +0 -0
  85. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/admin.py +0 -0
  86. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/api.py +0 -0
  87. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/api_versions.py +0 -0
  88. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/broker_api_versions.py +0 -0
  89. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/commit.py +0 -0
  90. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/end_txn.py +0 -0
  91. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/fetch.py +0 -0
  92. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/find_coordinator.py +0 -0
  93. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/frame.py +0 -0
  94. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/group.py +0 -0
  95. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/init_producer_id.py +0 -0
  96. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/list_offsets.py +0 -0
  97. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/message.py +0 -0
  98. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/metadata.py +0 -0
  99. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/offset_for_leader_epoch.py +0 -0
  100. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/parser.py +0 -0
  101. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/pickle.py +0 -0
  102. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/produce.py +0 -0
  103. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/sasl_authenticate.py +0 -0
  104. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/sasl_handshake.py +0 -0
  105. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/struct.py +0 -0
  106. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/txn_offset_commit.py +0 -0
  107. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/protocol/types.py +0 -0
  108. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/record/__init__.py +0 -0
  109. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/record/_crc32c.py +0 -0
  110. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/record/abc.py +0 -0
  111. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/record/default_records.py +0 -0
  112. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/record/legacy_records.py +0 -0
  113. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/record/memory_records.py +0 -0
  114. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/record/util.py +0 -0
  115. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/sasl/__init__.py +0 -0
  116. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/sasl/abc.py +0 -0
  117. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/sasl/gssapi.py +0 -0
  118. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/sasl/msk.py +0 -0
  119. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/sasl/oauth.py +0 -0
  120. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/sasl/plain.py +0 -0
  121. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/sasl/scram.py +0 -0
  122. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/sasl/sspi.py +0 -0
  123. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/serializer/__init__.py +0 -0
  124. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/serializer/abstract.py +0 -0
  125. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/socks5_wrapper.py +0 -0
  126. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/structs.py +0 -0
  127. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/util.py +0 -0
  128. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/vendor/__init__.py +0 -0
  129. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/vendor/enum34.py +0 -0
  130. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/vendor/selectors34.py +0 -0
  131. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/vendor/six.py +0 -0
  132. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka/vendor/socketpair.py +0 -0
  133. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka_python.egg-info/SOURCES.txt +0 -0
  134. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka_python.egg-info/dependency_links.txt +0 -0
  135. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka_python.egg-info/requires.txt +0 -0
  136. {kafka_python-2.2.4 → kafka_python-2.2.6}/kafka_python.egg-info/top_level.txt +0 -0
  137. {kafka_python-2.2.4 → kafka_python-2.2.6}/pyproject.toml +0 -0
  138. {kafka_python-2.2.4 → kafka_python-2.2.6}/setup.cfg +0 -0
  139. {kafka_python-2.2.4 → kafka_python-2.2.6}/setup.py +0 -0
  140. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/integration/__init__.py +0 -0
  141. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/integration/conftest.py +0 -0
  142. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/integration/fixtures.py +0 -0
  143. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/integration/test_admin_integration.py +0 -0
  144. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/integration/test_consumer_group.py +0 -0
  145. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/integration/test_consumer_integration.py +0 -0
  146. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/integration/test_producer_integration.py +0 -0
  147. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/integration/test_sasl_integration.py +0 -0
  148. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_acl_comparisons.py +0 -0
  149. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_admin.py +0 -0
  150. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_api_object_implementation.py +0 -0
  151. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_assignors.py +0 -0
  152. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_client_async.py +0 -0
  153. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_cluster.py +0 -0
  154. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_codec.py +0 -0
  155. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_conn.py +0 -0
  156. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_consumer.py +0 -0
  157. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_coordinator.py +0 -0
  158. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_metrics.py +0 -0
  159. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_object_conversion.py +0 -0
  160. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_package.py +0 -0
  161. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_partition_movements.py +0 -0
  162. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_partitioner.py +0 -0
  163. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_producer.py +0 -0
  164. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_protocol.py +0 -0
  165. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_record_accumulator.py +0 -0
  166. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_subscription_state.py +0 -0
  167. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/test_util.py +0 -0
  168. {kafka_python-2.2.4 → kafka_python-2.2.6}/test/testutil.py +0 -0
@@ -1,3 +1,14 @@
1
+ # 2.2.6 (May 8, 2025)
2
+
3
+ Fixes
4
+ * Only disable heartbeat thread once at beginning of join-group (#2617)
5
+
6
+ # 2.2.5 (May 8, 2025)
7
+
8
+ Fixes
9
+ * Fix producer busy loop with no pending batches (#2616)
10
+ * Fixup py27 fetcher test failure
11
+
1
12
  # 2.2.4 (May 3, 2025)
2
13
 
3
14
  Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kafka-python
3
- Version: 2.2.4
3
+ Version: 2.2.6
4
4
  Summary: Pure Python client for Apache Kafka
5
5
  Author-email: Dana Powers <dana.powers@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dpkp/kafka-python
@@ -19,6 +19,7 @@ from kafka.protocol.group import HeartbeatRequest, JoinGroupRequest, LeaveGroupR
19
19
  from kafka.util import Timer
20
20
 
21
21
  log = logging.getLogger('kafka.coordinator')
22
+ heartbeat_log = logging.getLogger('kafka.coordinator.heartbeat')
22
23
 
23
24
 
24
25
  class MemberState(object):
@@ -449,11 +450,12 @@ class BaseCoordinator(object):
449
450
  timeout_ms=timer.timeout_ms)
450
451
  self.rejoining = True
451
452
 
452
- # fence off the heartbeat thread explicitly so that it cannot
453
- # interfere with the join group. # Note that this must come after
454
- # the call to onJoinPrepare since we must be able to continue
455
- # sending heartbeats if that callback takes some time.
456
- self._disable_heartbeat_thread()
453
+ # fence off the heartbeat thread explicitly so that it cannot
454
+ # interfere with the join group. # Note that this must come after
455
+ # the call to onJoinPrepare since we must be able to continue
456
+ # sending heartbeats if that callback takes some time.
457
+ log.debug("Disabling heartbeat thread during join-group")
458
+ self._disable_heartbeat_thread()
457
459
 
458
460
  # ensure that there are no pending requests to the coordinator.
459
461
  # This is important in particular to avoid resending a pending
@@ -779,7 +781,7 @@ class BaseCoordinator(object):
779
781
  future.failure(error)
780
782
  else:
781
783
  error = error_type()
782
- log.error("Group coordinator lookup for group %s failed: %s",
784
+ log.error("Group Coordinator lookup for group %s failed: %s",
783
785
  self.group_id, error)
784
786
  future.failure(error)
785
787
 
@@ -815,11 +817,11 @@ class BaseCoordinator(object):
815
817
  raise Errors.UnsupportedVersionError('Heartbeat APIs require 0.9+ broker')
816
818
  with self._lock:
817
819
  if self._heartbeat_thread is None:
818
- log.info('Starting new heartbeat thread')
820
+ heartbeat_log.info('Starting new heartbeat thread')
819
821
  self._heartbeat_thread = HeartbeatThread(weakref.proxy(self))
820
822
  self._heartbeat_thread.daemon = True
821
823
  self._heartbeat_thread.start()
822
- log.debug("Started heartbeat thread %s", self._heartbeat_thread.ident)
824
+ heartbeat_log.debug("Started heartbeat thread %s", self._heartbeat_thread.ident)
823
825
 
824
826
  def _disable_heartbeat_thread(self):
825
827
  with self._lock:
@@ -829,7 +831,7 @@ class BaseCoordinator(object):
829
831
  def _close_heartbeat_thread(self, timeout_ms=None):
830
832
  with self._lock:
831
833
  if self._heartbeat_thread is not None:
832
- log.info('Stopping heartbeat thread')
834
+ heartbeat_log.info('Stopping heartbeat thread')
833
835
  try:
834
836
  self._heartbeat_thread.close(timeout_ms=timeout_ms)
835
837
  except ReferenceError:
@@ -893,7 +895,7 @@ class BaseCoordinator(object):
893
895
  request = HeartbeatRequest[version](self.group_id,
894
896
  self._generation.generation_id,
895
897
  self._generation.member_id)
896
- log.debug("Heartbeat: %s[%s] %s", request.group, request.generation_id, request.member_id) # pylint: disable-msg=no-member
898
+ heartbeat_log.debug("Heartbeat: %s[%s] %s", request.group, request.generation_id, request.member_id) # pylint: disable-msg=no-member
897
899
  future = Future()
898
900
  _f = self._client.send(self.coordinator_id, request)
899
901
  _f.add_callback(self._handle_heartbeat_response, future, time.time())
@@ -906,38 +908,38 @@ class BaseCoordinator(object):
906
908
  self._sensors.heartbeat_latency.record((time.time() - send_time) * 1000)
907
909
  error_type = Errors.for_code(response.error_code)
908
910
  if error_type is Errors.NoError:
909
- log.debug("Received successful heartbeat response for group %s",
911
+ heartbeat_log.debug("Received successful heartbeat response for group %s",
910
912
  self.group_id)
911
913
  future.success(None)
912
914
  elif error_type in (Errors.CoordinatorNotAvailableError,
913
915
  Errors.NotCoordinatorError):
914
- log.warning("Heartbeat failed for group %s: coordinator (node %s)"
916
+ heartbeat_log.warning("Heartbeat failed for group %s: coordinator (node %s)"
915
917
  " is either not started or not valid", self.group_id,
916
918
  self.coordinator())
917
919
  self.coordinator_dead(error_type())
918
920
  future.failure(error_type())
919
921
  elif error_type is Errors.RebalanceInProgressError:
920
- log.warning("Heartbeat failed for group %s because it is"
922
+ heartbeat_log.warning("Heartbeat failed for group %s because it is"
921
923
  " rebalancing", self.group_id)
922
924
  self.request_rejoin()
923
925
  future.failure(error_type())
924
926
  elif error_type is Errors.IllegalGenerationError:
925
- log.warning("Heartbeat failed for group %s: generation id is not "
927
+ heartbeat_log.warning("Heartbeat failed for group %s: generation id is not "
926
928
  " current.", self.group_id)
927
929
  self.reset_generation()
928
930
  future.failure(error_type())
929
931
  elif error_type is Errors.UnknownMemberIdError:
930
- log.warning("Heartbeat: local member_id was not recognized;"
932
+ heartbeat_log.warning("Heartbeat: local member_id was not recognized;"
931
933
  " this consumer needs to re-join")
932
934
  self.reset_generation()
933
935
  future.failure(error_type)
934
936
  elif error_type is Errors.GroupAuthorizationFailedError:
935
937
  error = error_type(self.group_id)
936
- log.error("Heartbeat failed: authorization error: %s", error)
938
+ heartbeat_log.error("Heartbeat failed: authorization error: %s", error)
937
939
  future.failure(error)
938
940
  else:
939
941
  error = error_type()
940
- log.error("Heartbeat failed: Unhandled error: %s", error)
942
+ heartbeat_log.error("Heartbeat failed: Unhandled error: %s", error)
941
943
  future.failure(error)
942
944
 
943
945
 
@@ -1003,14 +1005,14 @@ class HeartbeatThread(threading.Thread):
1003
1005
 
1004
1006
  def enable(self):
1005
1007
  with self.coordinator._lock:
1006
- log.debug('Enabling heartbeat thread')
1008
+ heartbeat_log.debug('Enabling heartbeat thread')
1007
1009
  self.enabled = True
1008
1010
  self.coordinator.heartbeat.reset_timeouts()
1009
1011
  self.coordinator._lock.notify()
1010
1012
 
1011
1013
  def disable(self):
1012
1014
  with self.coordinator._lock:
1013
- log.debug('Disabling heartbeat thread')
1015
+ heartbeat_log.debug('Disabling heartbeat thread')
1014
1016
  self.enabled = False
1015
1017
 
1016
1018
  def close(self, timeout_ms=None):
@@ -1032,24 +1034,24 @@ class HeartbeatThread(threading.Thread):
1032
1034
  timeout_ms = self.coordinator.config['heartbeat_interval_ms']
1033
1035
  self.join(timeout_ms / 1000)
1034
1036
  if self.is_alive():
1035
- log.warning("Heartbeat thread did not fully terminate during close")
1037
+ heartbeat_log.warning("Heartbeat thread did not fully terminate during close")
1036
1038
 
1037
1039
  def run(self):
1038
1040
  try:
1039
- log.debug('Heartbeat thread started')
1041
+ heartbeat_log.debug('Heartbeat thread started')
1040
1042
  while not self.closed:
1041
1043
  self._run_once()
1042
1044
 
1043
1045
  except ReferenceError:
1044
- log.debug('Heartbeat thread closed due to coordinator gc')
1046
+ heartbeat_log.debug('Heartbeat thread closed due to coordinator gc')
1045
1047
 
1046
1048
  except RuntimeError as e:
1047
- log.error("Heartbeat thread for group %s failed due to unexpected error: %s",
1049
+ heartbeat_log.error("Heartbeat thread for group %s failed due to unexpected error: %s",
1048
1050
  self.coordinator.group_id, e)
1049
1051
  self.failed = e
1050
1052
 
1051
1053
  finally:
1052
- log.debug('Heartbeat thread closed')
1054
+ heartbeat_log.debug('Heartbeat thread closed')
1053
1055
 
1054
1056
  def _run_once(self):
1055
1057
  with self.coordinator._client._lock, self.coordinator._lock:
@@ -1063,16 +1065,16 @@ class HeartbeatThread(threading.Thread):
1063
1065
 
1064
1066
  with self.coordinator._lock:
1065
1067
  if not self.enabled:
1066
- log.debug('Heartbeat disabled. Waiting')
1068
+ heartbeat_log.debug('Heartbeat disabled. Waiting')
1067
1069
  self.coordinator._lock.wait()
1068
- log.debug('Heartbeat re-enabled.')
1070
+ heartbeat_log.debug('Heartbeat re-enabled.')
1069
1071
  return
1070
1072
 
1071
1073
  if self.coordinator.state is not MemberState.STABLE:
1072
1074
  # the group is not stable (perhaps because we left the
1073
1075
  # group or because the coordinator kicked us out), so
1074
1076
  # disable heartbeats and wait for the main thread to rejoin.
1075
- log.debug('Group state is not stable, disabling heartbeats')
1077
+ heartbeat_log.debug('Group state is not stable, disabling heartbeats')
1076
1078
  self.disable()
1077
1079
  return
1078
1080
 
@@ -1088,14 +1090,14 @@ class HeartbeatThread(threading.Thread):
1088
1090
  # the session timeout has expired without seeing a
1089
1091
  # successful heartbeat, so we should probably make sure
1090
1092
  # the coordinator is still healthy.
1091
- log.warning('Heartbeat session expired, marking coordinator dead')
1093
+ heartbeat_log.warning('Heartbeat session expired, marking coordinator dead')
1092
1094
  self.coordinator.coordinator_dead('Heartbeat session expired')
1093
1095
 
1094
1096
  elif self.coordinator.heartbeat.poll_timeout_expired():
1095
1097
  # the poll timeout has expired, which means that the
1096
1098
  # foreground thread has stalled in between calls to
1097
1099
  # poll(), so we explicitly leave the group.
1098
- log.warning('Heartbeat poll expired, leaving group')
1100
+ heartbeat_log.warning('Heartbeat poll expired, leaving group')
1099
1101
  ### XXX
1100
1102
  # maybe_leave_group acquires client + coordinator lock;
1101
1103
  # if we hold coordinator lock before calling, we risk deadlock
@@ -1106,7 +1108,7 @@ class HeartbeatThread(threading.Thread):
1106
1108
  elif not self.coordinator.heartbeat.should_heartbeat():
1107
1109
  # poll again after waiting for the retry backoff in case
1108
1110
  # the heartbeat failed or the coordinator disconnected
1109
- log.log(0, 'Not ready to heartbeat, waiting')
1111
+ heartbeat_log.log(0, 'Not ready to heartbeat, waiting')
1110
1112
  self.coordinator._lock.wait(self.coordinator.config['retry_backoff_ms'] / 1000)
1111
1113
 
1112
1114
  else:
@@ -328,6 +328,9 @@ class RecordAccumulator(object):
328
328
  finally:
329
329
  self._appends_in_progress.decrement()
330
330
 
331
+ def reset_next_batch_expiry_time(self):
332
+ self._next_batch_expiry_time_ms = float('inf')
333
+
331
334
  def maybe_update_next_batch_expiry_time(self, batch):
332
335
  self._next_batch_expiry_time_ms = min(self._next_batch_expiry_time_ms, batch.created * 1000 + self.delivery_timeout_ms)
333
336
 
@@ -77,7 +77,7 @@ class Sender(threading.Thread):
77
77
  queue.pop()
78
78
  heapq.heapify(queue)
79
79
 
80
- def _get_expired_inflight_batches(self):
80
+ def _get_expired_inflight_batches(self, now=None):
81
81
  """Get the in-flight batches that has reached delivery timeout."""
82
82
  expired_batches = []
83
83
  to_remove = []
@@ -174,7 +174,7 @@ class Sender(threading.Thread):
174
174
  def _send_producer_data(self, now=None):
175
175
  now = time.time() if now is None else now
176
176
  # get the list of partitions with data ready to send
177
- result = self._accumulator.ready(self._metadata)
177
+ result = self._accumulator.ready(self._metadata, now=now)
178
178
  ready_nodes, next_ready_check_delay, unknown_leaders_exist = result
179
179
 
180
180
  # if there are any partitions whose leaders are not known yet, force
@@ -195,7 +195,7 @@ class Sender(threading.Thread):
195
195
 
196
196
  # create produce requests
197
197
  batches_by_node = self._accumulator.drain(
198
- self._metadata, ready_nodes, self.config['max_request_size'])
198
+ self._metadata, ready_nodes, self.config['max_request_size'], now=now)
199
199
 
200
200
  for batch_list in six.itervalues(batches_by_node):
201
201
  for batch in batch_list:
@@ -209,8 +209,9 @@ class Sender(threading.Thread):
209
209
  for batch in batch_list:
210
210
  self._accumulator.muted.add(batch.topic_partition)
211
211
 
212
- expired_batches = self._accumulator.expired_batches()
213
- expired_batches.extend(self._get_expired_inflight_batches())
212
+ self._accumulator.reset_next_batch_expiry_time()
213
+ expired_batches = self._accumulator.expired_batches(now=now)
214
+ expired_batches.extend(self._get_expired_inflight_batches(now=now))
214
215
 
215
216
  if expired_batches:
216
217
  log.debug("%s: Expired %s batches in accumulator", str(self), len(expired_batches))
@@ -0,0 +1 @@
1
+ __version__ = '2.2.6'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kafka-python
3
- Version: 2.2.4
3
+ Version: 2.2.6
4
4
  Summary: Pure Python client for Apache Kafka
5
5
  Author-email: Dana Powers <dana.powers@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dpkp/kafka-python
@@ -140,7 +140,8 @@ def test__reset_offsets_async(fetcher, mocker):
140
140
  fetcher._subscriptions.assign_from_subscribed([tp0, tp1])
141
141
  fetcher._subscriptions.request_offset_reset(tp0)
142
142
  fetcher._subscriptions.request_offset_reset(tp1)
143
- mocker.patch.object(fetcher._client.cluster, "leader_for_partition", side_effect=[0, 1])
143
+ leaders = {tp0: 0, tp1: 1}
144
+ mocker.patch.object(fetcher._client.cluster, "leader_for_partition", side_effect=lambda tp: leaders[tp])
144
145
  mocker.patch.object(fetcher._client, 'ready', return_value=True)
145
146
  future1 = Future()
146
147
  future2 = Future()
@@ -240,3 +240,16 @@ def test_maybe_wait_for_producer_id():
240
240
 
241
241
  def test_run_once():
242
242
  pass
243
+
244
+
245
+ def test__send_producer_data_expiry_time_reset(sender, accumulator, mocker):
246
+ now = time.time()
247
+ tp = TopicPartition('foo', 0)
248
+ mocker.patch.object(sender, '_failed_produce')
249
+ result = accumulator.append(tp, 0, b'key', b'value', [], now=now)
250
+ poll_timeout_ms = sender._send_producer_data(now=now)
251
+ assert poll_timeout_ms == accumulator.config['delivery_timeout_ms']
252
+ sender._failed_produce.assert_not_called()
253
+ now += accumulator.config['delivery_timeout_ms']
254
+ poll_timeout_ms = sender._send_producer_data(now=now)
255
+ assert poll_timeout_ms > 0
@@ -1 +0,0 @@
1
- __version__ = '2.2.4'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes