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.
- {kafka_python-2.1.4 → kafka_python-2.2.0}/CHANGES.md +58 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/PKG-INFO +3 -1
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/client.py +6 -6
- kafka_python-2.2.0/kafka/benchmarks/consumer_performance.py +142 -0
- kafka_python-2.2.0/kafka/benchmarks/load_example.py +110 -0
- kafka_python-2.2.0/kafka/benchmarks/producer_performance.py +153 -0
- kafka_python-2.2.0/kafka/benchmarks/record_batch_compose.py +78 -0
- kafka_python-2.2.0/kafka/benchmarks/record_batch_read.py +83 -0
- kafka_python-2.2.0/kafka/benchmarks/varint_speed.py +434 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/client_async.py +40 -2
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/cluster.py +18 -13
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/conn.py +7 -5
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/consumer/fetcher.py +309 -194
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/consumer/group.py +73 -63
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/consumer/subscription_state.py +84 -36
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/base.py +60 -26
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/consumer.py +40 -40
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/errors.py +68 -93
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/compound_stat.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/kafka_metric.py +3 -1
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/metric_config.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/metric_name.py +1 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/quota.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/avg.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/count.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/histogram.py +6 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/max_stat.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/min_stat.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/percentile.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/percentiles.py +3 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/rate.py +3 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/sampled_stat.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/sensor.py +4 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/total.py +2 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/producer/future.py +3 -3
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/producer/kafka.py +291 -58
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/producer/record_accumulator.py +293 -214
- kafka_python-2.2.0/kafka/producer/sender.py +762 -0
- kafka_python-2.2.0/kafka/producer/transaction_manager.py +981 -0
- kafka_python-2.2.0/kafka/protocol/add_offsets_to_txn.py +59 -0
- kafka_python-2.2.0/kafka/protocol/add_partitions_to_txn.py +63 -0
- kafka_python-2.2.0/kafka/protocol/end_txn.py +58 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/fetch.py +6 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/group.py +17 -3
- kafka_python-2.2.0/kafka/protocol/init_producer_id.py +46 -0
- kafka_python-2.2.0/kafka/protocol/txn_offset_commit.py +78 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/abc.py +10 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/default_records.py +101 -12
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/legacy_records.py +12 -3
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/memory_records.py +54 -6
- kafka_python-2.2.0/kafka/vendor/__init__.py +0 -0
- kafka_python-2.2.0/kafka/version.py +1 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/PKG-INFO +3 -1
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/SOURCES.txt +23 -6
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/requires.txt +3 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/top_level.txt +1 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/pyproject.toml +1 -0
- kafka_python-2.2.0/test/integration/__init__.py +0 -0
- kafka_python-2.2.0/test/integration/conftest.py +168 -0
- kafka_python-2.2.0/test/integration/fixtures.py +765 -0
- {kafka_python-2.1.4/test → kafka_python-2.2.0/test/integration}/test_admin_integration.py +2 -2
- {kafka_python-2.1.4/test → kafka_python-2.2.0/test/integration}/test_consumer_group.py +2 -1
- {kafka_python-2.1.4/test → kafka_python-2.2.0/test/integration}/test_consumer_integration.py +3 -2
- kafka_python-2.1.4/test/test_producer.py → kafka_python-2.2.0/test/integration/test_producer_integration.py +81 -32
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_client_async.py +135 -136
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_conn.py +4 -1
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_coordinator.py +35 -27
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_fetcher.py +213 -70
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_metrics.py +2 -18
- kafka_python-2.2.0/test/test_producer.py +35 -0
- kafka_python-2.2.0/test/test_record_accumulator.py +266 -0
- kafka_python-2.2.0/test/test_sender.py +242 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_subscription_state.py +2 -2
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/testutil.py +16 -0
- kafka_python-2.1.4/kafka/producer/buffer.py +0 -115
- kafka_python-2.1.4/kafka/producer/sender.py +0 -482
- kafka_python-2.1.4/kafka/version.py +0 -1
- kafka_python-2.1.4/test/test_sender.py +0 -51
- {kafka_python-2.1.4 → kafka_python-2.2.0}/AUTHORS.md +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/LICENSE +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/MANIFEST.in +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/README.rst +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/acl_resource.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/config_resource.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/new_partitions.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/admin/new_topic.py +0 -0
- {kafka_python-2.1.4/kafka/coordinator → kafka_python-2.2.0/kafka/benchmarks}/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/codec.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/consumer/__init__.py +0 -0
- {kafka_python-2.1.4/kafka/coordinator/assignors → kafka_python-2.2.0/kafka/coordinator}/__init__.py +0 -0
- {kafka_python-2.1.4/kafka/coordinator/assignors/sticky → kafka_python-2.2.0/kafka/coordinator/assignors}/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/abstract.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/range.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/roundrobin.py +0 -0
- {kafka_python-2.1.4/kafka/vendor → kafka_python-2.2.0/kafka/coordinator/assignors/sticky}/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/sticky/partition_movements.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/sticky/sorted_set.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/assignors/sticky/sticky_assignor.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/heartbeat.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/coordinator/protocol.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/future.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/dict_reporter.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/measurable.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/measurable_stat.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/metrics.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/metrics_reporter.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stat.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/metrics/stats/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/partitioner/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/partitioner/default.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/producer/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/abstract.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/admin.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/api.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/api_versions.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/broker_api_versions.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/commit.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/find_coordinator.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/frame.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/list_offsets.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/message.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/metadata.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/offset_for_leader_epoch.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/parser.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/pickle.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/produce.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/sasl_authenticate.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/sasl_handshake.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/struct.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/protocol/types.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/_crc32c.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/record/util.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/abc.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/gssapi.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/msk.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/oauth.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/plain.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/scram.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/sasl/sspi.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/serializer/__init__.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/serializer/abstract.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/socks5_wrapper.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/structs.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/util.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/vendor/enum34.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/vendor/selectors34.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/vendor/six.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka/vendor/socketpair.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/kafka_python.egg-info/dependency_links.txt +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/setup.cfg +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/setup.py +0 -0
- {kafka_python-2.1.4/test → kafka_python-2.2.0/test/integration}/test_sasl_integration.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_acl_comparisons.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_admin.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_api_object_implementation.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_assignors.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_cluster.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_codec.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_consumer.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_object_conversion.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_package.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_partition_movements.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_partitioner.py +0 -0
- {kafka_python-2.1.4 → kafka_python-2.2.0}/test/test_protocol.py +0 -0
- {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.
|
|
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,
|
|
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
|
|
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
|
-
|
|
1463
|
+
CoordinatorNotAvailableError: The coordinator is not
|
|
1464
1464
|
available, so cannot process requests.
|
|
1465
|
-
|
|
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)
|