kafka-python 2.1.3__tar.gz → 2.1.5__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 (158) hide show
  1. {kafka_python-2.1.3 → kafka_python-2.1.5}/CHANGES.md +25 -0
  2. {kafka_python-2.1.3 → kafka_python-2.1.5}/PKG-INFO +3 -1
  3. kafka_python-2.1.5/kafka/benchmarks/consumer_performance.py +142 -0
  4. kafka_python-2.1.5/kafka/benchmarks/load_example.py +110 -0
  5. kafka_python-2.1.5/kafka/benchmarks/producer_performance.py +153 -0
  6. kafka_python-2.1.5/kafka/benchmarks/record_batch_compose.py +78 -0
  7. kafka_python-2.1.5/kafka/benchmarks/record_batch_read.py +83 -0
  8. kafka_python-2.1.5/kafka/benchmarks/varint_speed.py +434 -0
  9. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/client_async.py +4 -2
  10. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/conn.py +37 -28
  11. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/consumer/fetcher.py +42 -15
  12. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/consumer/group.py +19 -11
  13. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/consumer/subscription_state.py +7 -3
  14. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/base.py +23 -7
  15. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/consumer.py +18 -9
  16. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/compound_stat.py +2 -0
  17. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/kafka_metric.py +3 -1
  18. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/metric_config.py +2 -0
  19. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/metric_name.py +1 -0
  20. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/quota.py +2 -0
  21. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/avg.py +2 -0
  22. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/count.py +2 -0
  23. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/histogram.py +6 -0
  24. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/max_stat.py +2 -0
  25. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/min_stat.py +2 -0
  26. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/percentile.py +2 -0
  27. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/percentiles.py +3 -0
  28. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/rate.py +3 -0
  29. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/sampled_stat.py +2 -0
  30. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/sensor.py +4 -0
  31. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/total.py +2 -0
  32. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/producer/kafka.py +27 -29
  33. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/producer/record_accumulator.py +4 -31
  34. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/producer/sender.py +14 -7
  35. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/metadata.py +1 -0
  36. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/sasl/oauth.py +15 -2
  37. kafka_python-2.1.5/kafka/vendor/__init__.py +0 -0
  38. kafka_python-2.1.5/kafka/version.py +1 -0
  39. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka_python.egg-info/PKG-INFO +3 -1
  40. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka_python.egg-info/SOURCES.txt +7 -1
  41. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka_python.egg-info/requires.txt +3 -0
  42. {kafka_python-2.1.3 → kafka_python-2.1.5}/pyproject.toml +1 -0
  43. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_client_async.py +3 -2
  44. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_conn.py +4 -1
  45. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_consumer_group.py +2 -1
  46. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_coordinator.py +21 -11
  47. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_fetcher.py +4 -3
  48. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_producer.py +3 -14
  49. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_sender.py +2 -14
  50. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_subscription_state.py +2 -2
  51. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/testutil.py +16 -0
  52. kafka_python-2.1.3/kafka/producer/buffer.py +0 -115
  53. kafka_python-2.1.3/kafka/version.py +0 -1
  54. {kafka_python-2.1.3 → kafka_python-2.1.5}/AUTHORS.md +0 -0
  55. {kafka_python-2.1.3 → kafka_python-2.1.5}/LICENSE +0 -0
  56. {kafka_python-2.1.3 → kafka_python-2.1.5}/MANIFEST.in +0 -0
  57. {kafka_python-2.1.3 → kafka_python-2.1.5}/README.rst +0 -0
  58. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/__init__.py +0 -0
  59. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/admin/__init__.py +0 -0
  60. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/admin/acl_resource.py +0 -0
  61. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/admin/client.py +0 -0
  62. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/admin/config_resource.py +0 -0
  63. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/admin/new_partitions.py +0 -0
  64. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/admin/new_topic.py +0 -0
  65. {kafka_python-2.1.3/kafka/coordinator → kafka_python-2.1.5/kafka/benchmarks}/__init__.py +0 -0
  66. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/cluster.py +0 -0
  67. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/codec.py +0 -0
  68. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/consumer/__init__.py +0 -0
  69. {kafka_python-2.1.3/kafka/coordinator/assignors → kafka_python-2.1.5/kafka/coordinator}/__init__.py +0 -0
  70. {kafka_python-2.1.3/kafka/coordinator/assignors/sticky → kafka_python-2.1.5/kafka/coordinator/assignors}/__init__.py +0 -0
  71. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/assignors/abstract.py +0 -0
  72. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/assignors/range.py +0 -0
  73. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/assignors/roundrobin.py +0 -0
  74. {kafka_python-2.1.3/kafka/vendor → kafka_python-2.1.5/kafka/coordinator/assignors/sticky}/__init__.py +0 -0
  75. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/assignors/sticky/partition_movements.py +0 -0
  76. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/assignors/sticky/sorted_set.py +0 -0
  77. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/assignors/sticky/sticky_assignor.py +0 -0
  78. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/heartbeat.py +0 -0
  79. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/coordinator/protocol.py +0 -0
  80. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/errors.py +0 -0
  81. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/future.py +0 -0
  82. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/__init__.py +0 -0
  83. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/dict_reporter.py +0 -0
  84. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/measurable.py +0 -0
  85. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/measurable_stat.py +0 -0
  86. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/metrics.py +0 -0
  87. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/metrics_reporter.py +0 -0
  88. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stat.py +0 -0
  89. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/metrics/stats/__init__.py +0 -0
  90. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/partitioner/__init__.py +0 -0
  91. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/partitioner/default.py +0 -0
  92. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/producer/__init__.py +0 -0
  93. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/producer/future.py +0 -0
  94. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/__init__.py +0 -0
  95. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/abstract.py +0 -0
  96. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/admin.py +0 -0
  97. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/api.py +0 -0
  98. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/api_versions.py +0 -0
  99. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/broker_api_versions.py +0 -0
  100. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/commit.py +0 -0
  101. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/fetch.py +0 -0
  102. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/find_coordinator.py +0 -0
  103. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/frame.py +0 -0
  104. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/group.py +0 -0
  105. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/list_offsets.py +0 -0
  106. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/message.py +0 -0
  107. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/offset_for_leader_epoch.py +0 -0
  108. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/parser.py +0 -0
  109. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/pickle.py +0 -0
  110. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/produce.py +0 -0
  111. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/sasl_authenticate.py +0 -0
  112. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/sasl_handshake.py +0 -0
  113. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/struct.py +0 -0
  114. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/protocol/types.py +0 -0
  115. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/record/__init__.py +0 -0
  116. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/record/_crc32c.py +0 -0
  117. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/record/abc.py +0 -0
  118. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/record/default_records.py +0 -0
  119. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/record/legacy_records.py +0 -0
  120. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/record/memory_records.py +0 -0
  121. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/record/util.py +0 -0
  122. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/sasl/__init__.py +0 -0
  123. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/sasl/abc.py +0 -0
  124. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/sasl/gssapi.py +0 -0
  125. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/sasl/msk.py +0 -0
  126. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/sasl/plain.py +0 -0
  127. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/sasl/scram.py +0 -0
  128. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/sasl/sspi.py +0 -0
  129. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/serializer/__init__.py +0 -0
  130. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/serializer/abstract.py +0 -0
  131. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/socks5_wrapper.py +0 -0
  132. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/structs.py +0 -0
  133. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/util.py +0 -0
  134. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/vendor/enum34.py +0 -0
  135. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/vendor/selectors34.py +0 -0
  136. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/vendor/six.py +0 -0
  137. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka/vendor/socketpair.py +0 -0
  138. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka_python.egg-info/dependency_links.txt +0 -0
  139. {kafka_python-2.1.3 → kafka_python-2.1.5}/kafka_python.egg-info/top_level.txt +0 -0
  140. {kafka_python-2.1.3 → kafka_python-2.1.5}/setup.cfg +0 -0
  141. {kafka_python-2.1.3 → kafka_python-2.1.5}/setup.py +0 -0
  142. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_acl_comparisons.py +0 -0
  143. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_admin.py +0 -0
  144. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_admin_integration.py +0 -0
  145. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_api_object_implementation.py +0 -0
  146. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_assignors.py +0 -0
  147. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_cluster.py +0 -0
  148. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_codec.py +0 -0
  149. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_consumer.py +0 -0
  150. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_consumer_integration.py +0 -0
  151. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_metrics.py +0 -0
  152. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_object_conversion.py +0 -0
  153. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_package.py +0 -0
  154. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_partition_movements.py +0 -0
  155. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_partitioner.py +0 -0
  156. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_protocol.py +0 -0
  157. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_sasl_integration.py +0 -0
  158. {kafka_python-2.1.3 → kafka_python-2.1.5}/test/test_util.py +0 -0
