kafka-python 2.2.15__tar.gz → 2.2.17__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.2.15 → kafka_python-2.2.17}/CHANGES.md +18 -1
- {kafka_python-2.2.15 → kafka_python-2.2.17}/PKG-INFO +1 -1
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/conn.py +14 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/consumer/group.py +8 -7
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/base.py +1 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/producer/kafka.py +5 -1
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/socks5_wrapper.py +31 -5
- kafka_python-2.2.17/kafka/version.py +1 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka_python.egg-info/PKG-INFO +1 -1
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/integration/test_consumer_integration.py +22 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_conn.py +55 -2
- kafka_python-2.2.15/kafka/version.py +0 -1
- {kafka_python-2.2.15 → kafka_python-2.2.17}/AUTHORS.md +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/LICENSE +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/MANIFEST.in +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/README.rst +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/admin/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/admin/acl_resource.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/admin/client.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/admin/config_resource.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/admin/new_partitions.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/admin/new_topic.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/benchmarks/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/benchmarks/consumer_performance.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/benchmarks/load_example.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/benchmarks/producer_performance.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/benchmarks/record_batch_compose.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/benchmarks/record_batch_read.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/benchmarks/varint_speed.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/client_async.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/cluster.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/codec.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/consumer/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/consumer/fetcher.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/consumer/subscription_state.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/abstract.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/range.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/roundrobin.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/sticky/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/sticky/partition_movements.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/sticky/sorted_set.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/sticky/sticky_assignor.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/consumer.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/heartbeat.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/protocol.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/errors.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/future.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/compound_stat.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/dict_reporter.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/kafka_metric.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/measurable.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/measurable_stat.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/metric_config.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/metric_name.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/metrics.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/metrics_reporter.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/quota.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stat.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/avg.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/count.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/histogram.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/max_stat.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/min_stat.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/percentile.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/percentiles.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/rate.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/sampled_stat.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/sensor.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/metrics/stats/total.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/partitioner/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/partitioner/default.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/producer/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/producer/future.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/producer/record_accumulator.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/producer/sender.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/producer/transaction_manager.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/abstract.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/add_offsets_to_txn.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/add_partitions_to_txn.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/admin.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/api.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/api_versions.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/broker_api_versions.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/commit.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/end_txn.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/fetch.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/find_coordinator.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/frame.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/group.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/init_producer_id.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/list_offsets.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/message.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/metadata.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/offset_for_leader_epoch.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/parser.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/pickle.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/produce.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/sasl_authenticate.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/sasl_handshake.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/struct.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/txn_offset_commit.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/protocol/types.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/record/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/record/_crc32c.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/record/abc.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/record/default_records.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/record/legacy_records.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/record/memory_records.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/record/util.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/sasl/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/sasl/abc.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/sasl/gssapi.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/sasl/msk.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/sasl/oauth.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/sasl/plain.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/sasl/scram.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/sasl/sspi.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/serializer/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/serializer/abstract.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/structs.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/util.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/vendor/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/vendor/enum34.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/vendor/selectors34.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/vendor/six.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/vendor/socketpair.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka_python.egg-info/SOURCES.txt +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka_python.egg-info/dependency_links.txt +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka_python.egg-info/requires.txt +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/kafka_python.egg-info/top_level.txt +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/pyproject.toml +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/setup.cfg +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/setup.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/integration/__init__.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/integration/conftest.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/integration/fixtures.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/integration/test_admin_integration.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/integration/test_consumer_group.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/integration/test_producer_integration.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/integration/test_sasl_integration.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_acl_comparisons.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_admin.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_api_object_implementation.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_assignors.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_client_async.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_cluster.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_codec.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_consumer.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_coordinator.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_fetcher.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_metrics.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_object_conversion.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_package.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_partition_movements.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_partitioner.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_producer.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_protocol.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_record_accumulator.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_sender.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_subscription_state.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/test_util.py +0 -0
- {kafka_python-2.2.15 → kafka_python-2.2.17}/test/testutil.py +0 -0
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
# 2.2.17 (Nov 20, 2025)
|
|
2
|
+
|
|
3
|
+
Fixes
|
|
4
|
+
* Add internal poll to consumer.position() (#2696)
|
|
5
|
+
* Initiate Coordinator Reconnect w/ Backoff from Heartbeat Thread (#2695)
|
|
6
|
+
|
|
7
|
+
Networking
|
|
8
|
+
* SOCKS5: support looking up names remotely (jschwartzenberg / #2666)
|
|
9
|
+
|
|
10
|
+
Documentation
|
|
11
|
+
* Add `transactional_id` to KafkaProducer Keyword Arguments docstring
|
|
12
|
+
|
|
13
|
+
# 2.2.16 (Nov 18, 2025)
|
|
14
|
+
|
|
15
|
+
Fixes
|
|
16
|
+
* Fix thread not waking up when there is still data to be sent (gqmelo / #2670)
|
|
17
|
+
* Ensure timeout is checked after each fetch position update in `Consumer.position()` (k61n / #2668)
|
|
18
|
+
|
|
1
19
|
# 2.2.15 (July 1, 2025)
|
|
2
20
|
|
|
3
21
|
Fixes
|
|
@@ -26,7 +44,6 @@ Fixes
|
|
|
26
44
|
* Avoid RuntimeError on mutated `_completed_fetches` deque in consumer fetcher (#2646)
|
|
27
45
|
* Throw exception on invalid bucket type (#2642)
|
|
28
46
|
|
|
29
|
-
|
|
30
47
|
# 2.2.11 (June 5, 2025)
|
|
31
48
|
|
|
32
49
|
Fixes
|
|
@@ -326,6 +326,9 @@ class BrokerConnection(object):
|
|
|
326
326
|
return True
|
|
327
327
|
|
|
328
328
|
def _next_afi_sockaddr(self):
|
|
329
|
+
if self.config["socks5_proxy"] and Socks5Wrapper.use_remote_lookup(self.config["socks5_proxy"]):
|
|
330
|
+
return (socket.AF_UNSPEC, (self.host, self.port))
|
|
331
|
+
|
|
329
332
|
if not self._gai:
|
|
330
333
|
if not self._dns_lookup():
|
|
331
334
|
return
|
|
@@ -379,6 +382,7 @@ class BrokerConnection(object):
|
|
|
379
382
|
self._sock_afi, self._sock_addr = next_lookup
|
|
380
383
|
try:
|
|
381
384
|
if self.config["socks5_proxy"] is not None:
|
|
385
|
+
log.debug('%s: initializing Socks5 proxy at %s', self, self.config["socks5_proxy"])
|
|
382
386
|
self._socks5_proxy = Socks5Wrapper(self.config["socks5_proxy"], self.afi)
|
|
383
387
|
self._sock = self._socks5_proxy.socket(self._sock_afi, socket.SOCK_STREAM)
|
|
384
388
|
else:
|
|
@@ -864,6 +868,8 @@ class BrokerConnection(object):
|
|
|
864
868
|
if self.disconnected() or self.connecting():
|
|
865
869
|
if len(self._gai) > 0:
|
|
866
870
|
return 0
|
|
871
|
+
elif self.config["socks5_proxy"] and Socks5Wrapper.use_remote_lookup(self.config["socks5_proxy"]):
|
|
872
|
+
return 0
|
|
867
873
|
else:
|
|
868
874
|
time_waited = time.time() - self.last_attempt
|
|
869
875
|
return max(self._reconnect_backoff - time_waited, 0) * 1000
|
|
@@ -964,6 +970,7 @@ class BrokerConnection(object):
|
|
|
964
970
|
# the socket fd from selectors cleanly.
|
|
965
971
|
sock = self._sock
|
|
966
972
|
self._sock = None
|
|
973
|
+
self._socks5_proxy = None
|
|
967
974
|
|
|
968
975
|
# drop lock before state change callback and processing futures
|
|
969
976
|
self.config['state_change_callback'](self.node_id, sock, self)
|
|
@@ -1075,6 +1082,13 @@ class BrokerConnection(object):
|
|
|
1075
1082
|
total_bytes = self._send_bytes(self._send_buffer)
|
|
1076
1083
|
self._send_buffer = self._send_buffer[total_bytes:]
|
|
1077
1084
|
|
|
1085
|
+
# If all data was sent, we need to get the new data from the protocol now, otherwise
|
|
1086
|
+
# this function would return True, indicating that there are no more pending
|
|
1087
|
+
# requests. This could cause the calling thread to wait indefinitely as it won't
|
|
1088
|
+
# know that there is still buffered data to send.
|
|
1089
|
+
if not self._send_buffer:
|
|
1090
|
+
self._send_buffer = self._protocol.send_bytes()
|
|
1091
|
+
|
|
1078
1092
|
if self._sensors:
|
|
1079
1093
|
self._sensors.bytes_sent.record(total_bytes)
|
|
1080
1094
|
# Return True iff send buffer is empty
|
|
@@ -723,7 +723,9 @@ class KafkaConsumer(six.Iterator):
|
|
|
723
723
|
|
|
724
724
|
# We do not want to be stuck blocking in poll if we are missing some positions
|
|
725
725
|
# since the offset lookup may be backing off after a failure
|
|
726
|
-
poll_timeout_ms =
|
|
726
|
+
poll_timeout_ms = timer.timeout_ms
|
|
727
|
+
if self.config['group_id'] is not None:
|
|
728
|
+
poll_timeout_ms = min(poll_timeout_ms, self._coordinator.time_to_next_poll() * 1000)
|
|
727
729
|
if not has_all_fetch_positions:
|
|
728
730
|
log.debug('poll: do not have all fetch positions...')
|
|
729
731
|
poll_timeout_ms = min(poll_timeout_ms, self.config['retry_backoff_ms'])
|
|
@@ -753,13 +755,12 @@ class KafkaConsumer(six.Iterator):
|
|
|
753
755
|
|
|
754
756
|
timer = Timer(timeout_ms)
|
|
755
757
|
position = self._subscription.assignment[partition].position
|
|
756
|
-
while position is None:
|
|
758
|
+
while position is None and not timer.expired:
|
|
757
759
|
# batch update fetch positions for any partitions without a valid position
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
else:
|
|
760
|
+
self._update_fetch_positions(timeout_ms=timer.timeout_ms)
|
|
761
|
+
self._client.poll(timeout_ms=timer.timeout_ms)
|
|
762
|
+
position = self._subscription.assignment[partition].position
|
|
763
|
+
if position is not None:
|
|
763
764
|
return position.offset
|
|
764
765
|
|
|
765
766
|
def highwater(self, partition):
|
|
@@ -1120,6 +1120,7 @@ class HeartbeatThread(threading.Thread):
|
|
|
1120
1120
|
self.coordinator._lock.wait(self.coordinator.config['retry_backoff_ms'] / 1000)
|
|
1121
1121
|
|
|
1122
1122
|
elif not self.coordinator.connected():
|
|
1123
|
+
self.coordinator._client.maybe_connect(self.coordinator.coordinator_id)
|
|
1123
1124
|
self.coordinator._client._lock.release()
|
|
1124
1125
|
self.coordinator._lock.wait(self.coordinator.config['retry_backoff_ms'] / 1000)
|
|
1125
1126
|
|
|
@@ -134,10 +134,14 @@ class KafkaProducer(object):
|
|
|
134
134
|
value_serializer (callable): used to convert user-supplied message
|
|
135
135
|
values to bytes. If not None, called as f(value), should return
|
|
136
136
|
bytes. Default: None.
|
|
137
|
+
transactional_id (str): Enable transactional producer with a unique
|
|
138
|
+
identifier. This will be used to identify the same producer
|
|
139
|
+
instance across process restarts. Default: None.
|
|
137
140
|
enable_idempotence (bool): When set to True, the producer will ensure
|
|
138
141
|
that exactly one copy of each message is written in the stream.
|
|
139
142
|
If False, producer retries due to broker failures, etc., may write
|
|
140
|
-
duplicates of the retried message in the stream.
|
|
143
|
+
duplicates of the retried message in the stream.
|
|
144
|
+
Default: True if `transactional_id` is provided, otherwise False.
|
|
141
145
|
|
|
142
146
|
Note that enabling idempotence requires
|
|
143
147
|
`max_in_flight_requests_per_connection` to be set to 1 and `retries`
|
|
@@ -64,6 +64,15 @@ class Socks5Wrapper:
|
|
|
64
64
|
log.warning("DNS lookup failed for proxy %s:%d, %r", host, port, ex)
|
|
65
65
|
return []
|
|
66
66
|
|
|
67
|
+
@classmethod
|
|
68
|
+
def use_remote_lookup(cls, proxy_url):
|
|
69
|
+
if proxy_url is None:
|
|
70
|
+
return False
|
|
71
|
+
return urlparse(proxy_url).scheme == 'socks5h'
|
|
72
|
+
|
|
73
|
+
def _use_remote_lookup(self):
|
|
74
|
+
return self._proxy_url.scheme == 'socks5h'
|
|
75
|
+
|
|
67
76
|
def socket(self, family, sock_type):
|
|
68
77
|
"""Open and record a socket.
|
|
69
78
|
|
|
@@ -187,7 +196,10 @@ class Socks5Wrapper:
|
|
|
187
196
|
return errno.ECONNREFUSED
|
|
188
197
|
|
|
189
198
|
if self._state == ProxyConnectionStates.REQUEST_SUBMIT:
|
|
190
|
-
if self.
|
|
199
|
+
if self._use_remote_lookup():
|
|
200
|
+
addr_type = 3
|
|
201
|
+
addr_len = len(addr[0])
|
|
202
|
+
elif self._target_afi == socket.AF_INET:
|
|
191
203
|
addr_type = 1
|
|
192
204
|
addr_len = 4
|
|
193
205
|
elif self._target_afi == socket.AF_INET6:
|
|
@@ -200,14 +212,28 @@ class Socks5Wrapper:
|
|
|
200
212
|
return errno.ECONNREFUSED
|
|
201
213
|
|
|
202
214
|
self._buffer_out = struct.pack(
|
|
203
|
-
"!bbbb
|
|
215
|
+
"!bbbb",
|
|
204
216
|
5, # version
|
|
205
217
|
1, # command: connect
|
|
206
218
|
0, # reserved
|
|
207
|
-
addr_type, # 1 for ipv4, 4 for ipv6 address
|
|
208
|
-
socket.inet_pton(self._target_afi, addr[0]), # either 4 or 16 bytes of actual address
|
|
209
|
-
addr[1], # port
|
|
219
|
+
addr_type, # 1 for ipv4, 4 for ipv6 address, 3 for domain name
|
|
210
220
|
)
|
|
221
|
+
# Addr format depends on type
|
|
222
|
+
if addr_type == 3:
|
|
223
|
+
# len + domain name (no null terminator)
|
|
224
|
+
self._buffer_out += struct.pack(
|
|
225
|
+
"!b{}s".format(addr_len),
|
|
226
|
+
addr_len,
|
|
227
|
+
addr[0].encode('ascii'),
|
|
228
|
+
)
|
|
229
|
+
else:
|
|
230
|
+
# either 4 (type 1) or 16 (type 4) bytes of actual address
|
|
231
|
+
self._buffer_out += struct.pack(
|
|
232
|
+
"!{}s".format(addr_len),
|
|
233
|
+
socket.inet_pton(self._target_afi, addr[0]),
|
|
234
|
+
)
|
|
235
|
+
self._buffer_out += struct.pack("!H", addr[1]) # port
|
|
236
|
+
|
|
211
237
|
self._state = ProxyConnectionStates.REQUESTING
|
|
212
238
|
|
|
213
239
|
if self._state == ProxyConnectionStates.REQUESTING:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.2.17'
|
|
@@ -302,3 +302,25 @@ def test_kafka_consumer_offsets_for_times_errors(kafka_consumer_factory, topic):
|
|
|
302
302
|
|
|
303
303
|
with pytest.raises(KafkaTimeoutError):
|
|
304
304
|
consumer.offsets_for_times({bad_tp: 0})
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
@pytest.mark.skipif(not env_kafka_version(), reason="No KAFKA_VERSION set")
|
|
308
|
+
def test_kafka_consumer_position_after_seek_to_end(kafka_consumer_factory, topic, send_messages):
|
|
309
|
+
send_messages(range(0, 10), partition=0)
|
|
310
|
+
|
|
311
|
+
# Start a consumer with manual partition assignment.
|
|
312
|
+
consumer = kafka_consumer_factory(
|
|
313
|
+
topics=(),
|
|
314
|
+
group_id=None,
|
|
315
|
+
enable_auto_commit=False,
|
|
316
|
+
)
|
|
317
|
+
tp = TopicPartition(topic, 0)
|
|
318
|
+
consumer.assign([tp])
|
|
319
|
+
|
|
320
|
+
# Seek to the end of the partition, and call position() to synchronize the
|
|
321
|
+
# partition's offset without calling poll().
|
|
322
|
+
consumer.seek_to_end(tp)
|
|
323
|
+
position = consumer.position(tp, timeout_ms=1000)
|
|
324
|
+
|
|
325
|
+
# Verify we got the expected position
|
|
326
|
+
assert position == 10, f"Expected position 10, got {position}"
|
|
@@ -12,6 +12,9 @@ import pytest
|
|
|
12
12
|
|
|
13
13
|
from kafka.conn import BrokerConnection, ConnectionStates
|
|
14
14
|
from kafka.future import Future
|
|
15
|
+
from kafka.conn import BrokerConnection, ConnectionStates, SSLWantWriteError
|
|
16
|
+
from kafka.metrics.metrics import Metrics
|
|
17
|
+
from kafka.metrics.stats.sensor import Sensor
|
|
15
18
|
from kafka.protocol.api import RequestHeader
|
|
16
19
|
from kafka.protocol.group import HeartbeatResponse
|
|
17
20
|
from kafka.protocol.metadata import MetadataRequest
|
|
@@ -43,10 +46,20 @@ def _socket(mocker):
|
|
|
43
46
|
mocker.patch('socket.socket', return_value=socket)
|
|
44
47
|
return socket
|
|
45
48
|
|
|
49
|
+
@pytest.fixture
|
|
50
|
+
def metrics(mocker):
|
|
51
|
+
metrics = mocker.MagicMock(Metrics)
|
|
52
|
+
metrics.mocked_sensors = {}
|
|
53
|
+
def sensor(name, **kwargs):
|
|
54
|
+
if name not in metrics.mocked_sensors:
|
|
55
|
+
metrics.mocked_sensors[name] = mocker.MagicMock(Sensor)
|
|
56
|
+
return metrics.mocked_sensors[name]
|
|
57
|
+
metrics.sensor.side_effect = sensor
|
|
58
|
+
return metrics
|
|
46
59
|
|
|
47
60
|
@pytest.fixture
|
|
48
|
-
def conn(_socket, dns_lookup, mocker):
|
|
49
|
-
conn = BrokerConnection('localhost', 9092, socket.AF_INET)
|
|
61
|
+
def conn(_socket, dns_lookup, metrics, mocker):
|
|
62
|
+
conn = BrokerConnection('localhost', 9092, socket.AF_INET, metrics=metrics)
|
|
50
63
|
mocker.patch.object(conn, '_try_api_versions_check', return_value=True)
|
|
51
64
|
return conn
|
|
52
65
|
|
|
@@ -228,6 +241,46 @@ def test_send_response(_socket, conn):
|
|
|
228
241
|
assert len(conn.in_flight_requests) == 1
|
|
229
242
|
|
|
230
243
|
|
|
244
|
+
def test_send_async_request_while_other_request_is_already_in_buffer(_socket, conn, metrics):
|
|
245
|
+
conn.connect()
|
|
246
|
+
assert conn.state is ConnectionStates.CONNECTED
|
|
247
|
+
assert 'node-0.bytes-sent' in metrics.mocked_sensors
|
|
248
|
+
bytes_sent_sensor = metrics.mocked_sensors['node-0.bytes-sent']
|
|
249
|
+
|
|
250
|
+
req1 = MetadataRequest[0](topics='foo')
|
|
251
|
+
header1 = RequestHeader(req1, client_id=conn.config['client_id'])
|
|
252
|
+
payload_bytes1 = len(header1.encode()) + len(req1.encode())
|
|
253
|
+
req2 = MetadataRequest[0]([])
|
|
254
|
+
header2 = RequestHeader(req2, client_id=conn.config['client_id'])
|
|
255
|
+
payload_bytes2 = len(header2.encode()) + len(req2.encode())
|
|
256
|
+
|
|
257
|
+
# The first call to the socket will raise a transient SSL exception. This will make the first
|
|
258
|
+
# request to be kept in the internal buffer to be sent in the next call of
|
|
259
|
+
# send_pending_requests_v2.
|
|
260
|
+
_socket.send.side_effect = [SSLWantWriteError, 4 + payload_bytes1, 4 + payload_bytes2]
|
|
261
|
+
|
|
262
|
+
conn.send(req1, blocking=False)
|
|
263
|
+
# This won't send any bytes because of the SSL exception and the request bytes will be kept in
|
|
264
|
+
# the buffer.
|
|
265
|
+
assert conn.send_pending_requests_v2() is False
|
|
266
|
+
assert bytes_sent_sensor.record.call_args_list[0].args == (0,)
|
|
267
|
+
|
|
268
|
+
conn.send(req2, blocking=False)
|
|
269
|
+
# This will send the remaining bytes in the buffer from the first request, but should notice
|
|
270
|
+
# that the second request was queued, therefore it should return False.
|
|
271
|
+
bytes_sent_sensor.record.reset_mock()
|
|
272
|
+
assert conn.send_pending_requests_v2() is False
|
|
273
|
+
bytes_sent_sensor.record.assert_called_once_with(4 + payload_bytes1)
|
|
274
|
+
|
|
275
|
+
bytes_sent_sensor.record.reset_mock()
|
|
276
|
+
assert conn.send_pending_requests_v2() is True
|
|
277
|
+
bytes_sent_sensor.record.assert_called_once_with(4 + payload_bytes2)
|
|
278
|
+
|
|
279
|
+
bytes_sent_sensor.record.reset_mock()
|
|
280
|
+
assert conn.send_pending_requests_v2() is True
|
|
281
|
+
bytes_sent_sensor.record.assert_called_once_with(0)
|
|
282
|
+
|
|
283
|
+
|
|
231
284
|
def test_send_error(_socket, conn):
|
|
232
285
|
conn.connect()
|
|
233
286
|
assert conn.state is ConnectionStates.CONNECTED
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.2.15'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/sticky/sorted_set.py
RENAMED
|
File without changes
|
{kafka_python-2.2.15 → kafka_python-2.2.17}/kafka/coordinator/assignors/sticky/sticky_assignor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|