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.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +393 -0
  3. data/.github/workflows/stale.yml +19 -0
  4. data/.gitignore +13 -0
  5. data/.readygo +1 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +44 -0
  8. data/.ruby-version +1 -0
  9. data/.yardopts +3 -0
  10. data/CHANGELOG.md +314 -0
  11. data/Gemfile +5 -0
  12. data/ISSUE_TEMPLATE.md +23 -0
  13. data/LICENSE.txt +176 -0
  14. data/Procfile +2 -0
  15. data/README.md +1356 -0
  16. data/Rakefile +8 -0
  17. data/benchmarks/message_encoding.rb +23 -0
  18. data/bin/console +8 -0
  19. data/bin/setup +5 -0
  20. data/docker-compose.yml +39 -0
  21. data/examples/consumer-group.rb +35 -0
  22. data/examples/firehose-consumer.rb +64 -0
  23. data/examples/firehose-producer.rb +54 -0
  24. data/examples/simple-consumer.rb +34 -0
  25. data/examples/simple-producer.rb +42 -0
  26. data/examples/ssl-producer.rb +44 -0
  27. data/lib/kafka/async_producer.rb +297 -0
  28. data/lib/kafka/broker.rb +217 -0
  29. data/lib/kafka/broker_info.rb +16 -0
  30. data/lib/kafka/broker_pool.rb +41 -0
  31. data/lib/kafka/broker_uri.rb +43 -0
  32. data/lib/kafka/client.rb +838 -0
  33. data/lib/kafka/cluster.rb +513 -0
  34. data/lib/kafka/compression.rb +45 -0
  35. data/lib/kafka/compressor.rb +86 -0
  36. data/lib/kafka/connection.rb +228 -0
  37. data/lib/kafka/connection_builder.rb +33 -0
  38. data/lib/kafka/consumer.rb +642 -0
  39. data/lib/kafka/consumer_group/assignor.rb +63 -0
  40. data/lib/kafka/consumer_group.rb +231 -0
  41. data/lib/kafka/crc32_hash.rb +15 -0
  42. data/lib/kafka/datadog.rb +420 -0
  43. data/lib/kafka/digest.rb +22 -0
  44. data/lib/kafka/fetch_operation.rb +115 -0
  45. data/lib/kafka/fetched_batch.rb +58 -0
  46. data/lib/kafka/fetched_batch_generator.rb +120 -0
  47. data/lib/kafka/fetched_message.rb +48 -0
  48. data/lib/kafka/fetched_offset_resolver.rb +48 -0
  49. data/lib/kafka/fetcher.rb +224 -0
  50. data/lib/kafka/gzip_codec.rb +34 -0
  51. data/lib/kafka/heartbeat.rb +25 -0
  52. data/lib/kafka/instrumenter.rb +38 -0
  53. data/lib/kafka/interceptors.rb +33 -0
  54. data/lib/kafka/lz4_codec.rb +27 -0
  55. data/lib/kafka/message_buffer.rb +87 -0
  56. data/lib/kafka/murmur2_hash.rb +17 -0
  57. data/lib/kafka/offset_manager.rb +259 -0
  58. data/lib/kafka/partitioner.rb +40 -0
  59. data/lib/kafka/pause.rb +92 -0
  60. data/lib/kafka/pending_message.rb +29 -0
  61. data/lib/kafka/pending_message_queue.rb +41 -0
  62. data/lib/kafka/produce_operation.rb +205 -0
  63. data/lib/kafka/producer.rb +528 -0
  64. data/lib/kafka/prometheus.rb +316 -0
  65. data/lib/kafka/protocol/add_offsets_to_txn_request.rb +29 -0
  66. data/lib/kafka/protocol/add_offsets_to_txn_response.rb +21 -0
  67. data/lib/kafka/protocol/add_partitions_to_txn_request.rb +34 -0
  68. data/lib/kafka/protocol/add_partitions_to_txn_response.rb +47 -0
  69. data/lib/kafka/protocol/alter_configs_request.rb +44 -0
  70. data/lib/kafka/protocol/alter_configs_response.rb +49 -0
  71. data/lib/kafka/protocol/api_versions_request.rb +21 -0
  72. data/lib/kafka/protocol/api_versions_response.rb +53 -0
  73. data/lib/kafka/protocol/consumer_group_protocol.rb +19 -0
  74. data/lib/kafka/protocol/create_partitions_request.rb +42 -0
  75. data/lib/kafka/protocol/create_partitions_response.rb +28 -0
  76. data/lib/kafka/protocol/create_topics_request.rb +45 -0
  77. data/lib/kafka/protocol/create_topics_response.rb +26 -0
  78. data/lib/kafka/protocol/decoder.rb +175 -0
  79. data/lib/kafka/protocol/delete_topics_request.rb +33 -0
  80. data/lib/kafka/protocol/delete_topics_response.rb +26 -0
  81. data/lib/kafka/protocol/describe_configs_request.rb +35 -0
  82. data/lib/kafka/protocol/describe_configs_response.rb +73 -0
  83. data/lib/kafka/protocol/describe_groups_request.rb +27 -0
  84. data/lib/kafka/protocol/describe_groups_response.rb +73 -0
  85. data/lib/kafka/protocol/encoder.rb +184 -0
  86. data/lib/kafka/protocol/end_txn_request.rb +29 -0
  87. data/lib/kafka/protocol/end_txn_response.rb +19 -0
  88. data/lib/kafka/protocol/fetch_request.rb +70 -0
  89. data/lib/kafka/protocol/fetch_response.rb +136 -0
  90. data/lib/kafka/protocol/find_coordinator_request.rb +29 -0
  91. data/lib/kafka/protocol/find_coordinator_response.rb +29 -0
  92. data/lib/kafka/protocol/heartbeat_request.rb +27 -0
  93. data/lib/kafka/protocol/heartbeat_response.rb +17 -0
  94. data/lib/kafka/protocol/init_producer_id_request.rb +26 -0
  95. data/lib/kafka/protocol/init_producer_id_response.rb +27 -0
  96. data/lib/kafka/protocol/join_group_request.rb +47 -0
  97. data/lib/kafka/protocol/join_group_response.rb +41 -0
  98. data/lib/kafka/protocol/leave_group_request.rb +25 -0
  99. data/lib/kafka/protocol/leave_group_response.rb +17 -0
  100. data/lib/kafka/protocol/list_groups_request.rb +23 -0
  101. data/lib/kafka/protocol/list_groups_response.rb +35 -0
  102. data/lib/kafka/protocol/list_offset_request.rb +53 -0
  103. data/lib/kafka/protocol/list_offset_response.rb +89 -0
  104. data/lib/kafka/protocol/member_assignment.rb +42 -0
  105. data/lib/kafka/protocol/message.rb +172 -0
  106. data/lib/kafka/protocol/message_set.rb +55 -0
  107. data/lib/kafka/protocol/metadata_request.rb +31 -0
  108. data/lib/kafka/protocol/metadata_response.rb +185 -0
  109. data/lib/kafka/protocol/offset_commit_request.rb +47 -0
  110. data/lib/kafka/protocol/offset_commit_response.rb +29 -0
  111. data/lib/kafka/protocol/offset_fetch_request.rb +38 -0
  112. data/lib/kafka/protocol/offset_fetch_response.rb +56 -0
  113. data/lib/kafka/protocol/produce_request.rb +94 -0
  114. data/lib/kafka/protocol/produce_response.rb +63 -0
  115. data/lib/kafka/protocol/record.rb +88 -0
  116. data/lib/kafka/protocol/record_batch.rb +223 -0
  117. data/lib/kafka/protocol/request_message.rb +26 -0
  118. data/lib/kafka/protocol/sasl_handshake_request.rb +33 -0
  119. data/lib/kafka/protocol/sasl_handshake_response.rb +28 -0
  120. data/lib/kafka/protocol/sync_group_request.rb +33 -0
  121. data/lib/kafka/protocol/sync_group_response.rb +26 -0
  122. data/lib/kafka/protocol/txn_offset_commit_request.rb +46 -0
  123. data/lib/kafka/protocol/txn_offset_commit_response.rb +47 -0
  124. data/lib/kafka/protocol.rb +225 -0
  125. data/lib/kafka/round_robin_assignment_strategy.rb +52 -0
  126. data/lib/kafka/sasl/awsmskiam.rb +128 -0
  127. data/lib/kafka/sasl/gssapi.rb +76 -0
  128. data/lib/kafka/sasl/oauth.rb +64 -0
  129. data/lib/kafka/sasl/plain.rb +39 -0
  130. data/lib/kafka/sasl/scram.rb +180 -0
  131. data/lib/kafka/sasl_authenticator.rb +73 -0
  132. data/lib/kafka/snappy_codec.rb +29 -0
  133. data/lib/kafka/socket_with_timeout.rb +96 -0
  134. data/lib/kafka/ssl_context.rb +66 -0
  135. data/lib/kafka/ssl_socket_with_timeout.rb +192 -0
  136. data/lib/kafka/statsd.rb +296 -0
  137. data/lib/kafka/tagged_logger.rb +77 -0
  138. data/lib/kafka/transaction_manager.rb +306 -0
  139. data/lib/kafka/transaction_state_machine.rb +72 -0
  140. data/lib/kafka/version.rb +5 -0
  141. data/lib/kafka/zstd_codec.rb +27 -0
  142. data/lib/kafka.rb +373 -0
  143. data/lib/ruby-kafka.rb +5 -0
  144. data/ruby-kafka.gemspec +54 -0
  145. 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