kafka-python 3.0.0__py3-none-any.whl
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/__init__.py +34 -0
- kafka/__main__.py +5 -0
- kafka/admin/__init__.py +29 -0
- kafka/admin/__main__.py +5 -0
- kafka/admin/_acls.py +355 -0
- kafka/admin/_cluster.py +359 -0
- kafka/admin/_configs.py +479 -0
- kafka/admin/_groups.py +754 -0
- kafka/admin/_partitions.py +595 -0
- kafka/admin/_topics.py +281 -0
- kafka/admin/_transactions.py +450 -0
- kafka/admin/_users.py +194 -0
- kafka/admin/client.py +373 -0
- kafka/benchmarks/__init__.py +0 -0
- kafka/benchmarks/consumer_performance.py +138 -0
- kafka/benchmarks/load_example.py +109 -0
- kafka/benchmarks/producer_encode_path.py +201 -0
- kafka/benchmarks/producer_performance.py +161 -0
- kafka/benchmarks/profile_protocol.py +138 -0
- kafka/benchmarks/protocol_old_vs_new.py +447 -0
- kafka/benchmarks/record_batch_compose.py +77 -0
- kafka/benchmarks/record_batch_read.py +82 -0
- kafka/benchmarks/varint_speed.py +426 -0
- kafka/cli/__init__.py +36 -0
- kafka/cli/admin/__init__.py +117 -0
- kafka/cli/admin/acls/__init__.py +9 -0
- kafka/cli/admin/acls/common.py +76 -0
- kafka/cli/admin/acls/create.py +19 -0
- kafka/cli/admin/acls/delete.py +23 -0
- kafka/cli/admin/acls/describe.py +16 -0
- kafka/cli/admin/cluster/__init__.py +14 -0
- kafka/cli/admin/cluster/describe.py +11 -0
- kafka/cli/admin/cluster/describe_quorum.py +11 -0
- kafka/cli/admin/cluster/features.py +52 -0
- kafka/cli/admin/cluster/log_dirs.py +43 -0
- kafka/cli/admin/cluster/versions.py +33 -0
- kafka/cli/admin/configs/__init__.py +10 -0
- kafka/cli/admin/configs/alter.py +43 -0
- kafka/cli/admin/configs/common.py +17 -0
- kafka/cli/admin/configs/describe.py +30 -0
- kafka/cli/admin/configs/list.py +16 -0
- kafka/cli/admin/configs/reset.py +20 -0
- kafka/cli/admin/groups/__init__.py +16 -0
- kafka/cli/admin/groups/alter_offsets.py +30 -0
- kafka/cli/admin/groups/delete.py +11 -0
- kafka/cli/admin/groups/delete_offsets.py +29 -0
- kafka/cli/admin/groups/describe.py +11 -0
- kafka/cli/admin/groups/list.py +28 -0
- kafka/cli/admin/groups/list_offsets.py +29 -0
- kafka/cli/admin/groups/remove_members.py +40 -0
- kafka/cli/admin/groups/reset_offsets.py +139 -0
- kafka/cli/admin/partitions/__init__.py +21 -0
- kafka/cli/admin/partitions/alter_reassignments.py +37 -0
- kafka/cli/admin/partitions/create.py +27 -0
- kafka/cli/admin/partitions/delete_records.py +31 -0
- kafka/cli/admin/partitions/describe.py +36 -0
- kafka/cli/admin/partitions/elect_leaders.py +53 -0
- kafka/cli/admin/partitions/list_offsets.py +88 -0
- kafka/cli/admin/partitions/list_reassignments.py +35 -0
- kafka/cli/admin/topics/__init__.py +10 -0
- kafka/cli/admin/topics/create.py +13 -0
- kafka/cli/admin/topics/delete.py +19 -0
- kafka/cli/admin/topics/describe.py +18 -0
- kafka/cli/admin/topics/list.py +11 -0
- kafka/cli/admin/transactions/__init__.py +17 -0
- kafka/cli/admin/transactions/abort.py +38 -0
- kafka/cli/admin/transactions/describe.py +24 -0
- kafka/cli/admin/transactions/describe_producers.py +29 -0
- kafka/cli/admin/transactions/find_hanging.py +26 -0
- kafka/cli/admin/transactions/list.py +37 -0
- kafka/cli/admin/users/__init__.py +8 -0
- kafka/cli/admin/users/alter_user_scram_credentials.py +34 -0
- kafka/cli/admin/users/describe_user_scram_credentials.py +15 -0
- kafka/cli/common.py +95 -0
- kafka/cli/consumer/__init__.py +63 -0
- kafka/cli/producer/__init__.py +57 -0
- kafka/cluster.py +824 -0
- kafka/codec.py +325 -0
- kafka/consumer/__init__.py +5 -0
- kafka/consumer/__main__.py +5 -0
- kafka/consumer/fetcher.py +2012 -0
- kafka/consumer/group.py +1347 -0
- kafka/consumer/subscription_state.py +897 -0
- kafka/coordinator/__init__.py +0 -0
- kafka/coordinator/assignors/__init__.py +0 -0
- kafka/coordinator/assignors/abstract.py +90 -0
- kafka/coordinator/assignors/cooperative_sticky.py +167 -0
- kafka/coordinator/assignors/range.py +81 -0
- kafka/coordinator/assignors/roundrobin.py +101 -0
- kafka/coordinator/assignors/sticky/StickyAssignorUserData.json +37 -0
- kafka/coordinator/assignors/sticky/__init__.py +0 -0
- kafka/coordinator/assignors/sticky/partition_movements.py +149 -0
- kafka/coordinator/assignors/sticky/sorted_set.py +63 -0
- kafka/coordinator/assignors/sticky/sticky_assignor.py +665 -0
- kafka/coordinator/assignors/sticky/user_data.py +8 -0
- kafka/coordinator/base.py +1215 -0
- kafka/coordinator/consumer.py +1224 -0
- kafka/coordinator/heartbeat.py +82 -0
- kafka/coordinator/subscription.py +34 -0
- kafka/errors.py +1004 -0
- kafka/future.py +166 -0
- kafka/metrics/__init__.py +13 -0
- kafka/metrics/compound_stat.py +33 -0
- kafka/metrics/dict_reporter.py +81 -0
- kafka/metrics/kafka_metric.py +36 -0
- kafka/metrics/measurable.py +27 -0
- kafka/metrics/measurable_stat.py +13 -0
- kafka/metrics/metric_config.py +33 -0
- kafka/metrics/metric_name.py +105 -0
- kafka/metrics/metrics.py +261 -0
- kafka/metrics/metrics_reporter.py +53 -0
- kafka/metrics/quota.py +41 -0
- kafka/metrics/stat.py +19 -0
- kafka/metrics/stats/__init__.py +15 -0
- kafka/metrics/stats/avg.py +24 -0
- kafka/metrics/stats/count.py +17 -0
- kafka/metrics/stats/histogram.py +99 -0
- kafka/metrics/stats/max_stat.py +17 -0
- kafka/metrics/stats/min_stat.py +19 -0
- kafka/metrics/stats/percentile.py +14 -0
- kafka/metrics/stats/percentiles.py +75 -0
- kafka/metrics/stats/rate.py +118 -0
- kafka/metrics/stats/sampled_stat.py +99 -0
- kafka/metrics/stats/sensor.py +136 -0
- kafka/metrics/stats/total.py +15 -0
- kafka/net/__init__.py +19 -0
- kafka/net/compat.py +165 -0
- kafka/net/connection.py +593 -0
- kafka/net/http_connect.py +144 -0
- kafka/net/inet.py +122 -0
- kafka/net/manager.py +451 -0
- kafka/net/metrics.py +149 -0
- kafka/net/sasl/__init__.py +32 -0
- kafka/net/sasl/abc.py +28 -0
- kafka/net/sasl/gssapi.py +95 -0
- kafka/net/sasl/msk.py +245 -0
- kafka/net/sasl/oauth.py +98 -0
- kafka/net/sasl/plain.py +42 -0
- kafka/net/sasl/scram.py +135 -0
- kafka/net/sasl/sspi.py +111 -0
- kafka/net/selector.py +644 -0
- kafka/net/socks5.py +262 -0
- kafka/net/transport.py +415 -0
- kafka/net/wakeup_notifier.py +72 -0
- kafka/partitioner/__init__.py +8 -0
- kafka/partitioner/abc.py +8 -0
- kafka/partitioner/default.py +89 -0
- kafka/partitioner/sticky.py +109 -0
- kafka/producer/__init__.py +5 -0
- kafka/producer/__main__.py +5 -0
- kafka/producer/future.py +101 -0
- kafka/producer/kafka.py +1123 -0
- kafka/producer/producer_batch.py +192 -0
- kafka/producer/record_accumulator.py +647 -0
- kafka/producer/sender.py +884 -0
- kafka/producer/transaction_manager.py +1326 -0
- kafka/protocol/__init__.py +0 -0
- kafka/protocol/admin/__init__.py +29 -0
- kafka/protocol/admin/acl.py +83 -0
- kafka/protocol/admin/acl.pyi +375 -0
- kafka/protocol/admin/client_quotas.py +14 -0
- kafka/protocol/admin/client_quotas.pyi +265 -0
- kafka/protocol/admin/cluster.py +31 -0
- kafka/protocol/admin/cluster.pyi +620 -0
- kafka/protocol/admin/configs.py +22 -0
- kafka/protocol/admin/configs.pyi +437 -0
- kafka/protocol/admin/groups.py +24 -0
- kafka/protocol/admin/groups.pyi +261 -0
- kafka/protocol/admin/topics.py +53 -0
- kafka/protocol/admin/topics.pyi +982 -0
- kafka/protocol/admin/transactions.py +18 -0
- kafka/protocol/admin/transactions.pyi +311 -0
- kafka/protocol/admin/users.py +14 -0
- kafka/protocol/admin/users.pyi +223 -0
- kafka/protocol/api_data.py +125 -0
- kafka/protocol/api_header.py +55 -0
- kafka/protocol/api_key.py +97 -0
- kafka/protocol/api_message.py +277 -0
- kafka/protocol/broker_version_data.py +246 -0
- kafka/protocol/consumer/__init__.py +13 -0
- kafka/protocol/consumer/fetch.py +16 -0
- kafka/protocol/consumer/fetch.pyi +298 -0
- kafka/protocol/consumer/group.py +38 -0
- kafka/protocol/consumer/group.pyi +824 -0
- kafka/protocol/consumer/metadata.py +30 -0
- kafka/protocol/consumer/metadata.pyi +89 -0
- kafka/protocol/consumer/offsets.py +75 -0
- kafka/protocol/consumer/offsets.pyi +288 -0
- kafka/protocol/data_container.py +166 -0
- kafka/protocol/frame.py +30 -0
- kafka/protocol/generate_stubs.py +468 -0
- kafka/protocol/metadata/__init__.py +10 -0
- kafka/protocol/metadata/api_versions.py +41 -0
- kafka/protocol/metadata/api_versions.pyi +128 -0
- kafka/protocol/metadata/find_coordinator.py +19 -0
- kafka/protocol/metadata/find_coordinator.pyi +105 -0
- kafka/protocol/metadata/metadata.py +34 -0
- kafka/protocol/metadata/metadata.pyi +160 -0
- kafka/protocol/old/__init__.py +0 -0
- kafka/protocol/old/abstract.py +17 -0
- kafka/protocol/old/add_offsets_to_txn.py +54 -0
- kafka/protocol/old/add_partitions_to_txn.py +71 -0
- kafka/protocol/old/admin.py +1086 -0
- kafka/protocol/old/api.py +205 -0
- kafka/protocol/old/api_versions.py +133 -0
- kafka/protocol/old/commit.py +355 -0
- kafka/protocol/old/consumer_protocol.py +36 -0
- kafka/protocol/old/end_txn.py +53 -0
- kafka/protocol/old/fetch.py +408 -0
- kafka/protocol/old/find_coordinator.py +72 -0
- kafka/protocol/old/group.py +451 -0
- kafka/protocol/old/init_producer_id.py +42 -0
- kafka/protocol/old/list_offsets.py +186 -0
- kafka/protocol/old/metadata.py +290 -0
- kafka/protocol/old/offset_for_leader_epoch.py +133 -0
- kafka/protocol/old/produce.py +247 -0
- kafka/protocol/old/sasl_authenticate.py +38 -0
- kafka/protocol/old/sasl_handshake.py +39 -0
- kafka/protocol/old/struct.py +87 -0
- kafka/protocol/old/txn_offset_commit.py +73 -0
- kafka/protocol/old/types.py +440 -0
- kafka/protocol/parser.py +191 -0
- kafka/protocol/producer/__init__.py +7 -0
- kafka/protocol/producer/produce.py +17 -0
- kafka/protocol/producer/produce.pyi +197 -0
- kafka/protocol/producer/transaction.py +30 -0
- kafka/protocol/producer/transaction.pyi +663 -0
- kafka/protocol/sasl.py +52 -0
- kafka/protocol/sasl.pyi +126 -0
- kafka/protocol/schemas/__init__.py +7 -0
- kafka/protocol/schemas/fields/__init__.py +7 -0
- kafka/protocol/schemas/fields/array.py +127 -0
- kafka/protocol/schemas/fields/base.py +156 -0
- kafka/protocol/schemas/fields/codecs/__init__.py +12 -0
- kafka/protocol/schemas/fields/codecs/encode_buffer.py +82 -0
- kafka/protocol/schemas/fields/codecs/tagged_fields.py +109 -0
- kafka/protocol/schemas/fields/codecs/types.py +505 -0
- kafka/protocol/schemas/fields/codegen.py +40 -0
- kafka/protocol/schemas/fields/simple.py +127 -0
- kafka/protocol/schemas/fields/struct.py +357 -0
- kafka/protocol/schemas/fields/struct_array.py +142 -0
- kafka/protocol/schemas/load_json.py +42 -0
- kafka/protocol/schemas/resources/AddOffsetsToTxnRequest.json +40 -0
- kafka/protocol/schemas/resources/AddOffsetsToTxnResponse.json +35 -0
- kafka/protocol/schemas/resources/AddPartitionsToTxnRequest.json +65 -0
- kafka/protocol/schemas/resources/AddPartitionsToTxnResponse.json +60 -0
- kafka/protocol/schemas/resources/AlterClientQuotasRequest.json +47 -0
- kafka/protocol/schemas/resources/AlterClientQuotasResponse.json +41 -0
- kafka/protocol/schemas/resources/AlterConfigsRequest.json +43 -0
- kafka/protocol/schemas/resources/AlterConfigsResponse.json +39 -0
- kafka/protocol/schemas/resources/AlterPartitionReassignmentsRequest.json +42 -0
- kafka/protocol/schemas/resources/AlterPartitionReassignmentsResponse.json +47 -0
- kafka/protocol/schemas/resources/AlterReplicaLogDirsRequest.json +41 -0
- kafka/protocol/schemas/resources/AlterReplicaLogDirsResponse.json +41 -0
- kafka/protocol/schemas/resources/AlterUserScramCredentialsRequest.json +45 -0
- kafka/protocol/schemas/resources/AlterUserScramCredentialsResponse.json +35 -0
- kafka/protocol/schemas/resources/ApiVersionsRequest.json +34 -0
- kafka/protocol/schemas/resources/ApiVersionsResponse.json +79 -0
- kafka/protocol/schemas/resources/ConsumerProtocolAssignment.json +42 -0
- kafka/protocol/schemas/resources/ConsumerProtocolSubscription.json +49 -0
- kafka/protocol/schemas/resources/CreateAclsRequest.json +46 -0
- kafka/protocol/schemas/resources/CreateAclsResponse.json +37 -0
- kafka/protocol/schemas/resources/CreatePartitionsRequest.json +47 -0
- kafka/protocol/schemas/resources/CreatePartitionsResponse.json +41 -0
- kafka/protocol/schemas/resources/CreateTopicsRequest.json +65 -0
- kafka/protocol/schemas/resources/CreateTopicsResponse.json +72 -0
- kafka/protocol/schemas/resources/DeleteAclsRequest.json +46 -0
- kafka/protocol/schemas/resources/DeleteAclsResponse.json +59 -0
- kafka/protocol/schemas/resources/DeleteGroupsRequest.json +30 -0
- kafka/protocol/schemas/resources/DeleteGroupsResponse.json +36 -0
- kafka/protocol/schemas/resources/DeleteRecordsRequest.json +42 -0
- kafka/protocol/schemas/resources/DeleteRecordsResponse.json +43 -0
- kafka/protocol/schemas/resources/DeleteTopicsRequest.json +43 -0
- kafka/protocol/schemas/resources/DeleteTopicsResponse.json +52 -0
- kafka/protocol/schemas/resources/DescribeAclsRequest.json +43 -0
- kafka/protocol/schemas/resources/DescribeAclsResponse.json +55 -0
- kafka/protocol/schemas/resources/DescribeClientQuotasRequest.json +37 -0
- kafka/protocol/schemas/resources/DescribeClientQuotasResponse.json +47 -0
- kafka/protocol/schemas/resources/DescribeClusterRequest.json +35 -0
- kafka/protocol/schemas/resources/DescribeClusterResponse.json +56 -0
- kafka/protocol/schemas/resources/DescribeConfigsRequest.json +42 -0
- kafka/protocol/schemas/resources/DescribeConfigsResponse.json +69 -0
- kafka/protocol/schemas/resources/DescribeGroupsRequest.json +38 -0
- kafka/protocol/schemas/resources/DescribeGroupsResponse.json +74 -0
- kafka/protocol/schemas/resources/DescribeLogDirsRequest.json +38 -0
- kafka/protocol/schemas/resources/DescribeLogDirsResponse.json +65 -0
- kafka/protocol/schemas/resources/DescribeProducersRequest.json +32 -0
- kafka/protocol/schemas/resources/DescribeProducersResponse.json +55 -0
- kafka/protocol/schemas/resources/DescribeQuorumRequest.json +39 -0
- kafka/protocol/schemas/resources/DescribeQuorumResponse.json +82 -0
- kafka/protocol/schemas/resources/DescribeTopicPartitionsRequest.json +40 -0
- kafka/protocol/schemas/resources/DescribeTopicPartitionsResponse.json +66 -0
- kafka/protocol/schemas/resources/DescribeTransactionsRequest.json +27 -0
- kafka/protocol/schemas/resources/DescribeTransactionsResponse.json +52 -0
- kafka/protocol/schemas/resources/DescribeUserScramCredentialsRequest.json +30 -0
- kafka/protocol/schemas/resources/DescribeUserScramCredentialsResponse.json +45 -0
- kafka/protocol/schemas/resources/ElectLeadersRequest.json +41 -0
- kafka/protocol/schemas/resources/ElectLeadersResponse.json +45 -0
- kafka/protocol/schemas/resources/EndTxnRequest.json +43 -0
- kafka/protocol/schemas/resources/EndTxnResponse.json +41 -0
- kafka/protocol/schemas/resources/FetchRequest.json +125 -0
- kafka/protocol/schemas/resources/FetchResponse.json +124 -0
- kafka/protocol/schemas/resources/FindCoordinatorRequest.json +43 -0
- kafka/protocol/schemas/resources/FindCoordinatorResponse.json +58 -0
- kafka/protocol/schemas/resources/HeartbeatRequest.json +39 -0
- kafka/protocol/schemas/resources/HeartbeatResponse.json +35 -0
- kafka/protocol/schemas/resources/IncrementalAlterConfigsRequest.json +44 -0
- kafka/protocol/schemas/resources/IncrementalAlterConfigsResponse.json +38 -0
- kafka/protocol/schemas/resources/InitProducerIdRequest.json +50 -0
- kafka/protocol/schemas/resources/InitProducerIdResponse.json +47 -0
- kafka/protocol/schemas/resources/JoinGroupRequest.json +63 -0
- kafka/protocol/schemas/resources/JoinGroupResponse.json +69 -0
- kafka/protocol/schemas/resources/LeaveGroupRequest.json +47 -0
- kafka/protocol/schemas/resources/LeaveGroupResponse.json +47 -0
- kafka/protocol/schemas/resources/ListConfigResourcesRequest.json +31 -0
- kafka/protocol/schemas/resources/ListConfigResourcesResponse.json +37 -0
- kafka/protocol/schemas/resources/ListGroupsRequest.json +36 -0
- kafka/protocol/schemas/resources/ListGroupsResponse.json +49 -0
- kafka/protocol/schemas/resources/ListOffsetsRequest.json +72 -0
- kafka/protocol/schemas/resources/ListOffsetsResponse.json +71 -0
- kafka/protocol/schemas/resources/ListPartitionReassignmentsRequest.json +34 -0
- kafka/protocol/schemas/resources/ListPartitionReassignmentsResponse.json +46 -0
- kafka/protocol/schemas/resources/ListTransactionsRequest.json +40 -0
- kafka/protocol/schemas/resources/ListTransactionsResponse.json +42 -0
- kafka/protocol/schemas/resources/MetadataRequest.json +56 -0
- kafka/protocol/schemas/resources/MetadataResponse.json +101 -0
- kafka/protocol/schemas/resources/OffsetCommitRequest.json +76 -0
- kafka/protocol/schemas/resources/OffsetCommitResponse.json +71 -0
- kafka/protocol/schemas/resources/OffsetDeleteRequest.json +39 -0
- kafka/protocol/schemas/resources/OffsetDeleteResponse.json +42 -0
- kafka/protocol/schemas/resources/OffsetFetchRequest.json +76 -0
- kafka/protocol/schemas/resources/OffsetFetchResponse.json +107 -0
- kafka/protocol/schemas/resources/OffsetForLeaderEpochRequest.json +52 -0
- kafka/protocol/schemas/resources/OffsetForLeaderEpochResponse.json +51 -0
- kafka/protocol/schemas/resources/ProduceRequest.json +73 -0
- kafka/protocol/schemas/resources/ProduceResponse.json +96 -0
- kafka/protocol/schemas/resources/RequestHeader.json +44 -0
- kafka/protocol/schemas/resources/ResponseHeader.json +26 -0
- kafka/protocol/schemas/resources/SaslAuthenticateRequest.json +29 -0
- kafka/protocol/schemas/resources/SaslAuthenticateResponse.json +34 -0
- kafka/protocol/schemas/resources/SaslHandshakeRequest.json +31 -0
- kafka/protocol/schemas/resources/SaslHandshakeResponse.json +32 -0
- kafka/protocol/schemas/resources/SyncGroupRequest.json +56 -0
- kafka/protocol/schemas/resources/SyncGroupResponse.json +46 -0
- kafka/protocol/schemas/resources/TxnOffsetCommitRequest.json +68 -0
- kafka/protocol/schemas/resources/TxnOffsetCommitResponse.json +47 -0
- kafka/protocol/schemas/resources/UpdateFeaturesRequest.json +43 -0
- kafka/protocol/schemas/resources/UpdateFeaturesResponse.json +39 -0
- kafka/protocol/schemas/resources/WriteTxnMarkersRequest.json +49 -0
- kafka/protocol/schemas/resources/WriteTxnMarkersResponse.json +45 -0
- kafka/protocol/schemas/resources/__init__.py +0 -0
- kafka/record/__init__.py +3 -0
- kafka/record/_crc32c.py +161 -0
- kafka/record/abc.py +144 -0
- kafka/record/default_records.py +782 -0
- kafka/record/legacy_records.py +587 -0
- kafka/record/memory_records.py +255 -0
- kafka/record/util.py +135 -0
- kafka/serializer/__init__.py +4 -0
- kafka/serializer/abstract.py +20 -0
- kafka/serializer/default.py +16 -0
- kafka/serializer/json.py +17 -0
- kafka/serializer/wrapper.py +21 -0
- kafka/structs.py +69 -0
- kafka/util.py +159 -0
- kafka/vendor/__init__.py +0 -0
- kafka/version.py +1 -0
- kafka_python-3.0.0.dist-info/METADATA +319 -0
- kafka_python-3.0.0.dist-info/RECORD +373 -0
- kafka_python-3.0.0.dist-info/WHEEL +5 -0
- kafka_python-3.0.0.dist-info/entry_points.txt +2 -0
- kafka_python-3.0.0.dist-info/licenses/LICENSE +202 -0
- kafka_python-3.0.0.dist-info/top_level.txt +1 -0
kafka/future.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import logging
|
|
3
|
+
import threading
|
|
4
|
+
|
|
5
|
+
from kafka.errors import RetriableError
|
|
6
|
+
|
|
7
|
+
log = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Future:
|
|
11
|
+
__slots__ = ('is_done', 'value', 'exception', '_callbacks', '_errbacks', '_lock')
|
|
12
|
+
error_on_callbacks = False # and errbacks
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.is_done = False
|
|
16
|
+
self.value = None
|
|
17
|
+
self.exception = None
|
|
18
|
+
self._callbacks = []
|
|
19
|
+
self._errbacks = []
|
|
20
|
+
self._lock = threading.Lock()
|
|
21
|
+
|
|
22
|
+
def succeeded(self):
|
|
23
|
+
return self.is_done and self.exception is None
|
|
24
|
+
|
|
25
|
+
def failed(self):
|
|
26
|
+
return self.is_done and self.exception is not None
|
|
27
|
+
|
|
28
|
+
def retriable(self):
|
|
29
|
+
return self.is_done and isinstance(self.exception, RetriableError)
|
|
30
|
+
|
|
31
|
+
def success(self, value):
|
|
32
|
+
# Hot path: called once per produced record via the sender thread's
|
|
33
|
+
# batch-completion callback chain. Kept tight: explicit acquire/
|
|
34
|
+
# release (cheaper than `with`), callbacks snapshot under the lock,
|
|
35
|
+
# dispatch outside the lock, inlined callback loop (avoids an extra
|
|
36
|
+
# Python frame per completion).
|
|
37
|
+
# Clearing the lists releases any reference cycle held via stored
|
|
38
|
+
# bound methods (e.g. FutureProduceResult<->FutureRecordMetadata).
|
|
39
|
+
lock = self._lock
|
|
40
|
+
lock.acquire()
|
|
41
|
+
self.value = value
|
|
42
|
+
self.is_done = True
|
|
43
|
+
callbacks = self._callbacks
|
|
44
|
+
self._callbacks = None
|
|
45
|
+
self._errbacks = None
|
|
46
|
+
lock.release()
|
|
47
|
+
if callbacks:
|
|
48
|
+
error_on_callbacks = self.error_on_callbacks
|
|
49
|
+
for f in callbacks:
|
|
50
|
+
try:
|
|
51
|
+
f(value)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
log.exception('Error processing callback')
|
|
54
|
+
if error_on_callbacks:
|
|
55
|
+
raise e
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def failure(self, e):
|
|
59
|
+
exception = e if type(e) is not type else e()
|
|
60
|
+
if not isinstance(exception, BaseException):
|
|
61
|
+
raise TypeError('future failed without an exception')
|
|
62
|
+
lock = self._lock
|
|
63
|
+
lock.acquire()
|
|
64
|
+
self.exception = exception
|
|
65
|
+
self.is_done = True
|
|
66
|
+
errbacks = self._errbacks
|
|
67
|
+
self._callbacks = None
|
|
68
|
+
self._errbacks = None
|
|
69
|
+
lock.release()
|
|
70
|
+
if errbacks:
|
|
71
|
+
error_on_callbacks = self.error_on_callbacks
|
|
72
|
+
for f in errbacks:
|
|
73
|
+
try:
|
|
74
|
+
f(exception)
|
|
75
|
+
except Exception as err:
|
|
76
|
+
log.exception('Error processing errback')
|
|
77
|
+
if error_on_callbacks:
|
|
78
|
+
raise err
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
def add_callback(self, f, *args, **kwargs):
|
|
82
|
+
if args or kwargs:
|
|
83
|
+
f = functools.partial(f, *args, **kwargs)
|
|
84
|
+
lock = self._lock
|
|
85
|
+
lock.acquire()
|
|
86
|
+
if not self.is_done:
|
|
87
|
+
self._callbacks.append(f)
|
|
88
|
+
lock.release()
|
|
89
|
+
return self
|
|
90
|
+
lock.release()
|
|
91
|
+
if self.exception is None:
|
|
92
|
+
try:
|
|
93
|
+
f(self.value)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
log.exception('Error processing callback')
|
|
96
|
+
if self.error_on_callbacks:
|
|
97
|
+
raise e
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def add_errback(self, f, *args, **kwargs):
|
|
101
|
+
if args or kwargs:
|
|
102
|
+
f = functools.partial(f, *args, **kwargs)
|
|
103
|
+
lock = self._lock
|
|
104
|
+
lock.acquire()
|
|
105
|
+
if not self.is_done:
|
|
106
|
+
self._errbacks.append(f)
|
|
107
|
+
lock.release()
|
|
108
|
+
return self
|
|
109
|
+
lock.release()
|
|
110
|
+
if self.exception is not None:
|
|
111
|
+
try:
|
|
112
|
+
f(self.exception)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
log.exception('Error processing errback')
|
|
115
|
+
if self.error_on_callbacks:
|
|
116
|
+
raise e
|
|
117
|
+
return self
|
|
118
|
+
|
|
119
|
+
def _add_cb_eb(self, cb, eb):
|
|
120
|
+
"""Register a (callback, errback) pair under a single lock acquire.
|
|
121
|
+
|
|
122
|
+
Fast path for call sites that always register both a plain callback
|
|
123
|
+
and errback with no ``*args``/``**kwargs``. Used on the producer hot
|
|
124
|
+
path (``FutureRecordMetadata`` -> ``FutureProduceResult``) to halve
|
|
125
|
+
the per-record lock-acquire count vs. calling ``add_callback()`` +
|
|
126
|
+
``add_errback()`` separately.
|
|
127
|
+
"""
|
|
128
|
+
lock = self._lock
|
|
129
|
+
lock.acquire()
|
|
130
|
+
if not self.is_done:
|
|
131
|
+
self._callbacks.append(cb)
|
|
132
|
+
self._errbacks.append(eb)
|
|
133
|
+
lock.release()
|
|
134
|
+
return self
|
|
135
|
+
lock.release()
|
|
136
|
+
if self.exception is None:
|
|
137
|
+
try:
|
|
138
|
+
cb(self.value)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
log.exception('Error processing callback')
|
|
141
|
+
if self.error_on_callbacks:
|
|
142
|
+
raise e
|
|
143
|
+
else:
|
|
144
|
+
try:
|
|
145
|
+
eb(self.exception)
|
|
146
|
+
except Exception as e:
|
|
147
|
+
log.exception('Error processing errback')
|
|
148
|
+
if self.error_on_callbacks:
|
|
149
|
+
raise e
|
|
150
|
+
return self
|
|
151
|
+
|
|
152
|
+
def add_both(self, f, *args, **kwargs):
|
|
153
|
+
self.add_callback(f, *args, **kwargs)
|
|
154
|
+
self.add_errback(f, *args, **kwargs)
|
|
155
|
+
return self
|
|
156
|
+
|
|
157
|
+
def chain(self, future):
|
|
158
|
+
self._add_cb_eb(future.success, future.failure)
|
|
159
|
+
return self
|
|
160
|
+
|
|
161
|
+
def __await__(self):
|
|
162
|
+
if not self.is_done:
|
|
163
|
+
yield self
|
|
164
|
+
if self.exception:
|
|
165
|
+
raise self.exception
|
|
166
|
+
return self.value
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from kafka.metrics.compound_stat import NamedMeasurable
|
|
2
|
+
from kafka.metrics.dict_reporter import DictReporter
|
|
3
|
+
from kafka.metrics.kafka_metric import KafkaMetric
|
|
4
|
+
from kafka.metrics.measurable import AnonMeasurable
|
|
5
|
+
from kafka.metrics.metric_config import MetricConfig
|
|
6
|
+
from kafka.metrics.metric_name import MetricName
|
|
7
|
+
from kafka.metrics.metrics import Metrics
|
|
8
|
+
from kafka.metrics.quota import Quota
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
'AnonMeasurable', 'DictReporter', 'KafkaMetric', 'MetricConfig',
|
|
12
|
+
'MetricName', 'Metrics', 'NamedMeasurable', 'Quota'
|
|
13
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from kafka.metrics.stat import AbstractStat
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AbstractCompoundStat(AbstractStat, ABC):
|
|
7
|
+
"""
|
|
8
|
+
A compound stat is a stat where a single measurement and associated
|
|
9
|
+
data structure feeds many metrics. This is the example for a
|
|
10
|
+
histogram which has many associated percentiles.
|
|
11
|
+
"""
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def stats(self):
|
|
14
|
+
"""
|
|
15
|
+
Return list of NamedMeasurable
|
|
16
|
+
"""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NamedMeasurable:
|
|
21
|
+
__slots__ = ('_name', '_stat')
|
|
22
|
+
|
|
23
|
+
def __init__(self, metric_name, measurable_stat):
|
|
24
|
+
self._name = metric_name
|
|
25
|
+
self._stat = measurable_stat
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def name(self):
|
|
29
|
+
return self._name
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def stat(self):
|
|
33
|
+
return self._stat
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import threading
|
|
3
|
+
|
|
4
|
+
from kafka.metrics.metrics_reporter import AbstractMetricsReporter
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DictReporter(AbstractMetricsReporter):
|
|
10
|
+
"""A basic dictionary based metrics reporter.
|
|
11
|
+
|
|
12
|
+
Store all metrics in a two level dictionary of category > name > metric.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, prefix=''):
|
|
15
|
+
self._lock = threading.Lock()
|
|
16
|
+
self._prefix = prefix if prefix else '' # never allow None
|
|
17
|
+
self._store = {}
|
|
18
|
+
|
|
19
|
+
def snapshot(self):
|
|
20
|
+
"""
|
|
21
|
+
Return a nested dictionary snapshot of all metrics and their
|
|
22
|
+
values at this time. Example:
|
|
23
|
+
{
|
|
24
|
+
'category': {
|
|
25
|
+
'metric1_name': 42.0,
|
|
26
|
+
'metric2_name': 'foo'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
"""
|
|
30
|
+
return dict((category, dict((name, metric.value())
|
|
31
|
+
for name, metric in list(metrics.items())))
|
|
32
|
+
for category, metrics in
|
|
33
|
+
list(self._store.items()))
|
|
34
|
+
|
|
35
|
+
def init(self, metrics):
|
|
36
|
+
for metric in metrics:
|
|
37
|
+
self.metric_change(metric)
|
|
38
|
+
|
|
39
|
+
def metric_change(self, metric):
|
|
40
|
+
with self._lock:
|
|
41
|
+
category = self.get_category(metric)
|
|
42
|
+
if category not in self._store:
|
|
43
|
+
self._store[category] = {}
|
|
44
|
+
self._store[category][metric.metric_name.name] = metric
|
|
45
|
+
|
|
46
|
+
def metric_removal(self, metric):
|
|
47
|
+
with self._lock:
|
|
48
|
+
category = self.get_category(metric)
|
|
49
|
+
metrics = self._store.get(category, {})
|
|
50
|
+
removed = metrics.pop(metric.metric_name.name, None)
|
|
51
|
+
if not metrics:
|
|
52
|
+
self._store.pop(category, None)
|
|
53
|
+
return removed
|
|
54
|
+
|
|
55
|
+
def get_category(self, metric):
|
|
56
|
+
"""
|
|
57
|
+
Return a string category for the metric.
|
|
58
|
+
|
|
59
|
+
The category is made up of this reporter's prefix and the
|
|
60
|
+
metric's group and tags.
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
prefix = 'foo', group = 'bar', tags = {'a': 1, 'b': 2}
|
|
64
|
+
returns: 'foo.bar.a=1,b=2'
|
|
65
|
+
|
|
66
|
+
prefix = 'foo', group = 'bar', tags = None
|
|
67
|
+
returns: 'foo.bar'
|
|
68
|
+
|
|
69
|
+
prefix = None, group = 'bar', tags = None
|
|
70
|
+
returns: 'bar'
|
|
71
|
+
"""
|
|
72
|
+
tags = ','.join('%s=%s' % (k, v) for k, v in
|
|
73
|
+
sorted(metric.metric_name.tags.items()))
|
|
74
|
+
return '.'.join(x for x in
|
|
75
|
+
[self._prefix, metric.metric_name.group, tags] if x)
|
|
76
|
+
|
|
77
|
+
def configure(self, configs):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
def close(self):
|
|
81
|
+
pass
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class KafkaMetric:
|
|
5
|
+
__slots__ = ('_metric_name', '_measurable', '_config')
|
|
6
|
+
|
|
7
|
+
# NOTE java constructor takes a lock instance
|
|
8
|
+
def __init__(self, metric_name, measurable, config):
|
|
9
|
+
if not metric_name:
|
|
10
|
+
raise ValueError('metric_name must be non-empty')
|
|
11
|
+
if not measurable:
|
|
12
|
+
raise ValueError('measurable must be non-empty')
|
|
13
|
+
self._metric_name = metric_name
|
|
14
|
+
self._measurable = measurable
|
|
15
|
+
self._config = config
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def metric_name(self):
|
|
19
|
+
return self._metric_name
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def measurable(self):
|
|
23
|
+
return self._measurable
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def config(self):
|
|
27
|
+
return self._config
|
|
28
|
+
|
|
29
|
+
@config.setter
|
|
30
|
+
def config(self, config):
|
|
31
|
+
self._config = config
|
|
32
|
+
|
|
33
|
+
def value(self, time_ms=None):
|
|
34
|
+
if time_ms is None:
|
|
35
|
+
time_ms = time.monotonic() * 1000
|
|
36
|
+
return self._measurable.measure(self._config, time_ms)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AbstractMeasurable(ABC):
|
|
5
|
+
"""A measurable quantity that can be registered as a metric"""
|
|
6
|
+
@abstractmethod
|
|
7
|
+
def measure(self, config, now):
|
|
8
|
+
"""
|
|
9
|
+
Measure this quantity and return the result
|
|
10
|
+
|
|
11
|
+
Arguments:
|
|
12
|
+
config (MetricConfig): The configuration for this metric
|
|
13
|
+
now (int): The POSIX time in milliseconds the measurement
|
|
14
|
+
is being taken
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
The measured value
|
|
18
|
+
"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AnonMeasurable(AbstractMeasurable):
|
|
23
|
+
def __init__(self, measure_fn):
|
|
24
|
+
self._measure_fn = measure_fn
|
|
25
|
+
|
|
26
|
+
def measure(self, config, now):
|
|
27
|
+
return float(self._measure_fn(config, now))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
|
|
3
|
+
from kafka.metrics.measurable import AbstractMeasurable
|
|
4
|
+
from kafka.metrics.stat import AbstractStat
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AbstractMeasurableStat(AbstractStat, AbstractMeasurable, ABC):
|
|
8
|
+
"""
|
|
9
|
+
An AbstractMeasurableStat is an AbstractStat that is also
|
|
10
|
+
an AbstractMeasurable (i.e. can produce a single floating point value).
|
|
11
|
+
This is the interface used for most of the simple statistics such
|
|
12
|
+
as Avg, Max, Count, etc.
|
|
13
|
+
"""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MetricConfig:
|
|
5
|
+
"""Configuration values for metrics"""
|
|
6
|
+
__slots__ = ('quota', '_samples', 'event_window', 'time_window_ms', 'tags')
|
|
7
|
+
|
|
8
|
+
def __init__(self, quota=None, samples=2, event_window=sys.maxsize,
|
|
9
|
+
time_window_ms=30 * 1000, tags=None):
|
|
10
|
+
"""
|
|
11
|
+
Arguments:
|
|
12
|
+
quota (Quota, optional): Upper or lower bound of a value.
|
|
13
|
+
samples (int, optional): Max number of samples kept per metric.
|
|
14
|
+
event_window (int, optional): Max number of values per sample.
|
|
15
|
+
time_window_ms (int, optional): Max age of an individual sample.
|
|
16
|
+
tags (dict of {str: str}, optional): Tags for each metric.
|
|
17
|
+
"""
|
|
18
|
+
self.quota = quota
|
|
19
|
+
self._samples = samples
|
|
20
|
+
self.event_window = event_window
|
|
21
|
+
self.time_window_ms = time_window_ms
|
|
22
|
+
# tags should be OrderedDict (not supported in py26)
|
|
23
|
+
self.tags = tags if tags else {}
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def samples(self):
|
|
27
|
+
return self._samples
|
|
28
|
+
|
|
29
|
+
@samples.setter
|
|
30
|
+
def samples(self, value):
|
|
31
|
+
if value < 1:
|
|
32
|
+
raise ValueError('The number of samples must be at least 1.')
|
|
33
|
+
self._samples = value
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MetricName:
|
|
5
|
+
"""
|
|
6
|
+
This class encapsulates a metric's name, logical group and its
|
|
7
|
+
related attributes (tags).
|
|
8
|
+
|
|
9
|
+
group, tags parameters can be used to create unique metric names.
|
|
10
|
+
e.g. domainName:type=group,key1=val1,key2=val2
|
|
11
|
+
|
|
12
|
+
Usage looks something like this:
|
|
13
|
+
|
|
14
|
+
# set up metrics:
|
|
15
|
+
metric_tags = {'client-id': 'producer-1', 'topic': 'topic'}
|
|
16
|
+
metric_config = MetricConfig(tags=metric_tags)
|
|
17
|
+
|
|
18
|
+
# metrics is the global repository of metrics and sensors
|
|
19
|
+
metrics = Metrics(metric_config)
|
|
20
|
+
|
|
21
|
+
sensor = metrics.sensor('message-sizes')
|
|
22
|
+
metric_name = metrics.metric_name('message-size-avg',
|
|
23
|
+
'producer-metrics',
|
|
24
|
+
'average message size')
|
|
25
|
+
sensor.add(metric_name, Avg())
|
|
26
|
+
|
|
27
|
+
metric_name = metrics.metric_name('message-size-max',
|
|
28
|
+
sensor.add(metric_name, Max())
|
|
29
|
+
|
|
30
|
+
tags = {'client-id': 'my-client', 'topic': 'my-topic'}
|
|
31
|
+
metric_name = metrics.metric_name('message-size-min',
|
|
32
|
+
'producer-metrics',
|
|
33
|
+
'message minimum size', tags)
|
|
34
|
+
sensor.add(metric_name, Min())
|
|
35
|
+
|
|
36
|
+
# as messages are sent we record the sizes
|
|
37
|
+
sensor.record(message_size)
|
|
38
|
+
"""
|
|
39
|
+
__slots__ = ('_name', '_group', '_description', '_tags', '_hash')
|
|
40
|
+
|
|
41
|
+
def __init__(self, name, group, description=None, tags=None):
|
|
42
|
+
"""
|
|
43
|
+
Arguments:
|
|
44
|
+
name (str): The name of the metric.
|
|
45
|
+
group (str): The logical group name of the metrics to which this
|
|
46
|
+
metric belongs.
|
|
47
|
+
description (str, optional): A human-readable description to
|
|
48
|
+
include in the metric.
|
|
49
|
+
tags (dict, optional): Additional key/val attributes of the metric.
|
|
50
|
+
"""
|
|
51
|
+
if not (name and group):
|
|
52
|
+
raise ValueError('name and group must be non-empty.')
|
|
53
|
+
if tags is not None and not isinstance(tags, dict):
|
|
54
|
+
raise ValueError('tags must be a dict if present.')
|
|
55
|
+
|
|
56
|
+
self._name = name
|
|
57
|
+
self._group = group
|
|
58
|
+
self._description = description
|
|
59
|
+
self._tags = copy.copy(tags)
|
|
60
|
+
self._hash = 0
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def name(self):
|
|
64
|
+
return self._name
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def group(self):
|
|
68
|
+
return self._group
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def description(self):
|
|
72
|
+
return self._description
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def tags(self):
|
|
76
|
+
return copy.copy(self._tags)
|
|
77
|
+
|
|
78
|
+
def __hash__(self):
|
|
79
|
+
if self._hash != 0:
|
|
80
|
+
return self._hash
|
|
81
|
+
prime = 31
|
|
82
|
+
result = 1
|
|
83
|
+
result = prime * result + hash(self.group)
|
|
84
|
+
result = prime * result + hash(self.name)
|
|
85
|
+
tags_hash = hash(frozenset(self.tags.items())) if self.tags else 0
|
|
86
|
+
result = prime * result + tags_hash
|
|
87
|
+
self._hash = result
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
def __eq__(self, other):
|
|
91
|
+
if self is other:
|
|
92
|
+
return True
|
|
93
|
+
if other is None:
|
|
94
|
+
return False
|
|
95
|
+
return (isinstance(self, type(other)) and
|
|
96
|
+
self.group == other.group and
|
|
97
|
+
self.name == other.name and
|
|
98
|
+
self.tags == other.tags)
|
|
99
|
+
|
|
100
|
+
def __ne__(self, other):
|
|
101
|
+
return not self.__eq__(other)
|
|
102
|
+
|
|
103
|
+
def __str__(self):
|
|
104
|
+
return 'MetricName(name=%s, group=%s, description=%s, tags=%s)' % (
|
|
105
|
+
self.name, self.group, self.description, self.tags)
|