ruby-kafka-aws-iam 1.4.1
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.
- checksums.yaml +7 -0
- data/.circleci/config.yml +393 -0
- data/.github/workflows/stale.yml +19 -0
- data/.gitignore +13 -0
- data/.readygo +1 -0
- data/.rspec +3 -0
- data/.rubocop.yml +44 -0
- data/.ruby-version +1 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +314 -0
- data/Gemfile +5 -0
- data/ISSUE_TEMPLATE.md +23 -0
- data/LICENSE.txt +176 -0
- data/Procfile +2 -0
- data/README.md +1356 -0
- data/Rakefile +8 -0
- data/benchmarks/message_encoding.rb +23 -0
- data/bin/console +8 -0
- data/bin/setup +5 -0
- data/docker-compose.yml +39 -0
- data/examples/consumer-group.rb +35 -0
- data/examples/firehose-consumer.rb +64 -0
- data/examples/firehose-producer.rb +54 -0
- data/examples/simple-consumer.rb +34 -0
- data/examples/simple-producer.rb +42 -0
- data/examples/ssl-producer.rb +44 -0
- data/lib/kafka/async_producer.rb +297 -0
- data/lib/kafka/broker.rb +217 -0
- data/lib/kafka/broker_info.rb +16 -0
- data/lib/kafka/broker_pool.rb +41 -0
- data/lib/kafka/broker_uri.rb +43 -0
- data/lib/kafka/client.rb +838 -0
- data/lib/kafka/cluster.rb +513 -0
- data/lib/kafka/compression.rb +45 -0
- data/lib/kafka/compressor.rb +86 -0
- data/lib/kafka/connection.rb +228 -0
- data/lib/kafka/connection_builder.rb +33 -0
- data/lib/kafka/consumer.rb +642 -0
- data/lib/kafka/consumer_group/assignor.rb +63 -0
- data/lib/kafka/consumer_group.rb +231 -0
- data/lib/kafka/crc32_hash.rb +15 -0
- data/lib/kafka/datadog.rb +420 -0
- data/lib/kafka/digest.rb +22 -0
- data/lib/kafka/fetch_operation.rb +115 -0
- data/lib/kafka/fetched_batch.rb +58 -0
- data/lib/kafka/fetched_batch_generator.rb +120 -0
- data/lib/kafka/fetched_message.rb +48 -0
- data/lib/kafka/fetched_offset_resolver.rb +48 -0
- data/lib/kafka/fetcher.rb +224 -0
- data/lib/kafka/gzip_codec.rb +34 -0
- data/lib/kafka/heartbeat.rb +25 -0
- data/lib/kafka/instrumenter.rb +38 -0
- data/lib/kafka/interceptors.rb +33 -0
- data/lib/kafka/lz4_codec.rb +27 -0
- data/lib/kafka/message_buffer.rb +87 -0
- data/lib/kafka/murmur2_hash.rb +17 -0
- data/lib/kafka/offset_manager.rb +259 -0
- data/lib/kafka/partitioner.rb +40 -0
- data/lib/kafka/pause.rb +92 -0
- data/lib/kafka/pending_message.rb +29 -0
- data/lib/kafka/pending_message_queue.rb +41 -0
- data/lib/kafka/produce_operation.rb +205 -0
- data/lib/kafka/producer.rb +528 -0
- data/lib/kafka/prometheus.rb +316 -0
- data/lib/kafka/protocol/add_offsets_to_txn_request.rb +29 -0
- data/lib/kafka/protocol/add_offsets_to_txn_response.rb +21 -0
- data/lib/kafka/protocol/add_partitions_to_txn_request.rb +34 -0
- data/lib/kafka/protocol/add_partitions_to_txn_response.rb +47 -0
- data/lib/kafka/protocol/alter_configs_request.rb +44 -0
- data/lib/kafka/protocol/alter_configs_response.rb +49 -0
- data/lib/kafka/protocol/api_versions_request.rb +21 -0
- data/lib/kafka/protocol/api_versions_response.rb +53 -0
- data/lib/kafka/protocol/consumer_group_protocol.rb +19 -0
- data/lib/kafka/protocol/create_partitions_request.rb +42 -0
- data/lib/kafka/protocol/create_partitions_response.rb +28 -0
- data/lib/kafka/protocol/create_topics_request.rb +45 -0
- data/lib/kafka/protocol/create_topics_response.rb +26 -0
- data/lib/kafka/protocol/decoder.rb +175 -0
- data/lib/kafka/protocol/delete_topics_request.rb +33 -0
- data/lib/kafka/protocol/delete_topics_response.rb +26 -0
- data/lib/kafka/protocol/describe_configs_request.rb +35 -0
- data/lib/kafka/protocol/describe_configs_response.rb +73 -0
- data/lib/kafka/protocol/describe_groups_request.rb +27 -0
- data/lib/kafka/protocol/describe_groups_response.rb +73 -0
- data/lib/kafka/protocol/encoder.rb +184 -0
- data/lib/kafka/protocol/end_txn_request.rb +29 -0
- data/lib/kafka/protocol/end_txn_response.rb +19 -0
- data/lib/kafka/protocol/fetch_request.rb +70 -0
- data/lib/kafka/protocol/fetch_response.rb +136 -0
- data/lib/kafka/protocol/find_coordinator_request.rb +29 -0
- data/lib/kafka/protocol/find_coordinator_response.rb +29 -0
- data/lib/kafka/protocol/heartbeat_request.rb +27 -0
- data/lib/kafka/protocol/heartbeat_response.rb +17 -0
- data/lib/kafka/protocol/init_producer_id_request.rb +26 -0
- data/lib/kafka/protocol/init_producer_id_response.rb +27 -0
- data/lib/kafka/protocol/join_group_request.rb +47 -0
- data/lib/kafka/protocol/join_group_response.rb +41 -0
- data/lib/kafka/protocol/leave_group_request.rb +25 -0
- data/lib/kafka/protocol/leave_group_response.rb +17 -0
- data/lib/kafka/protocol/list_groups_request.rb +23 -0
- data/lib/kafka/protocol/list_groups_response.rb +35 -0
- data/lib/kafka/protocol/list_offset_request.rb +53 -0
- data/lib/kafka/protocol/list_offset_response.rb +89 -0
- data/lib/kafka/protocol/member_assignment.rb +42 -0
- data/lib/kafka/protocol/message.rb +172 -0
- data/lib/kafka/protocol/message_set.rb +55 -0
- data/lib/kafka/protocol/metadata_request.rb +31 -0
- data/lib/kafka/protocol/metadata_response.rb +185 -0
- data/lib/kafka/protocol/offset_commit_request.rb +47 -0
- data/lib/kafka/protocol/offset_commit_response.rb +29 -0
- data/lib/kafka/protocol/offset_fetch_request.rb +38 -0
- data/lib/kafka/protocol/offset_fetch_response.rb +56 -0
- data/lib/kafka/protocol/produce_request.rb +94 -0
- data/lib/kafka/protocol/produce_response.rb +63 -0
- data/lib/kafka/protocol/record.rb +88 -0
- data/lib/kafka/protocol/record_batch.rb +223 -0
- data/lib/kafka/protocol/request_message.rb +26 -0
- data/lib/kafka/protocol/sasl_handshake_request.rb +33 -0
- data/lib/kafka/protocol/sasl_handshake_response.rb +28 -0
- data/lib/kafka/protocol/sync_group_request.rb +33 -0
- data/lib/kafka/protocol/sync_group_response.rb +26 -0
- data/lib/kafka/protocol/txn_offset_commit_request.rb +46 -0
- data/lib/kafka/protocol/txn_offset_commit_response.rb +47 -0
- data/lib/kafka/protocol.rb +225 -0
- data/lib/kafka/round_robin_assignment_strategy.rb +52 -0
- data/lib/kafka/sasl/awsmskiam.rb +128 -0
- data/lib/kafka/sasl/gssapi.rb +76 -0
- data/lib/kafka/sasl/oauth.rb +64 -0
- data/lib/kafka/sasl/plain.rb +39 -0
- data/lib/kafka/sasl/scram.rb +180 -0
- data/lib/kafka/sasl_authenticator.rb +73 -0
- data/lib/kafka/snappy_codec.rb +29 -0
- data/lib/kafka/socket_with_timeout.rb +96 -0
- data/lib/kafka/ssl_context.rb +66 -0
- data/lib/kafka/ssl_socket_with_timeout.rb +192 -0
- data/lib/kafka/statsd.rb +296 -0
- data/lib/kafka/tagged_logger.rb +77 -0
- data/lib/kafka/transaction_manager.rb +306 -0
- data/lib/kafka/transaction_state_machine.rb +72 -0
- data/lib/kafka/version.rb +5 -0
- data/lib/kafka/zstd_codec.rb +27 -0
- data/lib/kafka.rb +373 -0
- data/lib/ruby-kafka.rb +5 -0
- data/ruby-kafka.gemspec +54 -0
- metadata +520 -0
@@ -0,0 +1,228 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
require "kafka/socket_with_timeout"
|
5
|
+
require "kafka/ssl_socket_with_timeout"
|
6
|
+
require "kafka/protocol/request_message"
|
7
|
+
require "kafka/protocol/encoder"
|
8
|
+
require "kafka/protocol/decoder"
|
9
|
+
|
10
|
+
module Kafka
|
11
|
+
|
12
|
+
# A connection to a single Kafka broker.
|
13
|
+
#
|
14
|
+
# Usually you'll need a separate connection to each broker in a cluster, since most
|
15
|
+
# requests must be directed specifically to the broker that is currently leader for
|
16
|
+
# the set of topic partitions you want to produce to or consume from.
|
17
|
+
#
|
18
|
+
# ## Instrumentation
|
19
|
+
#
|
20
|
+
# Connections emit a `request.connection.kafka` notification on each request. The following
|
21
|
+
# keys will be found in the payload:
|
22
|
+
#
|
23
|
+
# * `:api` — the name of the API being invoked.
|
24
|
+
# * `:request_size` — the number of bytes in the request.
|
25
|
+
# * `:response_size` — the number of bytes in the response.
|
26
|
+
#
|
27
|
+
# The notification also includes the duration of the request.
|
28
|
+
#
|
29
|
+
class Connection
|
30
|
+
SOCKET_TIMEOUT = 10
|
31
|
+
CONNECT_TIMEOUT = 10
|
32
|
+
|
33
|
+
# Time after which an idle connection will be reopened.
|
34
|
+
IDLE_TIMEOUT = 60 * 5
|
35
|
+
|
36
|
+
attr_reader :encoder
|
37
|
+
attr_reader :decoder
|
38
|
+
|
39
|
+
# Opens a connection to a Kafka broker.
|
40
|
+
#
|
41
|
+
# @param host [String] the hostname of the broker.
|
42
|
+
# @param port [Integer] the port of the broker.
|
43
|
+
# @param client_id [String] the client id is a user-specified string sent in each
|
44
|
+
# request to help trace calls and should logically identify the application
|
45
|
+
# making the request.
|
46
|
+
# @param logger [Logger] the logger used to log trace messages.
|
47
|
+
# @param connect_timeout [Integer] the socket timeout for connecting to the broker.
|
48
|
+
# Default is 10 seconds.
|
49
|
+
# @param socket_timeout [Integer] the socket timeout for reading and writing to the
|
50
|
+
# broker. Default is 10 seconds.
|
51
|
+
#
|
52
|
+
# @return [Connection] a new connection.
|
53
|
+
def initialize(host:, port:, client_id:, logger:, instrumenter:, connect_timeout: nil, socket_timeout: nil, ssl_context: nil)
|
54
|
+
@host, @port, @client_id = host, port, client_id
|
55
|
+
@logger = TaggedLogger.new(logger)
|
56
|
+
@instrumenter = instrumenter
|
57
|
+
|
58
|
+
@connect_timeout = connect_timeout || CONNECT_TIMEOUT
|
59
|
+
@socket_timeout = socket_timeout || SOCKET_TIMEOUT
|
60
|
+
@ssl_context = ssl_context
|
61
|
+
|
62
|
+
@socket = nil
|
63
|
+
@last_request = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
"#{@host}:#{@port}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def open?
|
71
|
+
!@socket.nil? && !@socket.closed?
|
72
|
+
end
|
73
|
+
|
74
|
+
def close
|
75
|
+
@logger.debug "Closing socket to #{to_s}"
|
76
|
+
|
77
|
+
@socket.close if @socket
|
78
|
+
end
|
79
|
+
|
80
|
+
# Sends a request over the connection.
|
81
|
+
#
|
82
|
+
# @param request [#encode, #response_class] the request that should be
|
83
|
+
# encoded and written.
|
84
|
+
#
|
85
|
+
# @return [Object] the response.
|
86
|
+
def send_request(request)
|
87
|
+
api_name = Protocol.api_name(request.api_key)
|
88
|
+
|
89
|
+
# Default notification payload.
|
90
|
+
notification = {
|
91
|
+
broker_host: @host,
|
92
|
+
api: api_name,
|
93
|
+
request_size: 0,
|
94
|
+
response_size: 0,
|
95
|
+
}
|
96
|
+
|
97
|
+
raise IdleConnection if idle?
|
98
|
+
|
99
|
+
@logger.push_tags(api_name)
|
100
|
+
@instrumenter.instrument("request.connection", notification) do
|
101
|
+
open unless open?
|
102
|
+
|
103
|
+
@correlation_id += 1
|
104
|
+
|
105
|
+
@logger.debug "Sending #{api_name} API request #{@correlation_id} to #{to_s}"
|
106
|
+
|
107
|
+
write_request(request, notification)
|
108
|
+
|
109
|
+
response_class = request.response_class
|
110
|
+
response = wait_for_response(response_class, notification) unless response_class.nil?
|
111
|
+
|
112
|
+
@last_request = Time.now
|
113
|
+
|
114
|
+
response
|
115
|
+
end
|
116
|
+
rescue SystemCallError, EOFError, IOError => e
|
117
|
+
close
|
118
|
+
|
119
|
+
raise ConnectionError, "Connection error #{e.class}: #{e}"
|
120
|
+
ensure
|
121
|
+
@logger.pop_tags
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def open
|
127
|
+
@logger.debug "Opening connection to #{@host}:#{@port} with client id #{@client_id}..."
|
128
|
+
|
129
|
+
if @ssl_context
|
130
|
+
@socket = SSLSocketWithTimeout.new(@host,
|
131
|
+
@port,
|
132
|
+
connect_timeout: @connect_timeout,
|
133
|
+
timeout: @socket_timeout,
|
134
|
+
ssl_context: @ssl_context,
|
135
|
+
logger: @logger)
|
136
|
+
else
|
137
|
+
@socket = SocketWithTimeout.new(@host, @port, connect_timeout: @connect_timeout, timeout: @socket_timeout)
|
138
|
+
end
|
139
|
+
|
140
|
+
@encoder = Kafka::Protocol::Encoder.new(@socket)
|
141
|
+
@decoder = Kafka::Protocol::Decoder.new(@socket)
|
142
|
+
|
143
|
+
# Correlation id is initialized to zero and bumped for each request.
|
144
|
+
@correlation_id = 0
|
145
|
+
|
146
|
+
@last_request = nil
|
147
|
+
rescue Errno::ETIMEDOUT => e
|
148
|
+
@logger.error "Timed out while trying to connect to #{self}: #{e}"
|
149
|
+
raise ConnectionError, e
|
150
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e
|
151
|
+
@logger.error "Failed to connect to #{self}: #{e}"
|
152
|
+
raise ConnectionError, e
|
153
|
+
end
|
154
|
+
|
155
|
+
def idle?
|
156
|
+
@last_request && @last_request < Time.now - IDLE_TIMEOUT
|
157
|
+
end
|
158
|
+
|
159
|
+
# Writes a request over the connection.
|
160
|
+
#
|
161
|
+
# @param request [#encode] the request that should be encoded and written.
|
162
|
+
#
|
163
|
+
# @return [nil]
|
164
|
+
def write_request(request, notification)
|
165
|
+
message = Kafka::Protocol::RequestMessage.new(
|
166
|
+
api_key: request.api_key,
|
167
|
+
api_version: request.respond_to?(:api_version) ? request.api_version : 0,
|
168
|
+
correlation_id: @correlation_id,
|
169
|
+
client_id: @client_id,
|
170
|
+
request: request,
|
171
|
+
)
|
172
|
+
|
173
|
+
data = Kafka::Protocol::Encoder.encode_with(message)
|
174
|
+
notification[:request_size] = data.bytesize
|
175
|
+
|
176
|
+
@encoder.write_bytes(data)
|
177
|
+
|
178
|
+
nil
|
179
|
+
rescue Errno::ETIMEDOUT
|
180
|
+
@logger.error "Timed out while writing request #{@correlation_id}"
|
181
|
+
raise
|
182
|
+
end
|
183
|
+
|
184
|
+
# Reads a response from the connection.
|
185
|
+
#
|
186
|
+
# @param response_class [#decode] an object that can decode the response from
|
187
|
+
# a given Decoder.
|
188
|
+
#
|
189
|
+
# @return [nil]
|
190
|
+
def read_response(response_class, notification)
|
191
|
+
@logger.debug "Waiting for response #{@correlation_id} from #{to_s}"
|
192
|
+
|
193
|
+
data = @decoder.bytes
|
194
|
+
notification[:response_size] = data.bytesize
|
195
|
+
|
196
|
+
buffer = StringIO.new(data)
|
197
|
+
response_decoder = Kafka::Protocol::Decoder.new(buffer)
|
198
|
+
|
199
|
+
correlation_id = response_decoder.int32
|
200
|
+
response = response_class.decode(response_decoder)
|
201
|
+
|
202
|
+
@logger.debug "Received response #{correlation_id} from #{to_s}"
|
203
|
+
|
204
|
+
return correlation_id, response
|
205
|
+
rescue Errno::ETIMEDOUT
|
206
|
+
@logger.error "Timed out while waiting for response #{@correlation_id}"
|
207
|
+
raise
|
208
|
+
end
|
209
|
+
|
210
|
+
def wait_for_response(response_class, notification)
|
211
|
+
loop do
|
212
|
+
correlation_id, response = read_response(response_class, notification)
|
213
|
+
|
214
|
+
# There may have been a previous request that timed out before the client
|
215
|
+
# was able to read the response. In that case, the response will still be
|
216
|
+
# sitting in the socket waiting to be read. If the response we just read
|
217
|
+
# was to a previous request, we can safely skip it.
|
218
|
+
if correlation_id < @correlation_id
|
219
|
+
@logger.error "Received out-of-order response id #{correlation_id}, was expecting #{@correlation_id}"
|
220
|
+
elsif correlation_id > @correlation_id
|
221
|
+
raise Kafka::Error, "Correlation id mismatch: expected #{@correlation_id} but got #{correlation_id}"
|
222
|
+
else
|
223
|
+
return response
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kafka
|
4
|
+
class ConnectionBuilder
|
5
|
+
def initialize(client_id:, logger:, instrumenter:, connect_timeout:, socket_timeout:, ssl_context:, sasl_authenticator:)
|
6
|
+
@client_id = client_id
|
7
|
+
@logger = TaggedLogger.new(logger)
|
8
|
+
@instrumenter = instrumenter
|
9
|
+
@connect_timeout = connect_timeout
|
10
|
+
@socket_timeout = socket_timeout
|
11
|
+
@ssl_context = ssl_context
|
12
|
+
@sasl_authenticator = sasl_authenticator
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_connection(host, port)
|
16
|
+
connection = Connection.new(
|
17
|
+
host: host,
|
18
|
+
port: port,
|
19
|
+
client_id: @client_id,
|
20
|
+
connect_timeout: @connect_timeout,
|
21
|
+
socket_timeout: @socket_timeout,
|
22
|
+
logger: @logger,
|
23
|
+
instrumenter: @instrumenter,
|
24
|
+
ssl_context: @ssl_context,
|
25
|
+
)
|
26
|
+
|
27
|
+
@sasl_authenticator.authenticate!(connection)
|
28
|
+
|
29
|
+
connection
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|