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,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kafka/protocol/message_set"
4
+ require "kafka/protocol/record_batch"
5
+
6
+ module Kafka
7
+ # A produce operation attempts to send all messages in a buffer to the Kafka cluster.
8
+ # Since topics and partitions are spread among all brokers in a cluster, this usually
9
+ # involves sending requests to several or all of the brokers.
10
+ #
11
+ # ## Instrumentation
12
+ #
13
+ # When executing the operation, an `ack_message.producer.kafka` notification will be
14
+ # emitted for each message that was successfully appended to a topic partition.
15
+ # The following keys will be found in the payload:
16
+ #
17
+ # * `:topic` — the topic that was written to.
18
+ # * `:partition` — the partition that the message set was appended to.
19
+ # * `:offset` — the offset of the message in the partition.
20
+ # * `:key` — the message key.
21
+ # * `:value` — the message value.
22
+ # * `:delay` — the time between the message was produced and when it was acknowledged.
23
+ #
24
+ # In addition to these notifications, a `send_messages.producer.kafka` notification will
25
+ # be emitted after the operation completes, regardless of whether it succeeds. This
26
+ # notification will have the following keys:
27
+ #
28
+ # * `:message_count` – the total number of messages that the operation tried to
29
+ # send. Note that not all messages may get delivered.
30
+ # * `:sent_message_count` – the number of messages that were successfully sent.
31
+ #
32
+ class ProduceOperation
33
+ def initialize(cluster:, transaction_manager:, buffer:, compressor:, required_acks:, ack_timeout:, logger:, instrumenter:)
34
+ @cluster = cluster
35
+ @transaction_manager = transaction_manager
36
+ @buffer = buffer
37
+ @required_acks = required_acks
38
+ @ack_timeout = ack_timeout
39
+ @compressor = compressor
40
+ @logger = TaggedLogger.new(logger)
41
+ @instrumenter = instrumenter
42
+ end
43
+
44
+ def execute
45
+ if (@transaction_manager.idempotent? || @transaction_manager.transactional?) && @required_acks != -1
46
+ raise 'You must set required_acks option to :all to use idempotent / transactional production'
47
+ end
48
+
49
+ if @transaction_manager.transactional? && !@transaction_manager.in_transaction?
50
+ raise "Produce operation can only be executed in a pending transaction"
51
+ end
52
+
53
+ @instrumenter.instrument("send_messages.producer") do |notification|
54
+ message_count = @buffer.size
55
+
56
+ notification[:message_count] = message_count
57
+
58
+ begin
59
+ if @transaction_manager.idempotent? || @transaction_manager.transactional?
60
+ @transaction_manager.init_producer_id
61
+ end
62
+ send_buffered_messages
63
+ ensure
64
+ notification[:sent_message_count] = message_count - @buffer.size
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def send_buffered_messages
72
+ messages_for_broker = {}
73
+ topic_partitions = {}
74
+
75
+ @buffer.each do |topic, partition, messages|
76
+ begin
77
+ broker = @cluster.get_leader(topic, partition)
78
+
79
+ @logger.debug "Current leader for #{topic}/#{partition} is node #{broker}"
80
+
81
+ topic_partitions[topic] ||= Set.new
82
+ topic_partitions[topic].add(partition)
83
+
84
+ messages_for_broker[broker] ||= MessageBuffer.new
85
+ messages_for_broker[broker].concat(messages, topic: topic, partition: partition)
86
+ rescue Kafka::Error => e
87
+ @logger.error "Could not connect to leader for partition #{topic}/#{partition}: #{e.message}"
88
+
89
+ @instrumenter.instrument("topic_error.producer", {
90
+ topic: topic,
91
+ exception: [e.class.to_s, e.message],
92
+ })
93
+
94
+ # We can't send the messages right now, so we'll just keep them in the buffer.
95
+ # We'll mark the cluster as stale in order to force a metadata refresh.
96
+ @cluster.mark_as_stale!
97
+ end
98
+ end
99
+
100
+ # Add topic and partition to transaction
101
+ if @transaction_manager.transactional?
102
+ @transaction_manager.add_partitions_to_transaction(topic_partitions)
103
+ end
104
+
105
+ messages_for_broker.each do |broker, message_buffer|
106
+ begin
107
+ @logger.info "Sending #{message_buffer.size} messages to #{broker}"
108
+
109
+ records_for_topics = {}
110
+
111
+ message_buffer.each do |topic, partition, records|
112
+ record_batch = Protocol::RecordBatch.new(
113
+ records: records,
114
+ first_sequence: @transaction_manager.next_sequence_for(
115
+ topic, partition
116
+ ),
117
+ in_transaction: @transaction_manager.transactional?,
118
+ producer_id: @transaction_manager.producer_id,
119
+ producer_epoch: @transaction_manager.producer_epoch
120
+ )
121
+ records_for_topics[topic] ||= {}
122
+ records_for_topics[topic][partition] = record_batch
123
+ end
124
+
125
+ response = broker.produce(
126
+ messages_for_topics: records_for_topics,
127
+ compressor: @compressor,
128
+ required_acks: @required_acks,
129
+ timeout: @ack_timeout * 1000, # Kafka expects the timeout in milliseconds.
130
+ transactional_id: @transaction_manager.transactional_id
131
+ )
132
+
133
+ handle_response(broker, response, records_for_topics) if response
134
+ rescue ConnectionError => e
135
+ @logger.error "Could not connect to broker #{broker}: #{e}"
136
+
137
+ # Mark the cluster as stale in order to force a cluster metadata refresh.
138
+ @cluster.mark_as_stale!
139
+ end
140
+ end
141
+ end
142
+
143
+ def handle_response(broker, response, records_for_topics)
144
+ response.each_partition do |topic_info, partition_info|
145
+ topic = topic_info.topic
146
+ partition = partition_info.partition
147
+ record_batch = records_for_topics[topic][partition]
148
+ records = record_batch.records
149
+ ack_time = Time.now
150
+
151
+ begin
152
+ begin
153
+ Protocol.handle_error(partition_info.error_code)
154
+ rescue ProtocolError => e
155
+ @instrumenter.instrument("topic_error.producer", {
156
+ topic: topic,
157
+ exception: [e.class.to_s, e.message],
158
+ })
159
+
160
+ raise e
161
+ end
162
+
163
+ if @transaction_manager.idempotent? || @transaction_manager.transactional?
164
+ @transaction_manager.update_sequence_for(
165
+ topic, partition, record_batch.first_sequence + record_batch.size
166
+ )
167
+ end
168
+
169
+ records.each_with_index do |record, index|
170
+ @instrumenter.instrument("ack_message.producer", {
171
+ key: record.key,
172
+ value: record.value,
173
+ topic: topic,
174
+ partition: partition,
175
+ offset: partition_info.offset + index,
176
+ delay: ack_time - record.create_time,
177
+ })
178
+ end
179
+ rescue Kafka::CorruptMessage
180
+ @logger.error "Corrupt message when writing to #{topic}/#{partition} on #{broker}"
181
+ rescue Kafka::UnknownTopicOrPartition
182
+ @logger.error "Unknown topic or partition #{topic}/#{partition} on #{broker}"
183
+ @cluster.mark_as_stale!
184
+ rescue Kafka::LeaderNotAvailable
185
+ @logger.error "Leader currently not available for #{topic}/#{partition}"
186
+ @cluster.mark_as_stale!
187
+ rescue Kafka::NotLeaderForPartition
188
+ @logger.error "Broker #{broker} not currently leader for #{topic}/#{partition}"
189
+ @cluster.mark_as_stale!
190
+ rescue Kafka::RequestTimedOut
191
+ @logger.error "Timed out while writing to #{topic}/#{partition} on #{broker}"
192
+ rescue Kafka::NotEnoughReplicas
193
+ @logger.error "Not enough in-sync replicas for #{topic}/#{partition}"
194
+ rescue Kafka::NotEnoughReplicasAfterAppend
195
+ @logger.error "Messages written, but to fewer in-sync replicas than required for #{topic}/#{partition}"
196
+ else
197
+ @logger.debug "Successfully appended #{records.count} messages to #{topic}/#{partition} on #{broker}"
198
+
199
+ # The messages were successfully written; clear them from the buffer.
200
+ @buffer.clear_messages(topic: topic, partition: partition)
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end