Class: Karafka::Connection::Proxy
- Inherits:
-
SimpleDelegator
- Object
- SimpleDelegator
- Karafka::Connection::Proxy
- Defined in:
- lib/karafka/connection/proxy.rb
Overview
Usually it is ok to use the Rdkafka::Consumer directly because we need 1:1 its
functionality. There are however cases where we want to have extra recoveries or other
handling of errors and settings. This is where this module comes in handy.
We do not want to wrap and delegate all via a proxy object for performance reasons, but we do still want to be able to alter some functionalities. This wrapper helps us do it when it would be needed
Instance Attribute Summary collapse
-
#wrapped ⇒ Object
(also: #__getobj__)
Returns the value of attribute wrapped.
Instance Method Summary collapse
-
#commit_offsets(tpl = nil, async: true) ⇒ Boolean
Non thread-safe message committing method.
-
#committed(tpl = nil) ⇒ Rdkafka::Consumer::TopicPartitionList
Similar to
#query_watermark_offsets. -
#initialize(obj) ⇒ Proxy
constructor
A new instance of Proxy.
-
#lag(tpl) ⇒ Hash{String => Hash}
Hash with topics and their partitions lags.
-
#metadata(topic_name = nil) ⇒ Rdkafka::Metadata
Rdkafka metadata object with the requested details.
-
#offsets_for_times(tpl) ⇒ Rdkafka::Consumer::TopicPartitionList
Similar to
#query_watermark_offsets, this method can be sensitive to latency. -
#query_watermark_offsets(topic, partition) ⇒ Array<Integer, Integer>
Proxies the
#query_watermark_offsetswith extra recovery from timeout problems. -
#read_partition_offsets(topic_partition_offsets, isolation_level: nil) ⇒ Array<Hash>
Resolves offsets of many partitions with one batched
ListOffsetsrequest instead of one roundtrip per partition. -
#store_offset(message, metadata = nil) ⇒ Boolean
When we cannot store an offset, it means we no longer own the partition.
Constructor Details
#initialize(obj) ⇒ Proxy
Returns a new instance of Proxy.
38 39 40 41 42 43 44 |
# File 'lib/karafka/connection/proxy.rb', line 38 def initialize(obj) super # Do not allow for wrapping proxy with a proxy. This will prevent a case where we might # wrap an already wrapped object with another proxy level. Simplifies passing consumers # and makes it safe to wrap without type checking @wrapped = obj.is_a?(self.class) ? obj.wrapped : obj end |
Instance Attribute Details
#wrapped ⇒ Object Also known as: __getobj__
Returns the value of attribute wrapped.
33 34 35 |
# File 'lib/karafka/connection/proxy.rb', line 33 def wrapped @wrapped end |
Instance Method Details
#commit_offsets(tpl = nil, async: true) ⇒ Boolean
We do not consider no_offset as any problem and we allow to commit offsets
even when no stored, because with sync commit, it refreshes the ownership state of the
consumer in a sync way.
Non thread-safe message committing method
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/karafka/connection/proxy.rb', line 172 def commit_offsets(tpl = nil, async: true) c_config = proxy_config.commit with_broker_errors_retry( wait_time: c_config.wait_time / 1_000.to_f, max_attempts: c_config.max_attempts ) do @wrapped.commit(tpl, async) end true rescue Rdkafka::RdkafkaError => e case e.code when :assignment_lost return false when :unknown_member_id return false when :illegal_generation return false when :no_offset return true when :coordinator_load_in_progress sleep(1) retry end raise e end |
#committed(tpl = nil) ⇒ Rdkafka::Consumer::TopicPartitionList
Similar to #query_watermark_offsets.
135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/karafka/connection/proxy.rb', line 135 def committed(tpl = nil) c_config = proxy_config.committed with_broker_errors_retry( # required to be in seconds, not ms wait_time: c_config.wait_time / 1_000.to_f, max_attempts: c_config.max_attempts ) do @wrapped.committed(tpl, c_config.timeout) end end |
#lag(tpl) ⇒ Hash{String => Hash}
Returns hash with topics and their partitions lags.
204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/karafka/connection/proxy.rb', line 204 def lag(tpl) l_config = proxy_config.committed with_broker_errors_retry( # required to be in seconds, not ms wait_time: l_config.wait_time / 1_000.to_f, max_attempts: l_config.max_attempts ) do @wrapped.lag(tpl, l_config.timeout) end end |
#metadata(topic_name = nil) ⇒ Rdkafka::Metadata
Returns rdkafka metadata object with the requested details.
219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/karafka/connection/proxy.rb', line 219 def (topic_name = nil) m_config = proxy_config. with_broker_errors_retry( # required to be in seconds, not ms wait_time: m_config.wait_time / 1_000.to_f, max_attempts: m_config.max_attempts ) do @wrapped.(topic_name, m_config.timeout) end end |
#offsets_for_times(tpl) ⇒ Rdkafka::Consumer::TopicPartitionList
Similar to #query_watermark_offsets, this method can be sensitive to latency. We handle
this the same way
118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/karafka/connection/proxy.rb', line 118 def offsets_for_times(tpl) l_config = proxy_config.offsets_for_times with_broker_errors_retry( # required to be in seconds, not ms wait_time: l_config.wait_time / 1_000.to_f, max_attempts: l_config.max_attempts ) do @wrapped.offsets_for_times(tpl, l_config.timeout) end end |
#query_watermark_offsets(topic, partition) ⇒ Array<Integer, Integer>
Proxies the #query_watermark_offsets with extra recovery from timeout problems.
We impose our own custom timeout to make sure, that high-latency clusters and overloaded
clusters can handle our requests.
53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/karafka/connection/proxy.rb', line 53 def query_watermark_offsets(topic, partition) l_config = proxy_config.query_watermark_offsets # For newly created topics or in cases where we're trying to get them but there is no # leader, this can fail. It happens more often for new topics under KRaft, however we # still want to make sure things operate as expected even then with_broker_errors_retry( # required to be in seconds, not ms wait_time: l_config.wait_time / 1_000.to_f, max_attempts: l_config.max_attempts ) do @wrapped.query_watermark_offsets(topic, partition, l_config.timeout) end end |
#read_partition_offsets(topic_partition_offsets, isolation_level: nil) ⇒ Array<Hash>
The ListOffsets admin API resolves :latest to the high watermark even with
isolation_level set to read_committed, unlike #query_watermark_offsets, which returns
the last stable offset for a read_committed consumer. On a topic with an in-flight
transaction :latest therefore includes the uncommitted messages. Non-transactional
topics are unaffected.
Specs given as a literal hash need explicit curly braces: a brace-less trailing
hash is parsed by Ruby as keyword arguments, which this method (having the
isolation_level: keyword) would reject as a missing positional argument
Resolves offsets of many partitions with one batched ListOffsets request instead of one
roundtrip per partition. This is the same broker query as #query_watermark_offsets,
only batched, so it is sensitive to the same latencies and leader-election hiccups and we
recover from them the same way and with the same settings.
Named after Admin::Topics#read_partition_offsets (same specs in, same offsets out) and
not after the underlying #list_offsets, so we do not shadow the rdkafka method of
that name: it returns a handle to wait on, while we return the already resolved offsets.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/karafka/connection/proxy.rb', line 91 def read_partition_offsets(topic_partition_offsets, isolation_level: nil) l_config = proxy_config.query_watermark_offsets with_broker_errors_retry( # required to be in seconds, not ms wait_time: l_config.wait_time / 1_000.to_f, max_attempts: l_config.max_attempts ) do @wrapped .list_offsets(topic_partition_offsets, isolation_level: isolation_level) .wait(max_wait_timeout_ms: l_config.timeout) .offsets rescue Rdkafka::AbstractHandle::WaitTimeoutError # Align with the error contract of the per-partition watermark query, so a slow broker # is retried here instead of escaping as a handle wait timeout raise Rdkafka::RdkafkaError.new( Rdkafka::Bindings::RD_KAFKA_RESP_ERR__TIMED_OUT, "Error querying offsets of '#{topic_partition_offsets.keys.join(", ")}'" ) end end |
#store_offset(message, metadata = nil) ⇒ Boolean
When we cannot store an offset, it means we no longer own the partition
Non thread-safe offset storing method
153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/karafka/connection/proxy.rb', line 153 def store_offset(, = nil) @wrapped.store_offset(, ) true rescue Rdkafka::RdkafkaError => e return false if e.code == :assignment_lost return false if e.code == :state return false if e.code == :illegal_generation raise e end |