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
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
# See:
|
|
2
|
+
# https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/\
|
|
3
|
+
# apache/kafka/common/record/LegacyRecord.java
|
|
4
|
+
|
|
5
|
+
# Builder and reader implementation for V0 and V1 record versions. As of Kafka
|
|
6
|
+
# 0.11.0.0 those were replaced with V2, thus the Legacy naming.
|
|
7
|
+
|
|
8
|
+
# The schema is given below (see
|
|
9
|
+
# https://kafka.apache.org/protocol#protocol_message_sets for more details):
|
|
10
|
+
|
|
11
|
+
# MessageSet => [Offset MessageSize Message]
|
|
12
|
+
# Offset => int64
|
|
13
|
+
# MessageSize => int32
|
|
14
|
+
|
|
15
|
+
# v0
|
|
16
|
+
# Message => Crc MagicByte Attributes Key Value
|
|
17
|
+
# Crc => int32
|
|
18
|
+
# MagicByte => int8
|
|
19
|
+
# Attributes => int8
|
|
20
|
+
# Key => bytes
|
|
21
|
+
# Value => bytes
|
|
22
|
+
|
|
23
|
+
# v1 (supported since 0.10.0)
|
|
24
|
+
# Message => Crc MagicByte Attributes Key Value
|
|
25
|
+
# Crc => int32
|
|
26
|
+
# MagicByte => int8
|
|
27
|
+
# Attributes => int8
|
|
28
|
+
# Timestamp => int64
|
|
29
|
+
# Key => bytes
|
|
30
|
+
# Value => bytes
|
|
31
|
+
|
|
32
|
+
# The message attribute bits are given below:
|
|
33
|
+
# * Unused (4-7)
|
|
34
|
+
# * Timestamp Type (3) (added in V1)
|
|
35
|
+
# * Compression Type (0-2)
|
|
36
|
+
|
|
37
|
+
# Note that when compression is enabled (see attributes above), the whole
|
|
38
|
+
# array of MessageSet's is compressed and places into a message as the `value`.
|
|
39
|
+
# Only the parent message is marked with `compression` bits in attributes.
|
|
40
|
+
|
|
41
|
+
# The CRC covers the data from the Magic byte to the end of the message.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
import struct
|
|
45
|
+
import time
|
|
46
|
+
|
|
47
|
+
from kafka.record.abc import ABCRecord, ABCRecordBatch, ABCRecordBatchBuilder
|
|
48
|
+
from kafka.record.util import calc_crc32
|
|
49
|
+
|
|
50
|
+
from kafka.codec import (
|
|
51
|
+
gzip_encode, snappy_encode, lz4_encode, lz4_encode_old_kafka,
|
|
52
|
+
gzip_decode, snappy_decode, lz4_decode, lz4_decode_old_kafka,
|
|
53
|
+
)
|
|
54
|
+
import kafka.codec as codecs
|
|
55
|
+
from kafka.errors import CorruptRecordError, UnsupportedCodecError
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class LegacyRecordBase:
|
|
59
|
+
|
|
60
|
+
__slots__ = ()
|
|
61
|
+
|
|
62
|
+
HEADER_STRUCT_V0 = struct.Struct(
|
|
63
|
+
">q" # BaseOffset => Int64
|
|
64
|
+
"i" # Length => Int32
|
|
65
|
+
"I" # CRC => Int32
|
|
66
|
+
"b" # Magic => Int8
|
|
67
|
+
"b" # Attributes => Int8
|
|
68
|
+
)
|
|
69
|
+
HEADER_STRUCT_V1 = struct.Struct(
|
|
70
|
+
">q" # BaseOffset => Int64
|
|
71
|
+
"i" # Length => Int32
|
|
72
|
+
"I" # CRC => Int32
|
|
73
|
+
"b" # Magic => Int8
|
|
74
|
+
"b" # Attributes => Int8
|
|
75
|
+
"q" # timestamp => Int64
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
LOG_OVERHEAD = CRC_OFFSET = struct.calcsize(
|
|
79
|
+
">q" # Offset
|
|
80
|
+
"i" # Size
|
|
81
|
+
)
|
|
82
|
+
MAGIC_OFFSET = LOG_OVERHEAD + struct.calcsize(
|
|
83
|
+
">I" # CRC
|
|
84
|
+
)
|
|
85
|
+
# Those are used for fast size calculations
|
|
86
|
+
RECORD_OVERHEAD_V0 = struct.calcsize(
|
|
87
|
+
">I" # CRC
|
|
88
|
+
"b" # magic
|
|
89
|
+
"b" # attributes
|
|
90
|
+
"i" # Key length
|
|
91
|
+
"i" # Value length
|
|
92
|
+
)
|
|
93
|
+
RECORD_OVERHEAD_V1 = struct.calcsize(
|
|
94
|
+
">I" # CRC
|
|
95
|
+
"b" # magic
|
|
96
|
+
"b" # attributes
|
|
97
|
+
"q" # timestamp
|
|
98
|
+
"i" # Key length
|
|
99
|
+
"i" # Value length
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
KEY_OFFSET_V0 = HEADER_STRUCT_V0.size
|
|
103
|
+
KEY_OFFSET_V1 = HEADER_STRUCT_V1.size
|
|
104
|
+
KEY_LENGTH = VALUE_LENGTH = struct.calcsize(">i") # Bytes length is Int32
|
|
105
|
+
|
|
106
|
+
CODEC_MASK = 0x07
|
|
107
|
+
CODEC_NONE = 0x00
|
|
108
|
+
CODEC_GZIP = 0x01
|
|
109
|
+
CODEC_SNAPPY = 0x02
|
|
110
|
+
CODEC_LZ4 = 0x03
|
|
111
|
+
TIMESTAMP_TYPE_MASK = 0x08
|
|
112
|
+
|
|
113
|
+
LOG_APPEND_TIME = 1
|
|
114
|
+
CREATE_TIME = 0
|
|
115
|
+
|
|
116
|
+
NO_TIMESTAMP = -1
|
|
117
|
+
|
|
118
|
+
def _assert_has_codec(self, compression_type):
|
|
119
|
+
if compression_type == self.CODEC_GZIP:
|
|
120
|
+
checker, name = codecs.has_gzip, "gzip"
|
|
121
|
+
elif compression_type == self.CODEC_SNAPPY:
|
|
122
|
+
checker, name = codecs.has_snappy, "snappy"
|
|
123
|
+
elif compression_type == self.CODEC_LZ4:
|
|
124
|
+
checker, name = codecs.has_lz4, "lz4"
|
|
125
|
+
else:
|
|
126
|
+
raise UnsupportedCodecError(
|
|
127
|
+
"Unrecognized compression type")
|
|
128
|
+
if not checker():
|
|
129
|
+
raise UnsupportedCodecError(
|
|
130
|
+
"Libraries for {} compression codec not found".format(name))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class LegacyRecordBatch(ABCRecordBatch, LegacyRecordBase):
|
|
134
|
+
|
|
135
|
+
__slots__ = ("_buffer", "_magic", "_offset", "_length", "_crc", "_timestamp",
|
|
136
|
+
"_attributes", "_decompressed")
|
|
137
|
+
|
|
138
|
+
def __init__(self, buffer, magic):
|
|
139
|
+
self._buffer = memoryview(buffer)
|
|
140
|
+
self._magic = magic
|
|
141
|
+
|
|
142
|
+
offset, length, crc, magic_, attrs, timestamp = self._read_header(0)
|
|
143
|
+
assert length == len(buffer) - self.LOG_OVERHEAD
|
|
144
|
+
assert magic == magic_
|
|
145
|
+
|
|
146
|
+
self._offset = offset
|
|
147
|
+
self._length = length
|
|
148
|
+
self._crc = crc
|
|
149
|
+
self._timestamp = timestamp
|
|
150
|
+
self._attributes = attrs
|
|
151
|
+
self._decompressed = False
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def base_offset(self):
|
|
155
|
+
return self._offset
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def size_in_bytes(self):
|
|
159
|
+
return self._length + self.LOG_OVERHEAD
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def timestamp_type(self):
|
|
163
|
+
"""0 for CreateTime; 1 for LogAppendTime; None if unsupported.
|
|
164
|
+
|
|
165
|
+
Value is determined by broker; produced messages should always set to 0
|
|
166
|
+
Requires Kafka >= 0.10 / message version >= 1
|
|
167
|
+
"""
|
|
168
|
+
if self._magic == 0:
|
|
169
|
+
return None
|
|
170
|
+
elif self._attributes & self.TIMESTAMP_TYPE_MASK:
|
|
171
|
+
return 1
|
|
172
|
+
else:
|
|
173
|
+
return 0
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def compression_type(self):
|
|
177
|
+
return self._attributes & self.CODEC_MASK
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def magic(self):
|
|
181
|
+
return self._magic
|
|
182
|
+
|
|
183
|
+
def validate_crc(self):
|
|
184
|
+
# memoryview avoids a full-body copy when slicing the bytearray.
|
|
185
|
+
crc = calc_crc32(memoryview(self._buffer)[self.MAGIC_OFFSET:])
|
|
186
|
+
return self._crc == crc
|
|
187
|
+
|
|
188
|
+
def _decompress(self, key_offset):
|
|
189
|
+
# Copy of `_read_key_value`, but uses memoryview
|
|
190
|
+
pos = key_offset
|
|
191
|
+
key_size = struct.unpack_from(">i", self._buffer, pos)[0]
|
|
192
|
+
pos += self.KEY_LENGTH
|
|
193
|
+
if key_size != -1:
|
|
194
|
+
pos += key_size
|
|
195
|
+
value_size = struct.unpack_from(">i", self._buffer, pos)[0]
|
|
196
|
+
pos += self.VALUE_LENGTH
|
|
197
|
+
if value_size == -1:
|
|
198
|
+
raise CorruptRecordError("Value of compressed message is None")
|
|
199
|
+
else:
|
|
200
|
+
data = self._buffer[pos:pos + value_size]
|
|
201
|
+
|
|
202
|
+
compression_type = self.compression_type
|
|
203
|
+
self._assert_has_codec(compression_type)
|
|
204
|
+
if compression_type == self.CODEC_GZIP:
|
|
205
|
+
uncompressed = gzip_decode(data)
|
|
206
|
+
elif compression_type == self.CODEC_SNAPPY:
|
|
207
|
+
uncompressed = snappy_decode(data.tobytes())
|
|
208
|
+
elif compression_type == self.CODEC_LZ4:
|
|
209
|
+
if self._magic == 0:
|
|
210
|
+
uncompressed = lz4_decode_old_kafka(data.tobytes())
|
|
211
|
+
else:
|
|
212
|
+
uncompressed = lz4_decode(data.tobytes())
|
|
213
|
+
return uncompressed # pylint: disable=E0606
|
|
214
|
+
|
|
215
|
+
def _read_header(self, pos):
|
|
216
|
+
if self._magic == 0:
|
|
217
|
+
offset, length, crc, magic_read, attrs = \
|
|
218
|
+
self.HEADER_STRUCT_V0.unpack_from(self._buffer, pos)
|
|
219
|
+
timestamp = None
|
|
220
|
+
else:
|
|
221
|
+
offset, length, crc, magic_read, attrs, timestamp = \
|
|
222
|
+
self.HEADER_STRUCT_V1.unpack_from(self._buffer, pos)
|
|
223
|
+
return offset, length, crc, magic_read, attrs, timestamp
|
|
224
|
+
|
|
225
|
+
def _read_all_headers(self):
|
|
226
|
+
pos = 0
|
|
227
|
+
msgs = []
|
|
228
|
+
buffer_len = len(self._buffer)
|
|
229
|
+
while pos < buffer_len:
|
|
230
|
+
header = self._read_header(pos)
|
|
231
|
+
msgs.append((header, pos))
|
|
232
|
+
pos += self.LOG_OVERHEAD + header[1] # length
|
|
233
|
+
return msgs
|
|
234
|
+
|
|
235
|
+
def _read_key_value(self, pos):
|
|
236
|
+
key_size = struct.unpack_from(">i", self._buffer, pos)[0]
|
|
237
|
+
pos += self.KEY_LENGTH
|
|
238
|
+
if key_size == -1:
|
|
239
|
+
key = None
|
|
240
|
+
else:
|
|
241
|
+
key = self._buffer[pos:pos + key_size].tobytes()
|
|
242
|
+
pos += key_size
|
|
243
|
+
|
|
244
|
+
value_size = struct.unpack_from(">i", self._buffer, pos)[0]
|
|
245
|
+
pos += self.VALUE_LENGTH
|
|
246
|
+
if value_size == -1:
|
|
247
|
+
value = None
|
|
248
|
+
else:
|
|
249
|
+
value = self._buffer[pos:pos + value_size].tobytes()
|
|
250
|
+
return key, value
|
|
251
|
+
|
|
252
|
+
def _crc_bytes(self, msg_pos, length):
|
|
253
|
+
# memoryview avoids copying the message bytes out of the batch buffer
|
|
254
|
+
# just to hand them to calc_crc32 later in LegacyRecord.validate_crc.
|
|
255
|
+
return memoryview(self._buffer)[
|
|
256
|
+
msg_pos + self.MAGIC_OFFSET:msg_pos + self.LOG_OVERHEAD + length]
|
|
257
|
+
|
|
258
|
+
def __iter__(self):
|
|
259
|
+
if self._magic == 1:
|
|
260
|
+
key_offset = self.KEY_OFFSET_V1
|
|
261
|
+
else:
|
|
262
|
+
key_offset = self.KEY_OFFSET_V0
|
|
263
|
+
timestamp_type = self.timestamp_type
|
|
264
|
+
|
|
265
|
+
if self.compression_type:
|
|
266
|
+
# In case we will call iter again
|
|
267
|
+
if not self._decompressed:
|
|
268
|
+
self._buffer = memoryview(self._decompress(key_offset))
|
|
269
|
+
self._decompressed = True
|
|
270
|
+
|
|
271
|
+
# If relative offset is used, we need to decompress the entire
|
|
272
|
+
# message first to compute the absolute offset.
|
|
273
|
+
headers = self._read_all_headers()
|
|
274
|
+
if self._magic > 0:
|
|
275
|
+
msg_header, _ = headers[-1]
|
|
276
|
+
absolute_base_offset = self._offset - msg_header[0]
|
|
277
|
+
else:
|
|
278
|
+
absolute_base_offset = -1
|
|
279
|
+
|
|
280
|
+
for header, msg_pos in headers:
|
|
281
|
+
offset, length, crc, _, attrs, timestamp = header
|
|
282
|
+
# There should only ever be a single layer of compression
|
|
283
|
+
assert not attrs & self.CODEC_MASK, (
|
|
284
|
+
'MessageSet at offset %d appears double-compressed. This '
|
|
285
|
+
'should not happen -- check your producers!' % (offset,))
|
|
286
|
+
|
|
287
|
+
# When magic value is greater than 0, the timestamp
|
|
288
|
+
# of a compressed message depends on the
|
|
289
|
+
# timestamp type of the wrapper message:
|
|
290
|
+
if timestamp_type == self.LOG_APPEND_TIME:
|
|
291
|
+
timestamp = self._timestamp
|
|
292
|
+
|
|
293
|
+
if absolute_base_offset >= 0:
|
|
294
|
+
offset += absolute_base_offset
|
|
295
|
+
|
|
296
|
+
key, value = self._read_key_value(msg_pos + key_offset)
|
|
297
|
+
crc_bytes = self._crc_bytes(msg_pos, length)
|
|
298
|
+
yield LegacyRecord(
|
|
299
|
+
self._magic, offset, timestamp, timestamp_type,
|
|
300
|
+
key, value, crc, crc_bytes)
|
|
301
|
+
else:
|
|
302
|
+
key, value = self._read_key_value(key_offset)
|
|
303
|
+
crc_bytes = self._crc_bytes(0, len(self._buffer) - self.LOG_OVERHEAD)
|
|
304
|
+
yield LegacyRecord(
|
|
305
|
+
self._magic, self._offset, self._timestamp, timestamp_type,
|
|
306
|
+
key, value, self._crc, crc_bytes)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class LegacyRecord(ABCRecord):
|
|
310
|
+
|
|
311
|
+
__slots__ = ("_magic", "_offset", "_timestamp", "_timestamp_type", "_key", "_value",
|
|
312
|
+
"_crc", "_crc_bytes")
|
|
313
|
+
|
|
314
|
+
def __init__(self, magic, offset, timestamp, timestamp_type, key, value, crc, crc_bytes):
|
|
315
|
+
self._magic = magic
|
|
316
|
+
self._offset = offset
|
|
317
|
+
self._timestamp = timestamp
|
|
318
|
+
self._timestamp_type = timestamp_type
|
|
319
|
+
self._key = key
|
|
320
|
+
self._value = value
|
|
321
|
+
self._crc = crc
|
|
322
|
+
self._crc_bytes = crc_bytes
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def magic(self):
|
|
326
|
+
return self._magic
|
|
327
|
+
|
|
328
|
+
@property
|
|
329
|
+
def offset(self):
|
|
330
|
+
return self._offset
|
|
331
|
+
|
|
332
|
+
@property
|
|
333
|
+
def timestamp(self):
|
|
334
|
+
""" Epoch milliseconds
|
|
335
|
+
"""
|
|
336
|
+
return self._timestamp
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def timestamp_type(self):
|
|
340
|
+
""" CREATE_TIME(0) or APPEND_TIME(1)
|
|
341
|
+
"""
|
|
342
|
+
return self._timestamp_type
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def key(self):
|
|
346
|
+
""" Bytes key or None
|
|
347
|
+
"""
|
|
348
|
+
return self._key
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def value(self):
|
|
352
|
+
""" Bytes value or None
|
|
353
|
+
"""
|
|
354
|
+
return self._value
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def headers(self):
|
|
358
|
+
return []
|
|
359
|
+
|
|
360
|
+
@property
|
|
361
|
+
def checksum(self):
|
|
362
|
+
return self._crc
|
|
363
|
+
|
|
364
|
+
def validate_crc(self):
|
|
365
|
+
crc = calc_crc32(self._crc_bytes)
|
|
366
|
+
return self._crc == crc
|
|
367
|
+
|
|
368
|
+
@property
|
|
369
|
+
def size_in_bytes(self):
|
|
370
|
+
return LegacyRecordBatchBuilder.estimate_size_in_bytes(self._magic, None, self._key, self._value)
|
|
371
|
+
|
|
372
|
+
def __repr__(self):
|
|
373
|
+
return (
|
|
374
|
+
"LegacyRecord(magic={!r} offset={!r}, timestamp={!r}, timestamp_type={!r},"
|
|
375
|
+
" key={!r}, value={!r}, crc={!r})".format(
|
|
376
|
+
self._magic, self._offset, self._timestamp, self._timestamp_type,
|
|
377
|
+
self._key, self._value, self._crc)
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class LegacyRecordBatchBuilder(ABCRecordBatchBuilder, LegacyRecordBase):
|
|
382
|
+
|
|
383
|
+
__slots__ = ("_magic", "_compression_type", "_batch_size", "_buffer")
|
|
384
|
+
|
|
385
|
+
def __init__(self, magic, compression_type, batch_size):
|
|
386
|
+
self._magic = magic
|
|
387
|
+
self._compression_type = compression_type
|
|
388
|
+
self._batch_size = batch_size
|
|
389
|
+
self._buffer = bytearray()
|
|
390
|
+
|
|
391
|
+
def append(self, offset, timestamp, key, value, headers=None):
|
|
392
|
+
""" Append message to batch.
|
|
393
|
+
"""
|
|
394
|
+
assert not headers, "Headers not supported in v0/v1"
|
|
395
|
+
# Check types
|
|
396
|
+
if type(offset) != int:
|
|
397
|
+
raise TypeError(offset)
|
|
398
|
+
if self._magic == 0:
|
|
399
|
+
timestamp = self.NO_TIMESTAMP
|
|
400
|
+
elif timestamp is None:
|
|
401
|
+
timestamp = int(time.time() * 1000)
|
|
402
|
+
elif type(timestamp) != int:
|
|
403
|
+
raise TypeError(
|
|
404
|
+
"`timestamp` should be int, but {} provided".format(
|
|
405
|
+
type(timestamp)))
|
|
406
|
+
if not (key is None or
|
|
407
|
+
isinstance(key, (bytes, bytearray, memoryview))):
|
|
408
|
+
raise TypeError(
|
|
409
|
+
"Not supported type for key: {}".format(type(key)))
|
|
410
|
+
if not (value is None or
|
|
411
|
+
isinstance(value, (bytes, bytearray, memoryview))):
|
|
412
|
+
raise TypeError(
|
|
413
|
+
"Not supported type for value: {}".format(type(value)))
|
|
414
|
+
|
|
415
|
+
# Check if we have room for another message
|
|
416
|
+
pos = len(self._buffer)
|
|
417
|
+
size = self.size_in_bytes(offset, timestamp, key, value)
|
|
418
|
+
# We always allow at least one record to be appended
|
|
419
|
+
if offset != 0 and pos + size >= self._batch_size:
|
|
420
|
+
return None
|
|
421
|
+
|
|
422
|
+
# Allocate proper buffer length
|
|
423
|
+
self._buffer.extend(bytearray(size))
|
|
424
|
+
|
|
425
|
+
# Encode message
|
|
426
|
+
crc = self._encode_msg(pos, offset, timestamp, key, value)
|
|
427
|
+
|
|
428
|
+
return LegacyRecordMetadata(offset, crc, size, timestamp)
|
|
429
|
+
|
|
430
|
+
def _encode_msg(self, start_pos, offset, timestamp, key, value,
|
|
431
|
+
attributes=0):
|
|
432
|
+
""" Encode msg data into the `msg_buffer`, which should be allocated
|
|
433
|
+
to at least the size of this message.
|
|
434
|
+
"""
|
|
435
|
+
magic = self._magic
|
|
436
|
+
buf = self._buffer
|
|
437
|
+
pos = start_pos
|
|
438
|
+
|
|
439
|
+
# Write key and value
|
|
440
|
+
pos += self.KEY_OFFSET_V0 if magic == 0 else self.KEY_OFFSET_V1
|
|
441
|
+
|
|
442
|
+
if key is None:
|
|
443
|
+
struct.pack_into(">i", buf, pos, -1)
|
|
444
|
+
pos += self.KEY_LENGTH
|
|
445
|
+
else:
|
|
446
|
+
key_size = len(key)
|
|
447
|
+
struct.pack_into(">i", buf, pos, key_size)
|
|
448
|
+
pos += self.KEY_LENGTH
|
|
449
|
+
buf[pos: pos + key_size] = key
|
|
450
|
+
pos += key_size
|
|
451
|
+
|
|
452
|
+
if value is None:
|
|
453
|
+
struct.pack_into(">i", buf, pos, -1)
|
|
454
|
+
pos += self.VALUE_LENGTH
|
|
455
|
+
else:
|
|
456
|
+
value_size = len(value)
|
|
457
|
+
struct.pack_into(">i", buf, pos, value_size)
|
|
458
|
+
pos += self.VALUE_LENGTH
|
|
459
|
+
buf[pos: pos + value_size] = value
|
|
460
|
+
pos += value_size
|
|
461
|
+
length = (pos - start_pos) - self.LOG_OVERHEAD
|
|
462
|
+
|
|
463
|
+
# Write msg header. Note, that Crc will be updated later
|
|
464
|
+
if magic == 0:
|
|
465
|
+
self.HEADER_STRUCT_V0.pack_into(
|
|
466
|
+
buf, start_pos,
|
|
467
|
+
offset, length, 0, magic, attributes)
|
|
468
|
+
else:
|
|
469
|
+
self.HEADER_STRUCT_V1.pack_into(
|
|
470
|
+
buf, start_pos,
|
|
471
|
+
offset, length, 0, magic, attributes, timestamp)
|
|
472
|
+
|
|
473
|
+
# Calculate CRC for msg
|
|
474
|
+
crc_data = memoryview(buf)[start_pos + self.MAGIC_OFFSET:]
|
|
475
|
+
crc = calc_crc32(crc_data)
|
|
476
|
+
struct.pack_into(">I", buf, start_pos + self.CRC_OFFSET, crc)
|
|
477
|
+
return crc
|
|
478
|
+
|
|
479
|
+
def _maybe_compress(self):
|
|
480
|
+
if self._compression_type:
|
|
481
|
+
self._assert_has_codec(self._compression_type)
|
|
482
|
+
data = bytes(self._buffer)
|
|
483
|
+
if self._compression_type == self.CODEC_GZIP:
|
|
484
|
+
compressed = gzip_encode(data)
|
|
485
|
+
elif self._compression_type == self.CODEC_SNAPPY:
|
|
486
|
+
compressed = snappy_encode(data)
|
|
487
|
+
elif self._compression_type == self.CODEC_LZ4:
|
|
488
|
+
if self._magic == 0:
|
|
489
|
+
compressed = lz4_encode_old_kafka(data)
|
|
490
|
+
else:
|
|
491
|
+
compressed = lz4_encode(data)
|
|
492
|
+
size = self.size_in_bytes(
|
|
493
|
+
0, timestamp=0, key=None, value=compressed) # pylint: disable=E0606
|
|
494
|
+
# We will try to reuse the same buffer if we have enough space
|
|
495
|
+
if size > len(self._buffer):
|
|
496
|
+
self._buffer = bytearray(size)
|
|
497
|
+
else:
|
|
498
|
+
del self._buffer[size:]
|
|
499
|
+
self._encode_msg(
|
|
500
|
+
start_pos=0,
|
|
501
|
+
offset=0, timestamp=0, key=None, value=compressed,
|
|
502
|
+
attributes=self._compression_type)
|
|
503
|
+
return True
|
|
504
|
+
return False
|
|
505
|
+
|
|
506
|
+
def build(self):
|
|
507
|
+
"""Compress batch to be ready for send"""
|
|
508
|
+
self._maybe_compress()
|
|
509
|
+
return self._buffer
|
|
510
|
+
|
|
511
|
+
def size(self):
|
|
512
|
+
""" Return current size of data written to buffer
|
|
513
|
+
"""
|
|
514
|
+
return len(self._buffer)
|
|
515
|
+
|
|
516
|
+
# Size calculations. Just copied Java's implementation
|
|
517
|
+
|
|
518
|
+
def size_in_bytes(self, offset, timestamp, key, value, headers=None):
|
|
519
|
+
""" Actual size of message to add
|
|
520
|
+
"""
|
|
521
|
+
assert not headers, "Headers not supported in v0/v1"
|
|
522
|
+
magic = self._magic
|
|
523
|
+
return self.LOG_OVERHEAD + self.record_size(magic, key, value)
|
|
524
|
+
|
|
525
|
+
@classmethod
|
|
526
|
+
def record_size(cls, magic, key, value):
|
|
527
|
+
message_size = cls.record_overhead(magic)
|
|
528
|
+
if key is not None:
|
|
529
|
+
message_size += len(key)
|
|
530
|
+
if value is not None:
|
|
531
|
+
message_size += len(value)
|
|
532
|
+
return message_size
|
|
533
|
+
|
|
534
|
+
@classmethod
|
|
535
|
+
def record_overhead(cls, magic):
|
|
536
|
+
assert magic in [0, 1], "Not supported magic"
|
|
537
|
+
if magic == 0:
|
|
538
|
+
return cls.RECORD_OVERHEAD_V0
|
|
539
|
+
else:
|
|
540
|
+
return cls.RECORD_OVERHEAD_V1
|
|
541
|
+
|
|
542
|
+
@classmethod
|
|
543
|
+
def estimate_size_in_bytes(cls, magic, compression_type, key, value):
|
|
544
|
+
""" Upper bound estimate of record size.
|
|
545
|
+
"""
|
|
546
|
+
assert magic in [0, 1], "Not supported magic"
|
|
547
|
+
# In case of compression we may need another overhead for inner msg
|
|
548
|
+
if compression_type:
|
|
549
|
+
return (
|
|
550
|
+
cls.LOG_OVERHEAD + cls.record_overhead(magic) +
|
|
551
|
+
cls.record_size(magic, key, value)
|
|
552
|
+
)
|
|
553
|
+
return cls.LOG_OVERHEAD + cls.record_size(magic, key, value)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class LegacyRecordMetadata:
|
|
557
|
+
|
|
558
|
+
__slots__ = ("_crc", "_size", "_timestamp", "_offset")
|
|
559
|
+
|
|
560
|
+
def __init__(self, offset, crc, size, timestamp):
|
|
561
|
+
self._offset = offset
|
|
562
|
+
self._crc = crc
|
|
563
|
+
self._size = size
|
|
564
|
+
self._timestamp = timestamp
|
|
565
|
+
|
|
566
|
+
@property
|
|
567
|
+
def offset(self):
|
|
568
|
+
return self._offset
|
|
569
|
+
|
|
570
|
+
@property
|
|
571
|
+
def crc(self):
|
|
572
|
+
return self._crc
|
|
573
|
+
|
|
574
|
+
@property
|
|
575
|
+
def size(self):
|
|
576
|
+
return self._size
|
|
577
|
+
|
|
578
|
+
@property
|
|
579
|
+
def timestamp(self):
|
|
580
|
+
return self._timestamp
|
|
581
|
+
|
|
582
|
+
def __repr__(self):
|
|
583
|
+
return (
|
|
584
|
+
"LegacyRecordMetadata(offset={!r}, crc={!r}, size={!r},"
|
|
585
|
+
" timestamp={!r})".format(
|
|
586
|
+
self._offset, self._crc, self._size, self._timestamp)
|
|
587
|
+
)
|