kafka-python 2.1.4__tar.gz → 2.2.0__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 (171) hide show
  1. {kafka_python-2.1.4 → kafka_python-2.2.0}/CHANGES.md +58 -0
  2. {kafka_python-2.1.4 → kafka_python-2.2.0}/PKG-INFO +3 -1
  3. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/client.py +6 -6
  4. kafka_python-2.2.0/kafka/benchmarks/consumer_performance.py +142 -0
  5. kafka_python-2.2.0/kafka/benchmarks/load_example.py +110 -0
  6. kafka_python-2.2.0/kafka/benchmarks/producer_performance.py +153 -0
  7. kafka_python-2.2.0/kafka/benchmarks/record_batch_compose.py +78 -0
  8. kafka_python-2.2.0/kafka/benchmarks/record_batch_read.py +83 -0
  9. kafka_python-2.2.0/kafka/benchmarks/varint_speed.py +434 -0
  10. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/client_async.py +40 -2
  11. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/cluster.py +18 -13
  12. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/conn.py +7 -5
  13. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/consumer/fetcher.py +309 -194
  14. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/consumer/group.py +73 -63
  15. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/consumer/subscription_state.py +84 -36
  16. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/base.py +60 -26
  17. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/consumer.py +40 -40
  18. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/errors.py +68 -93
  19. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/compound_stat.py +2 -0
  20. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/kafka_metric.py +3 -1
  21. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/metric_config.py +2 -0
  22. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/metric_name.py +1 -0
  23. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/quota.py +2 -0
  24. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/avg.py +2 -0
  25. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/count.py +2 -0
  26. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/histogram.py +6 -0
  27. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/max_stat.py +2 -0
  28. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/min_stat.py +2 -0
  29. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/percentile.py +2 -0
  30. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/percentiles.py +3 -0
  31. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/rate.py +3 -0
  32. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/sampled_stat.py +2 -0
  33. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/sensor.py +4 -0
  34. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/total.py +2 -0
  35. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/producer/future.py +3 -3
  36. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/producer/kafka.py +291 -58
  37. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/producer/record_accumulator.py +293 -214
  38. kafka_python-2.2.0/kafka/producer/sender.py +762 -0
  39. kafka_python-2.2.0/kafka/producer/transaction_manager.py +981 -0
  40. kafka_python-2.2.0/kafka/protocol/add_offsets_to_txn.py +59 -0
  41. kafka_python-2.2.0/kafka/protocol/add_partitions_to_txn.py +63 -0
  42. kafka_python-2.2.0/kafka/protocol/end_txn.py +58 -0
  43. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/fetch.py +6 -0
  44. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/group.py +17 -3
  45. kafka_python-2.2.0/kafka/protocol/init_producer_id.py +46 -0
  46. kafka_python-2.2.0/kafka/protocol/txn_offset_commit.py +78 -0
  47. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/abc.py +10 -0
  48. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/default_records.py +101 -12
  49. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/legacy_records.py +12 -3
  50. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/memory_records.py +54 -6
  51. kafka_python-2.2.0/kafka/vendor/__init__.py +0 -0
  52. kafka_python-2.2.0/kafka/version.py +1 -0
  53. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/PKG-INFO +3 -1
  54. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/SOURCES.txt +23 -6
  55. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/requires.txt +3 -0
  56. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/top_level.txt +1 -0
  57. {kafka_python-2.1.4 → kafka_python-2.2.0}/pyproject.toml +1 -0
  58. kafka_python-2.2.0/test/integration/__init__.py +0 -0
  59. kafka_python-2.2.0/test/integration/conftest.py +168 -0
  60. kafka_python-2.2.0/test/integration/fixtures.py +765 -0
  61. {kafka_python-2.1.4/test → kafka_python-2.2.0/test/integration}/test_admin_integration.py +2 -2
  62. {kafka_python-2.1.4/test → kafka_python-2.2.0/test/integration}/test_consumer_group.py +2 -1
  63. {kafka_python-2.1.4/test → kafka_python-2.2.0/test/integration}/test_consumer_integration.py +3 -2
  64. kafka_python-2.1.4/test/test_producer.py → kafka_python-2.2.0/test/integration/test_producer_integration.py +81 -32
  65. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_client_async.py +135 -136
  66. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_conn.py +4 -1
  67. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_coordinator.py +35 -27
  68. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_fetcher.py +213 -70
  69. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_metrics.py +2 -18
  70. kafka_python-2.2.0/test/test_producer.py +35 -0
  71. kafka_python-2.2.0/test/test_record_accumulator.py +266 -0
  72. kafka_python-2.2.0/test/test_sender.py +242 -0
  73. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_subscription_state.py +2 -2
  74. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/testutil.py +16 -0
  75. kafka_python-2.1.4/kafka/producer/buffer.py +0 -115
  76. kafka_python-2.1.4/kafka/producer/sender.py +0 -482
  77. kafka_python-2.1.4/kafka/version.py +0 -1
  78. kafka_python-2.1.4/test/test_sender.py +0 -51
  79. {kafka_python-2.1.4 → kafka_python-2.2.0}/AUTHORS.md +0 -0
  80. {kafka_python-2.1.4 → kafka_python-2.2.0}/LICENSE +0 -0
  81. {kafka_python-2.1.4 → kafka_python-2.2.0}/MANIFEST.in +0 -0
  82. {kafka_python-2.1.4 → kafka_python-2.2.0}/README.rst +0 -0
  83. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/__init__.py +0 -0
  84. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/__init__.py +0 -0
  85. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/acl_resource.py +0 -0
  86. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/config_resource.py +0 -0
  87. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/new_partitions.py +0 -0
  88. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/new_topic.py +0 -0
  89. {kafka_python-2.1.4/kafka/coordinator → kafka_python-2.2.0/kafka/benchmarks}/__init__.py +0 -0
  90. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/codec.py +0 -0
  91. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/consumer/__init__.py +0 -0
  92. {kafka_python-2.1.4/kafka/coordinator/assignors → kafka_python-2.2.0/kafka/coordinator}/__init__.py +0 -0
  93. {kafka_python-2.1.4/kafka/coordinator/assignors/sticky → kafka_python-2.2.0/kafka/coordinator/assignors}/__init__.py +0 -0
  94. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/abstract.py +0 -0
  95. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/range.py +0 -0
  96. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/roundrobin.py +0 -0
  97. {kafka_python-2.1.4/kafka/vendor → kafka_python-2.2.0/kafka/coordinator/assignors/sticky}/__init__.py +0 -0
  98. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/sticky/partition_movements.py +0 -0
  99. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/sticky/sorted_set.py +0 -0
  100. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/sticky/sticky_assignor.py +0 -0
  101. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/heartbeat.py +0 -0
  102. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/protocol.py +0 -0
  103. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/future.py +0 -0
  104. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/__init__.py +0 -0
  105. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/dict_reporter.py +0 -0
  106. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/measurable.py +0 -0
  107. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/measurable_stat.py +0 -0
  108. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/metrics.py +0 -0
  109. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/metrics_reporter.py +0 -0
  110. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stat.py +0 -0
  111. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/__init__.py +0 -0
  112. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/partitioner/__init__.py +0 -0
  113. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/partitioner/default.py +0 -0
  114. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/producer/__init__.py +0 -0
  115. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/__init__.py +0 -0
  116. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/abstract.py +0 -0
  117. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/admin.py +0 -0
  118. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/api.py +0 -0
  119. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/api_versions.py +0 -0
  120. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/broker_api_versions.py +0 -0
  121. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/commit.py +0 -0
  122. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/find_coordinator.py +0 -0
  123. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/frame.py +0 -0
  124. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/list_offsets.py +0 -0
  125. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/message.py +0 -0
  126. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/metadata.py +0 -0
  127. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/offset_for_leader_epoch.py +0 -0
  128. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/parser.py +0 -0
  129. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/pickle.py +0 -0
  130. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/produce.py +0 -0
  131. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/sasl_authenticate.py +0 -0
  132. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/sasl_handshake.py +0 -0
  133. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/struct.py +0 -0
  134. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/types.py +0 -0
  135. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/__init__.py +0 -0
  136. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/_crc32c.py +0 -0
  137. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/util.py +0 -0
  138. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/__init__.py +0 -0
  139. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/abc.py +0 -0
  140. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/gssapi.py +0 -0
  141. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/msk.py +0 -0
  142. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/oauth.py +0 -0
  143. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/plain.py +0 -0
  144. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/scram.py +0 -0
  145. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/sspi.py +0 -0
  146. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/serializer/__init__.py +0 -0
  147. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/serializer/abstract.py +0 -0
  148. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/socks5_wrapper.py +0 -0
  149. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/structs.py +0 -0
  150. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/util.py +0 -0
  151. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/vendor/enum34.py +0 -0
  152. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/vendor/selectors34.py +0 -0
  153. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/vendor/six.py +0 -0
  154. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/vendor/socketpair.py +0 -0
  155. {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/dependency_links.txt +0 -0
  156. {kafka_python-2.1.4 → kafka_python-2.2.0}/setup.cfg +0 -0
  157. {kafka_python-2.1.4 → kafka_python-2.2.0}/setup.py +0 -0
  158. {kafka_python-2.1.4/test → kafka_python-2.2.0/test/integration}/test_sasl_integration.py +0 -0
  159. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_acl_comparisons.py +0 -0
  160. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_admin.py +0 -0
  161. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_api_object_implementation.py +0 -0
  162. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_assignors.py +0 -0
  163. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_cluster.py +0 -0
  164. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_codec.py +0 -0
  165. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_consumer.py +0 -0
  166. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_object_conversion.py +0 -0
  167. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_package.py +0 -0
  168. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_partition_movements.py +0 -0
  169. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_partitioner.py +0 -0
  170. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_protocol.py +0 -0
  171. {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_util.py +0 -0
@@ -1,3 +1,61 @@
1
+ # 2.2.0 (Apr 28, 2025)
2
+
3
+ KafkaProducer
4
+ * KIP-98: Add idempotent producer support (#2569)
5
+ * KIP-98: Transactional Producer (#2587)
6
+ * KIP-98: Add offsets support to transactional KafkaProducer (#2590)
7
+ * Prefix producer logs w/ client id and transactional id (#2591)
8
+ * KAFKA-5429: Ignore produce response if batch was previously aborted
9
+ * KIP-91: KafkaProducer `delivery_timeout_ms`
10
+ * Default retries -> infinite
11
+ * Expand KafkaProducer docstring w/ idempotent and transactional notes
12
+ * RecordAccumulator: Use helper method to get/set `_tp_locks`; get dq with lock in reenqueue()
13
+
14
+ KafkaConsumer
15
+ * KIP-98: Add Consumer support for `READ_COMMITTED` (#2582)
16
+ * KIP-394: handle `MEMBER_ID_REQUIRED` error w/ second join group request (#2598)
17
+ * KAFKA-5078: Defer fetch record exception if iterator has already moved across a valid record
18
+ * KAFKA-5075: Defer consumer fetcher exception if fetch position has already increased
19
+ * KAFKA-4937: Batch offset fetches in the Consumer
20
+ * KAFKA-4547: Avoid resetting paused partitions to committed offsets
21
+ * KAFKA-6397: Consumer should not block setting positions of unavailable partitions (#2593)
22
+
23
+ Potentially Breaking Changes (internal)
24
+ * Rename CorruptRecordException -> CorruptRecordError
25
+ * Rename Coordinator errors to generic not group (#2585)
26
+ * Rename `ClusterMetadata.add_group_coordinator` -> `add_coordinator` + support txn type
27
+ * Use SaslAuthenticationFailedError in kafka.conn connection failure; Drop unused AuthenticationFailedError
28
+ * Remove old/unused errors; reorder; KafkaTimeout -> retriable
29
+ * Drop `log_start_offset` from producer RecordMetadata
30
+
31
+ Internal
32
+ * MemoryRecords iterator; MemoryRecordsBuilder records() helper
33
+ * Convert `DefaultRecordsBuilder.size_in_bytes` to classmethod
34
+
35
+ Fixes
36
+ * Resolve datetime deprecation warnings (#2589)
37
+ * Avoid self refcount in log messages; test thread close on all pythons
38
+ * Fix client.wakeup() race from producer/sender close
39
+ * Fix ElectionNotNeededError handling in admin client
40
+
41
+ Tests
42
+ * Move integration tests and fixtures to test/integration/; simplify unit fixtures (#2588)
43
+ * Expand Sender test coverage (#2586)
44
+ * py2 test fixups
45
+ * Drop unused KafkaClient import from `test_fetcher`
46
+
47
+ # 2.1.5 (Apr 4, 2025)
48
+
49
+ Fixes
50
+ * Fix python2.7 errors (#2578)
51
+
52
+ Improvements
53
+ * Move benchmark scripts to kafka.benchmarks module (#2584)
54
+ * Use __slots__ for metrics (#2583)
55
+ * Pass `metrics_enabled=False` to disable metrics (#2581)
56
+ * Drop unused kafka.producer.buffer / SimpleBufferPool (#2580)
57
+ * Raise UnsupportedVersionError from coordinator (#2579)
58
+
1
59
  # 2.1.4 (Mar 28, 2025)
2
60
 
3
61
  Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kafka-python
3
- Version: 2.1.4
3
+ Version: 2.2.0
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
@@ -39,6 +39,8 @@ Requires-Dist: pytest; extra == "testing"
39
39
  Requires-Dist: mock; python_version < "3.3" and extra == "testing"
40
40
  Requires-Dist: pytest-mock; extra == "testing"
41
41
  Requires-Dist: pytest-timeout; extra == "testing"
42
+ Provides-Extra: benchmarks
43
+ Requires-Dist: pyperf; extra == "benchmarks"
42
44
 
43
45
  Kafka Python client
44
46
  ------------------------
@@ -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
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env python
2
+ # Adapted from https://github.com/mrafayaleem/kafka-jython
3
+
4
+ from __future__ import absolute_import, print_function
5
+
6
+ import argparse
7
+ import pprint
8
+ import sys
9
+ import threading
10
+ import time
11
+ import traceback
12
+
13
+ from kafka import KafkaConsumer
14
+
15
+
16
+ class ConsumerPerformance(object):
17
+ @staticmethod
18
+ def run(args):
19
+ try:
20
+ props = {}
21
+ for prop in args.consumer_config:
22
+ k, v = prop.split('=')
23
+ try:
24
+ v = int(v)
25
+ except ValueError:
26
+ pass
27
+ if v == 'None':
28
+ v = None
29
+ elif v == 'False':
30
+ v = False
31
+ elif v == 'True':
32
+ v = True
33
+ props[k] = v
34
+
35
+ print('Initializing Consumer...')
36
+ props['bootstrap_servers'] = args.bootstrap_servers
37
+ props['auto_offset_reset'] = 'earliest'
38
+ if 'group_id' not in props:
39
+ props['group_id'] = 'kafka-consumer-benchmark'
40
+ if 'consumer_timeout_ms' not in props:
41
+ props['consumer_timeout_ms'] = 10000
42
+ props['metrics_sample_window_ms'] = args.stats_interval * 1000
43
+ for k, v in props.items():
44
+ print('---> {0}={1}'.format(k, v))
45
+ consumer = KafkaConsumer(args.topic, **props)
46
+ print('---> group_id={0}'.format(consumer.config['group_id']))
47
+ print('---> report stats every {0} secs'.format(args.stats_interval))
48
+ print('---> raw metrics? {0}'.format(args.raw_metrics))
49
+ timer_stop = threading.Event()
50
+ timer = StatsReporter(args.stats_interval, consumer,
51
+ event=timer_stop,
52
+ raw_metrics=args.raw_metrics)
53
+ timer.start()
54
+ print('-> OK!')
55
+ print()
56
+
57
+ start_time = time.time()
58
+ records = 0
59
+ for msg in consumer:
60
+ records += 1
61
+ if records >= args.num_records:
62
+ break
63
+
64
+ end_time = time.time()
65
+ timer_stop.set()
66
+ timer.join()
67
+ print('Consumed {0} records'.format(records))
68
+ print('Execution time:', end_time - start_time, 'secs')
69
+
70
+ except Exception:
71
+ exc_info = sys.exc_info()
72
+ traceback.print_exception(*exc_info)
73
+ sys.exit(1)
74
+
75
+
76
+ class StatsReporter(threading.Thread):
77
+ def __init__(self, interval, consumer, event=None, raw_metrics=False):
78
+ super(StatsReporter, self).__init__()
79
+ self.interval = interval
80
+ self.consumer = consumer
81
+ self.event = event
82
+ self.raw_metrics = raw_metrics
83
+
84
+ def print_stats(self):
85
+ metrics = self.consumer.metrics()
86
+ if self.raw_metrics:
87
+ pprint.pprint(metrics)
88
+ else:
89
+ print('{records-consumed-rate} records/sec ({bytes-consumed-rate} B/sec),'
90
+ ' {fetch-latency-avg} latency,'
91
+ ' {fetch-rate} fetch/s,'
92
+ ' {fetch-size-avg} fetch size,'
93
+ ' {records-lag-max} max record lag,'
94
+ ' {records-per-request-avg} records/req'
95
+ .format(**metrics['consumer-fetch-manager-metrics']))
96
+
97
+
98
+ def print_final(self):
99
+ self.print_stats()
100
+
101
+ def run(self):
102
+ while self.event and not self.event.wait(self.interval):
103
+ self.print_stats()
104
+ else:
105
+ self.print_final()
106
+
107
+
108
+ def get_args_parser():
109
+ parser = argparse.ArgumentParser(
110
+ description='This tool is used to verify the consumer performance.')
111
+
112
+ parser.add_argument(
113
+ '--bootstrap-servers', type=str, nargs='+', default=(),
114
+ help='host:port for cluster bootstrap servers')
115
+ parser.add_argument(
116
+ '--topic', type=str,
117
+ help='Topic for consumer test (default: kafka-python-benchmark-test)',
118
+ default='kafka-python-benchmark-test')
119
+ parser.add_argument(
120
+ '--num-records', type=int,
121
+ help='number of messages to consume (default: 1000000)',
122
+ default=1000000)
123
+ parser.add_argument(
124
+ '--consumer-config', type=str, nargs='+', default=(),
125
+ help='kafka consumer related configuration properties like '
126
+ 'bootstrap_servers,client_id etc..')
127
+ parser.add_argument(
128
+ '--fixture-compression', type=str,
129
+ help='specify a compression type for use with broker fixtures / producer')
130
+ parser.add_argument(
131
+ '--stats-interval', type=int,
132
+ help='Interval in seconds for stats reporting to console (default: 5)',
133
+ default=5)
134
+ parser.add_argument(
135
+ '--raw-metrics', action='store_true',
136
+ help='Enable this flag to print full metrics dict on each interval')
137
+ return parser
138
+
139
+
140
+ if __name__ == '__main__':
141
+ args = get_args_parser().parse_args()
142
+ ConsumerPerformance.run(args)
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import print_function
3
+
4
+ import argparse
5
+ import logging
6
+ import threading
7
+ import time
8
+
9
+ from kafka import KafkaConsumer, KafkaProducer
10
+
11
+
12
+ class Producer(threading.Thread):
13
+
14
+ def __init__(self, bootstrap_servers, topic, stop_event, msg_size):
15
+ super(Producer, self).__init__()
16
+ self.bootstrap_servers = bootstrap_servers
17
+ self.topic = topic
18
+ self.stop_event = stop_event
19
+ self.big_msg = b'1' * msg_size
20
+
21
+ def run(self):
22
+ producer = KafkaProducer(bootstrap_servers=self.bootstrap_servers)
23
+ self.sent = 0
24
+
25
+ while not self.stop_event.is_set():
26
+ producer.send(self.topic, self.big_msg)
27
+ self.sent += 1
28
+ producer.flush()
29
+ producer.close()
30
+
31
+
32
+ class Consumer(threading.Thread):
33
+ def __init__(self, bootstrap_servers, topic, stop_event, msg_size):
34
+ super(Consumer, self).__init__()
35
+ self.bootstrap_servers = bootstrap_servers
36
+ self.topic = topic
37
+ self.stop_event = stop_event
38
+ self.msg_size = msg_size
39
+
40
+ def run(self):
41
+ consumer = KafkaConsumer(bootstrap_servers=self.bootstrap_servers,
42
+ auto_offset_reset='earliest')
43
+ consumer.subscribe([self.topic])
44
+ self.valid = 0
45
+ self.invalid = 0
46
+
47
+ for message in consumer:
48
+ if len(message.value) == self.msg_size:
49
+ self.valid += 1
50
+ else:
51
+ print('Invalid message:', len(message.value), self.msg_size)
52
+ self.invalid += 1
53
+
54
+ if self.stop_event.is_set():
55
+ break
56
+ consumer.close()
57
+
58
+
59
+ def get_args_parser():
60
+ parser = argparse.ArgumentParser(
61
+ description='This tool is used to demonstrate consumer and producer load.')
62
+
63
+ parser.add_argument(
64
+ '--bootstrap-servers', type=str, nargs='+', default=('localhost:9092'),
65
+ help='host:port for cluster bootstrap servers (default: localhost:9092)')
66
+ parser.add_argument(
67
+ '--topic', type=str,
68
+ help='Topic for load test (default: kafka-python-benchmark-load-example)',
69
+ default='kafka-python-benchmark-load-example')
70
+ parser.add_argument(
71
+ '--msg-size', type=int,
72
+ help='Message size, in bytes, for load test (default: 524288)',
73
+ default=524288)
74
+ parser.add_argument(
75
+ '--load-time', type=int,
76
+ help='number of seconds to run load test (default: 10)',
77
+ default=10)
78
+ parser.add_argument(
79
+ '--log-level', type=str,
80
+ help='Optional logging level for load test: ERROR|INFO|DEBUG etc',
81
+ default=None)
82
+ return parser
83
+
84
+
85
+ def main(args):
86
+ if args.log_level:
87
+ logging.basicConfig(
88
+ format='%(asctime)s.%(msecs)s:%(name)s:%(thread)d:%(levelname)s:%(process)d:%(message)s',
89
+ level=getattr(logging, args.log_level))
90
+ producer_stop = threading.Event()
91
+ consumer_stop = threading.Event()
92
+ threads = [
93
+ Producer(args.bootstrap_servers, args.topic, producer_stop, args.msg_size),
94
+ Consumer(args.bootstrap_servers, args.topic, consumer_stop, args.msg_size)
95
+ ]
96
+
97
+ for t in threads:
98
+ t.start()
99
+
100
+ time.sleep(args.load_time)
101
+ producer_stop.set()
102
+ consumer_stop.set()
103
+ print('Messages sent: %d' % threads[0].sent)
104
+ print('Messages recvd: %d' % threads[1].valid)
105
+ print('Messages invalid: %d' % threads[1].invalid)
106
+
107
+
108
+ if __name__ == "__main__":
109
+ args = get_args_parser().parse_args()
110
+ main(args)
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env python
2
+ # Adapted from https://github.com/mrafayaleem/kafka-jython
3
+
4
+ from __future__ import absolute_import, print_function
5
+
6
+ import argparse
7
+ import pprint
8
+ import sys
9
+ import threading
10
+ import time
11
+ import traceback
12
+
13
+ from kafka.vendor.six.moves import range
14
+
15
+ from kafka import KafkaProducer
16
+
17
+
18
+ class ProducerPerformance(object):
19
+ @staticmethod
20
+ def run(args):
21
+ try:
22
+ props = {}
23
+ for prop in args.producer_config:
24
+ k, v = prop.split('=')
25
+ try:
26
+ v = int(v)
27
+ except ValueError:
28
+ pass
29
+ if v == 'None':
30
+ v = None
31
+ elif v == 'False':
32
+ v = False
33
+ elif v == 'True':
34
+ v = True
35
+ props[k] = v
36
+
37
+ print('Initializing producer...')
38
+ props['bootstrap_servers'] = args.bootstrap_servers
39
+ record = bytes(bytearray(args.record_size))
40
+ props['metrics_sample_window_ms'] = args.stats_interval * 1000
41
+
42
+ producer = KafkaProducer(**props)
43
+ for k, v in props.items():
44
+ print('---> {0}={1}'.format(k, v))
45
+ print('---> send {0} byte records'.format(args.record_size))
46
+ print('---> report stats every {0} secs'.format(args.stats_interval))
47
+ print('---> raw metrics? {0}'.format(args.raw_metrics))
48
+ timer_stop = threading.Event()
49
+ timer = StatsReporter(args.stats_interval, producer,
50
+ event=timer_stop,
51
+ raw_metrics=args.raw_metrics)
52
+ timer.start()
53
+ print('-> OK!')
54
+ print()
55
+
56
+ def _benchmark():
57
+ results = []
58
+ for i in range(args.num_records):
59
+ results.append(producer.send(topic=args.topic, value=record))
60
+ print("Send complete...")
61
+ producer.flush()
62
+ producer.close()
63
+ count_success, count_failure = 0, 0
64
+ for r in results:
65
+ if r.succeeded():
66
+ count_success += 1
67
+ elif r.failed():
68
+ count_failure += 1
69
+ else:
70
+ raise ValueError(r)
71
+ print("%d suceeded, %d failed" % (count_success, count_failure))
72
+
73
+ start_time = time.time()
74
+ _benchmark()
75
+ end_time = time.time()
76
+ timer_stop.set()
77
+ timer.join()
78
+ print('Execution time:', end_time - start_time, 'secs')
79
+
80
+ except Exception:
81
+ exc_info = sys.exc_info()
82
+ traceback.print_exception(*exc_info)
83
+ sys.exit(1)
84
+
85
+
86
+ class StatsReporter(threading.Thread):
87
+ def __init__(self, interval, producer, event=None, raw_metrics=False):
88
+ super(StatsReporter, self).__init__()
89
+ self.interval = interval
90
+ self.producer = producer
91
+ self.event = event
92
+ self.raw_metrics = raw_metrics
93
+
94
+ def print_stats(self):
95
+ metrics = self.producer.metrics()
96
+ if not metrics:
97
+ return
98
+ if self.raw_metrics:
99
+ pprint.pprint(metrics)
100
+ else:
101
+ print('{record-send-rate} records/sec ({byte-rate} B/sec),'
102
+ ' {request-latency-avg} latency,'
103
+ ' {record-size-avg} record size,'
104
+ ' {batch-size-avg} batch size,'
105
+ ' {records-per-request-avg} records/req'
106
+ .format(**metrics['producer-metrics']))
107
+
108
+ def print_final(self):
109
+ self.print_stats()
110
+
111
+ def run(self):
112
+ while self.event and not self.event.wait(self.interval):
113
+ self.print_stats()
114
+ else:
115
+ self.print_final()
116
+
117
+
118
+ def get_args_parser():
119
+ parser = argparse.ArgumentParser(
120
+ description='This tool is used to verify the producer performance.')
121
+
122
+ parser.add_argument(
123
+ '--bootstrap-servers', type=str, nargs='+', default=(),
124
+ help='host:port for cluster bootstrap server')
125
+ parser.add_argument(
126
+ '--topic', type=str,
127
+ help='Topic name for test (default: kafka-python-benchmark-test)',
128
+ default='kafka-python-benchmark-test')
129
+ parser.add_argument(
130
+ '--num-records', type=int,
131
+ help='number of messages to produce (default: 1000000)',
132
+ default=1000000)
133
+ parser.add_argument(
134
+ '--record-size', type=int,
135
+ help='message size in bytes (default: 100)',
136
+ default=100)
137
+ parser.add_argument(
138
+ '--producer-config', type=str, nargs='+', default=(),
139
+ help='kafka producer related configuaration properties like '
140
+ 'bootstrap_servers,client_id etc..')
141
+ parser.add_argument(
142
+ '--stats-interval', type=int,
143
+ help='Interval in seconds for stats reporting to console (default: 5)',
144
+ default=5)
145
+ parser.add_argument(
146
+ '--raw-metrics', action='store_true',
147
+ help='Enable this flag to print full metrics dict on each interval')
148
+ return parser
149
+
150
+
151
+ if __name__ == '__main__':
152
+ args = get_args_parser().parse_args()
153
+ ProducerPerformance.run(args)
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import print_function
3
+ import hashlib
4
+ import itertools
5
+ import os
6
+ import random
7
+
8
+ import pyperf
9
+
10
+ from kafka.record.memory_records import MemoryRecordsBuilder
11
+
12
+
13
+ DEFAULT_BATCH_SIZE = 1600 * 1024
14
+ KEY_SIZE = 6
15
+ VALUE_SIZE = 60
16
+ TIMESTAMP_RANGE = [1505824130000, 1505824140000]
17
+
18
+ # With values above v1 record is 100 bytes, so 10 000 bytes for 100 messages
19
+ MESSAGES_PER_BATCH = 100
20
+
21
+
22
+ def random_bytes(length):
23
+ buffer = bytearray(length)
24
+ for i in range(length):
25
+ buffer[i] = random.randint(0, 255)
26
+ return bytes(buffer)
27
+
28
+
29
+ def prepare():
30
+ return iter(itertools.cycle([
31
+ (random_bytes(KEY_SIZE),
32
+ random_bytes(VALUE_SIZE),
33
+ random.randint(*TIMESTAMP_RANGE)
34
+ )
35
+ for _ in range(int(MESSAGES_PER_BATCH * 1.94))
36
+ ]))
37
+
38
+
39
+ def finalize(results):
40
+ # Just some strange code to make sure PyPy does execute the main code
41
+ # properly, without optimizing it away
42
+ hash_val = hashlib.md5()
43
+ for buf in results:
44
+ hash_val.update(buf)
45
+ print(hash_val, file=open(os.devnull, "w"))
46
+
47
+
48
+ def func(loops, magic):
49
+ # Jit can optimize out the whole function if the result is the same each
50
+ # time, so we need some randomized input data )
51
+ precomputed_samples = prepare()
52
+ results = []
53
+
54
+ # Main benchmark code.
55
+ t0 = pyperf.perf_counter()
56
+ for _ in range(loops):
57
+ batch = MemoryRecordsBuilder(
58
+ magic, batch_size=DEFAULT_BATCH_SIZE, compression_type=0)
59
+ for _ in range(MESSAGES_PER_BATCH):
60
+ key, value, timestamp = next(precomputed_samples)
61
+ size = batch.append(
62
+ timestamp=timestamp, key=key, value=value)
63
+ assert size
64
+ batch.close()
65
+ results.append(batch.buffer())
66
+
67
+ res = pyperf.perf_counter() - t0
68
+
69
+ finalize(results)
70
+
71
+ return res
72
+
73
+
74
+ if __name__ == '__main__':
75
+ runner = pyperf.Runner()
76
+ runner.bench_time_func('batch_append_v0', func, 0)
77
+ runner.bench_time_func('batch_append_v1', func, 1)
78
+ runner.bench_time_func('batch_append_v2', func, 2)
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import print_function
3
+ import hashlib
4
+ import itertools
5
+ import os
6
+ import random
7
+
8
+ import pyperf
9
+
10
+ from kafka.record.memory_records import MemoryRecords, MemoryRecordsBuilder
11
+
12
+
13
+ DEFAULT_BATCH_SIZE = 1600 * 1024
14
+ KEY_SIZE = 6
15
+ VALUE_SIZE = 60
16
+ TIMESTAMP_RANGE = [1505824130000, 1505824140000]
17
+
18
+ BATCH_SAMPLES = 5
19
+ MESSAGES_PER_BATCH = 100
20
+
21
+
22
+ def random_bytes(length):
23
+ buffer = bytearray(length)
24
+ for i in range(length):
25
+ buffer[i] = random.randint(0, 255)
26
+ return bytes(buffer)
27
+
28
+
29
+ def prepare(magic):
30
+ samples = []
31
+ for _ in range(BATCH_SAMPLES):
32
+ batch = MemoryRecordsBuilder(
33
+ magic, batch_size=DEFAULT_BATCH_SIZE, compression_type=0)
34
+ for _ in range(MESSAGES_PER_BATCH):
35
+ size = batch.append(
36
+ random.randint(*TIMESTAMP_RANGE),
37
+ random_bytes(KEY_SIZE),
38
+ random_bytes(VALUE_SIZE),
39
+ headers=[])
40
+ assert size
41
+ batch.close()
42
+ samples.append(bytes(batch.buffer()))
43
+
44
+ return iter(itertools.cycle(samples))
45
+
46
+
47
+ def finalize(results):
48
+ # Just some strange code to make sure PyPy does execute the code above
49
+ # properly
50
+ hash_val = hashlib.md5()
51
+ for buf in results:
52
+ hash_val.update(buf)
53
+ print(hash_val, file=open(os.devnull, "w"))
54
+
55
+
56
+ def func(loops, magic):
57
+ # Jit can optimize out the whole function if the result is the same each
58
+ # time, so we need some randomized input data )
59
+ precomputed_samples = prepare(magic)
60
+ results = []
61
+
62
+ # Main benchmark code.
63
+ batch_data = next(precomputed_samples)
64
+ t0 = pyperf.perf_counter()
65
+ for _ in range(loops):
66
+ records = MemoryRecords(batch_data)
67
+ while records.has_next():
68
+ batch = records.next_batch()
69
+ batch.validate_crc()
70
+ for record in batch:
71
+ results.append(record.value)
72
+
73
+ res = pyperf.perf_counter() - t0
74
+ finalize(results)
75
+
76
+ return res
77
+
78
+
79
+ if __name__ == '__main__':
80
+ runner = pyperf.Runner()
81
+ runner.bench_time_func('batch_read_v0', func, 0)
82
+ runner.bench_time_func('batch_read_v1', func, 1)
83
+ runner.bench_time_func('batch_read_v2', func, 2)