@@ -1,3 +1,28 @@
1
+ # 2.1.5 (Apr 4, 2025)
2
+
3
+ Fixes
4
+ * Fix python2.7 errors (#2578)
5
+
6
+ Improvements
7
+ * Move benchmark scripts to kafka.benchmarks module (#2584)
8
+ * Use __slots__ for metrics (#2583)
9
+ * Pass `metrics_enabled=False` to disable metrics (#2581)
10
+ * Drop unused kafka.producer.buffer / SimpleBufferPool (#2580)
11
+ * Raise UnsupportedVersionError from coordinator (#2579)
12
+
13
+ # 2.1.4 (Mar 28, 2025)
14
+
15
+ Fixes
16
+ * Dont block pending FetchRequests when Metadata update requested (#2576)
17
+ * Fix MetadataRequest for no topics (#2573)
18
+ * Send final error byte x01 on Sasl OAuth failure (#2572)
19
+ * Reset SASL state on disconnect (#2571)
20
+ * Try import new Sequence before old to avoid DeprecationWarning
21
+
22
+ Improvements
23
+ * Update Makefile default to 4.0 broker; add make fixture
24
+ * Improve connection state logging (#2574)
25
+
1
26
  # 2.1.3 (Mar 25, 2025)
2
27
 
3
28
  Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kafka-python
3
- Version: 2.1.3
3
+ Version: 2.1.5
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
  ------------------------
@@ -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)