karafka 1.4.9 → 2.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/FUNDING.yml +3 -0
  4. data/.github/workflows/ci.yml +78 -26
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +46 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +39 -49
  9. data/LICENSE +14 -0
  10. data/LICENSE-COMM +89 -0
  11. data/LICENSE-LGPL +165 -0
  12. data/README.md +16 -48
  13. data/bin/benchmarks +85 -0
  14. data/bin/create_token +28 -0
  15. data/bin/integrations +160 -0
  16. data/bin/stress +13 -0
  17. data/certs/karafka-pro.pem +11 -0
  18. data/config/errors.yml +4 -38
  19. data/docker-compose.yml +11 -3
  20. data/karafka.gemspec +17 -17
  21. data/lib/active_job/consumer.rb +22 -0
  22. data/lib/active_job/karafka.rb +18 -0
  23. data/lib/active_job/queue_adapters/karafka_adapter.rb +29 -0
  24. data/lib/active_job/routing_extensions.rb +15 -0
  25. data/lib/karafka/app.rb +13 -20
  26. data/lib/karafka/base_consumer.rb +103 -34
  27. data/lib/karafka/cli/base.rb +4 -4
  28. data/lib/karafka/cli/info.rb +43 -8
  29. data/lib/karafka/cli/install.rb +3 -8
  30. data/lib/karafka/cli/server.rb +17 -30
  31. data/lib/karafka/cli.rb +4 -11
  32. data/lib/karafka/connection/client.rb +279 -93
  33. data/lib/karafka/connection/listener.rb +137 -38
  34. data/lib/karafka/connection/messages_buffer.rb +57 -0
  35. data/lib/karafka/connection/pauses_manager.rb +46 -0
  36. data/lib/karafka/connection/rebalance_manager.rb +62 -0
  37. data/lib/karafka/contracts/config.rb +25 -7
  38. data/lib/karafka/contracts/consumer_group.rb +0 -173
  39. data/lib/karafka/contracts/consumer_group_topic.rb +17 -7
  40. data/lib/karafka/contracts/server_cli_options.rb +1 -9
  41. data/lib/karafka/contracts.rb +1 -1
  42. data/lib/karafka/env.rb +46 -0
  43. data/lib/karafka/errors.rb +14 -18
  44. data/lib/karafka/helpers/multi_delegator.rb +2 -2
  45. data/lib/karafka/instrumentation/callbacks/error.rb +40 -0
  46. data/lib/karafka/instrumentation/callbacks/statistics.rb +42 -0
  47. data/lib/karafka/instrumentation/monitor.rb +14 -21
  48. data/lib/karafka/instrumentation/stdout_listener.rb +64 -91
  49. data/lib/karafka/instrumentation.rb +21 -0
  50. data/lib/karafka/licenser.rb +65 -0
  51. data/lib/karafka/{params → messages}/batch_metadata.rb +7 -13
  52. data/lib/karafka/messages/builders/batch_metadata.rb +30 -0
  53. data/lib/karafka/messages/builders/message.rb +38 -0
  54. data/lib/karafka/messages/builders/messages.rb +40 -0
  55. data/lib/karafka/{params/params.rb → messages/message.rb} +7 -12
  56. data/lib/karafka/messages/messages.rb +64 -0
  57. data/lib/karafka/{params → messages}/metadata.rb +4 -6
  58. data/lib/karafka/messages/seek.rb +9 -0
  59. data/lib/karafka/patches/rdkafka/consumer.rb +22 -0
  60. data/lib/karafka/processing/executor.rb +96 -0
  61. data/lib/karafka/processing/executors_buffer.rb +49 -0
  62. data/lib/karafka/processing/jobs/base.rb +18 -0
  63. data/lib/karafka/processing/jobs/consume.rb +28 -0
  64. data/lib/karafka/processing/jobs/revoked.rb +22 -0
  65. data/lib/karafka/processing/jobs/shutdown.rb +23 -0
  66. data/lib/karafka/processing/jobs_queue.rb +121 -0
  67. data/lib/karafka/processing/worker.rb +57 -0
  68. data/lib/karafka/processing/workers_batch.rb +22 -0
  69. data/lib/karafka/railtie.rb +65 -0
  70. data/lib/karafka/routing/builder.rb +15 -14
  71. data/lib/karafka/routing/consumer_group.rb +10 -18
  72. data/lib/karafka/routing/consumer_mapper.rb +1 -2
  73. data/lib/karafka/routing/router.rb +1 -1
  74. data/lib/karafka/routing/subscription_group.rb +53 -0
  75. data/lib/karafka/routing/subscription_groups_builder.rb +51 -0
  76. data/lib/karafka/routing/topic.rb +47 -25
  77. data/lib/karafka/runner.rb +59 -0
  78. data/lib/karafka/serialization/json/deserializer.rb +6 -15
  79. data/lib/karafka/server.rb +62 -25
  80. data/lib/karafka/setup/config.rb +86 -159
  81. data/lib/karafka/status.rb +13 -3
  82. data/lib/karafka/templates/example_consumer.rb.erb +16 -0
  83. data/lib/karafka/templates/karafka.rb.erb +14 -50
  84. data/lib/karafka/time_trackers/base.rb +19 -0
  85. data/lib/karafka/time_trackers/pause.rb +84 -0
  86. data/lib/karafka/time_trackers/poll.rb +65 -0
  87. data/lib/karafka/version.rb +1 -1
  88. data/lib/karafka.rb +30 -13
  89. data.tar.gz.sig +0 -0
  90. metadata +78 -108
  91. metadata.gz.sig +0 -0
  92. data/MIT-LICENCE +0 -18
  93. data/lib/karafka/assignment_strategies/round_robin.rb +0 -13
  94. data/lib/karafka/attributes_map.rb +0 -63
  95. data/lib/karafka/backends/inline.rb +0 -16
  96. data/lib/karafka/base_responder.rb +0 -226
  97. data/lib/karafka/cli/flow.rb +0 -48
  98. data/lib/karafka/cli/missingno.rb +0 -19
  99. data/lib/karafka/code_reloader.rb +0 -67
  100. data/lib/karafka/connection/api_adapter.rb +0 -158
  101. data/lib/karafka/connection/batch_delegator.rb +0 -55
  102. data/lib/karafka/connection/builder.rb +0 -23
  103. data/lib/karafka/connection/message_delegator.rb +0 -36
  104. data/lib/karafka/consumers/batch_metadata.rb +0 -10
  105. data/lib/karafka/consumers/callbacks.rb +0 -71
  106. data/lib/karafka/consumers/includer.rb +0 -64
  107. data/lib/karafka/consumers/responders.rb +0 -24
  108. data/lib/karafka/consumers/single_params.rb +0 -15
  109. data/lib/karafka/contracts/responder_usage.rb +0 -54
  110. data/lib/karafka/fetcher.rb +0 -42
  111. data/lib/karafka/helpers/class_matcher.rb +0 -88
  112. data/lib/karafka/helpers/config_retriever.rb +0 -46
  113. data/lib/karafka/helpers/inflector.rb +0 -26
  114. data/lib/karafka/params/builders/batch_metadata.rb +0 -30
  115. data/lib/karafka/params/builders/params.rb +0 -38
  116. data/lib/karafka/params/builders/params_batch.rb +0 -25
  117. data/lib/karafka/params/params_batch.rb +0 -60
  118. data/lib/karafka/patches/ruby_kafka.rb +0 -47
  119. data/lib/karafka/persistence/client.rb +0 -29
  120. data/lib/karafka/persistence/consumers.rb +0 -45
  121. data/lib/karafka/persistence/topics.rb +0 -48
  122. data/lib/karafka/responders/builder.rb +0 -36
  123. data/lib/karafka/responders/topic.rb +0 -55
  124. data/lib/karafka/routing/topic_mapper.rb +0 -53
  125. data/lib/karafka/serialization/json/serializer.rb +0 -31
  126. data/lib/karafka/setup/configurators/water_drop.rb +0 -36
  127. data/lib/karafka/templates/application_responder.rb.erb +0 -11
@@ -3,7 +3,7 @@
3
3
  module Karafka
4
4
  module Instrumentation
5
5
  # Default listener that hooks up to our instrumentation and uses its events for logging
6
- # It can be removed/replaced or anything without any harm to the Karafka app flow
6
+ # It can be removed/replaced or anything without any harm to the Karafka app flow.
7
7
  class StdoutListener
8
8
  # Log levels that we use in this particular listener
9
9
  USED_LOG_LEVELS = %i[
@@ -13,121 +13,94 @@ module Karafka
13
13
  fatal
14
14
  ].freeze
15
15
 
16
- # Logs details about incoming batches and with which consumer we will consume them
17
- # @param event [Dry::Events::Event] event details including payload
18
- def on_connection_batch_delegator_call(event)
19
- consumer = event[:consumer]
20
- topic = consumer.topic.name
21
- kafka_messages = event[:kafka_batch].messages
22
- info(
23
- <<~MSG.chomp.tr("\n", ' ')
24
- #{kafka_messages.count} messages
25
- on #{topic} topic
26
- delegated to #{consumer.class}
27
- MSG
28
- )
29
- end
30
-
31
- # Logs details about incoming message and with which consumer we will consume it
32
- # @param event [Dry::Events::Event] event details including payload
33
- def on_connection_message_delegator_call(event)
34
- consumer = event[:consumer]
35
- topic = consumer.topic.name
36
- info "1 message on #{topic} topic delegated to #{consumer.class}"
37
- end
38
-
39
- # Logs details about each received message value deserialization
40
- # @param event [Dry::Events::Event] event details including payload
41
- def on_params_params_deserialize(event)
42
- # Keep in mind, that a caller here is a param object not a controller,
43
- # so it returns a topic as a string, not a routing topic
44
- debug(
45
- <<~MSG.chomp.tr("\n", ' ')
46
- Params deserialization for #{event[:caller].metadata.topic} topic
47
- successful in #{event[:time]} ms
48
- MSG
49
- )
50
- end
51
-
52
- # Logs unsuccessful deserialization attempts of incoming data
53
- # @param event [Dry::Events::Event] event details including payload
54
- def on_params_params_deserialize_error(event)
55
- topic = event[:caller].metadata.topic
56
- error = event[:error]
57
- error "Params deserialization error for #{topic} topic: #{error}"
58
- end
59
-
60
- # Logs errors that occurred in a listener fetch loop
61
- # @param event [Dry::Events::Event] event details including payload
62
- # @note It's an error as we can recover from it not a fatal
63
- def on_connection_listener_fetch_loop_error(event)
64
- error "Listener fetch loop error: #{event[:error]}"
65
- end
66
-
67
- # Logs errors that are related to the connection itself
68
- # @param event [Dry::Events::Event] event details including payload
69
- # @note Karafka will attempt to reconnect, so an error not a fatal
70
- def on_connection_client_fetch_loop_error(event)
71
- error "Client fetch loop error: #{event[:error]}"
72
- end
73
-
74
- # Logs info about crashed fetcher
75
- # @param event [Dry::Events::Event] event details including payload
76
- # @note If this happens, Karafka will shutdown as it means a critical error
77
- # in one of the threads
78
- def on_fetcher_call_error(event)
79
- fatal "Fetcher crash due to an error: #{event[:error]}"
16
+ # Logs each messages fetching attempt
17
+ #
18
+ # @param _event [Dry::Events::Event] event details including payload
19
+ def on_connection_listener_fetch_loop(_event)
20
+ info 'Receiving new messages from Kafka...'
80
21
  end
81
22
 
82
- # Logs info about processing of a certain dataset with an inline backend
23
+ # Logs about messages that we've received from Kafka
24
+ #
83
25
  # @param event [Dry::Events::Event] event details including payload
84
- def on_backends_inline_process(event)
85
- count = event[:caller].send(:params_batch).to_a.size
86
- topic = event[:caller].topic.name
87
- time = event[:time]
88
- info "Inline processing of topic #{topic} with #{count} messages took #{time} ms"
26
+ def on_connection_listener_fetch_loop_received(event)
27
+ info "Received #{event[:messages_buffer].size} new messages from Kafka"
89
28
  end
90
29
 
91
- # Logs info about system signals that Karafka received
30
+ # Logs info about system signals that Karafka received.
31
+ #
92
32
  # @param event [Dry::Events::Event] event details including payload
93
33
  def on_process_notice_signal(event)
94
34
  info "Received #{event[:signal]} system signal"
95
35
  end
96
36
 
97
- # Logs info about responder usage withing a controller flow
98
- # @param event [Dry::Events::Event] event details including payload
99
- def on_consumers_responders_respond_with(event)
100
- calling = event[:caller]
101
- responder = calling.topic.responder
102
- data = event[:data]
103
- info "Responded from #{calling.class} using #{responder} with following data #{data}"
104
- end
105
-
106
- # Logs info that we're initializing Karafka framework components
37
+ # Logs info that we're initializing Karafka app.
38
+ #
107
39
  # @param _event [Dry::Events::Event] event details including payload
108
40
  def on_app_initializing(_event)
109
- info "Initializing Karafka framework #{::Process.pid}"
41
+ info 'Initializing Karafka framework'
110
42
  end
111
43
 
112
- # Logs info that we're running Karafka app
44
+ # Logs info that we're running Karafka app.
45
+ #
113
46
  # @param _event [Dry::Events::Event] event details including payload
114
47
  def on_app_running(_event)
115
- info "Running Karafka server #{::Process.pid}"
48
+ info 'Running Karafka server'
116
49
  end
117
50
 
118
- # Logs info that we're going to stop the Karafka server
51
+ # Logs info that we're going to stop the Karafka server.
52
+ #
119
53
  # @param _event [Dry::Events::Event] event details including payload
120
54
  def on_app_stopping(_event)
121
55
  # We use a separate thread as logging can't be called from trap context
122
- Thread.new { info "Stopping Karafka server #{::Process.pid}" }
56
+ Thread.new { info 'Stopping Karafka server' }
123
57
  end
124
58
 
125
- # Logs an error that Karafka was unable to stop the server gracefully and it had to do a
126
- # forced exit
59
+ # Logs info that we stopped the Karafka server.
60
+ #
127
61
  # @param _event [Dry::Events::Event] event details including payload
128
- def on_app_stopping_error(_event)
62
+ def on_app_stopped(_event)
129
63
  # We use a separate thread as logging can't be called from trap context
130
- Thread.new { error "Forceful Karafka server #{::Process.pid} stop" }
64
+ Thread.new { info 'Stopped Karafka server' }
65
+ end
66
+
67
+ # There are many types of errors that can occur in many places, but we provide a single
68
+ # handler for all of them to simplify error instrumentation.
69
+ # @param event [Dry::Events::Event] event details including payload
70
+ def on_error_occurred(event)
71
+ type = event[:type]
72
+ error = event[:error]
73
+ details = (error.backtrace || []).join("\n")
74
+
75
+ case type
76
+ when 'consumer.consume.error'
77
+ error "Consumer consuming error: #{error}"
78
+ error details
79
+ when 'consumer.revoked.error'
80
+ error "Consumer on revoked failed due to an error: #{error}"
81
+ error details
82
+ when 'consumer.shutdown.error'
83
+ error "Consumer on shutdown failed due to an error: #{error}"
84
+ error details
85
+ when 'worker.process.error'
86
+ fatal "Worker processing failed due to an error: #{error}"
87
+ fatal details
88
+ when 'connection.listener.fetch_loop.error'
89
+ error "Listener fetch loop error: #{error}"
90
+ error details
91
+ when 'runner.call.error'
92
+ fatal "Runner crashed due to an error: #{error}"
93
+ fatal details
94
+ when 'app.stopping.error'
95
+ # We use a separate thread as logging can't be called from trap context
96
+ Thread.new { error 'Forceful Karafka server stop' }
97
+ when 'librdkafka.error'
98
+ error "librdkafka internal error occurred: #{error}"
99
+ error details
100
+ else
101
+ # This should never happen. Please contact the maintainers
102
+ raise Errors::UnsupportedCaseError, event
103
+ end
131
104
  end
132
105
 
133
106
  USED_LOG_LEVELS.each do |log_level|
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ # @note Since we can only have one statistics callbacks manager and one error callbacks manager
5
+ # we use WaterDrops one that is already configured.
6
+ module Instrumentation
7
+ class << self
8
+ # Returns a manager for statistics callbacks
9
+ # @return [::WaterDrop::CallbacksManager]
10
+ def statistics_callbacks
11
+ ::WaterDrop::Instrumentation.statistics_callbacks
12
+ end
13
+
14
+ # Returns a manager for error callbacks
15
+ # @return [::WaterDrop::CallbacksManager]
16
+ def error_callbacks
17
+ ::WaterDrop::Instrumentation.error_callbacks
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ # Checks the license presence for pro and loads pro components when needed (if any)
5
+ class Licenser
6
+ # Location in the gem where we store the public key
7
+ PUBLIC_KEY_LOCATION = File.join(Karafka.gem_root, 'certs', 'karafka-pro.pem')
8
+
9
+ private_constant :PUBLIC_KEY_LOCATION
10
+
11
+ # Check license and setup license details (if needed)
12
+ # @param license_config [Dry::Configurable::Config] config part related to the licensing
13
+ def verify(license_config)
14
+ # If no license, it will just run LGPL components without anything extra
15
+ return unless license_config.token
16
+
17
+ public_key = OpenSSL::PKey::RSA.new(File.read(PUBLIC_KEY_LOCATION))
18
+
19
+ # We gsub and strip in case someone copy-pasted it as a multi line string
20
+ formatted_token = license_config.token.strip.delete("\n").delete(' ')
21
+ decoded_token = Base64.decode64(formatted_token)
22
+
23
+ begin
24
+ data = public_key.public_decrypt(decoded_token)
25
+ rescue OpenSSL::OpenSSLError
26
+ data = nil
27
+ end
28
+
29
+ details = data ? JSON.parse(data) : raise_invalid_license_token
30
+
31
+ license_config.entity = details.fetch('entity')
32
+ license_config.expires_on = Date.parse(details.fetch('expires_on'))
33
+
34
+ return if license_config.expires_on > Date.today
35
+
36
+ notify_if_license_expired(license_config.expires_on)
37
+ end
38
+
39
+ private
40
+
41
+ # Raises an error with info, that used token is invalid
42
+ def raise_invalid_license_token
43
+ raise(
44
+ Errors::InvalidLicenseTokenError,
45
+ <<~MSG.tr("\n", ' ')
46
+ License key you provided is invalid.
47
+ Please reach us at contact@karafka.io or visit https://karafka.io to obtain a valid one.
48
+ MSG
49
+ )
50
+ end
51
+
52
+ # We do not raise an error here as we don't want to cause any problems to someone that runs
53
+ # Karafka on production. Error is enough.
54
+ # @param expires_on [Date] when the license expires
55
+ def notify_if_license_expired(expires_on)
56
+ Karafka.logger.error(
57
+ <<~MSG.tr("\n", ' ')
58
+ Your license expired on #{expires_on}.
59
+ Karafka no longer uses any Pro capabilities.
60
+ Please reach us at contact@karafka.io or visit https://karafka.io to obtain a valid one.
61
+ MSG
62
+ )
63
+ end
64
+ end
65
+ end
@@ -1,26 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Karafka
4
- module Params
4
+ module Messages
5
5
  # Simple batch metadata object that stores all non-message information received from Kafka
6
- # cluster while fetching the data
7
- # @note This metadata object refers to per batch metadata, not `#params.metadata`
6
+ # cluster while fetching the data.
7
+ #
8
+ # @note This metadata object refers to per batch metadata, not `#message.metadata`
8
9
  BatchMetadata = Struct.new(
9
- :batch_size,
10
+ :size,
10
11
  :first_offset,
11
- :highwater_mark_offset,
12
- :unknown_last_offset,
13
12
  :last_offset,
14
- :offset_lag,
15
13
  :deserializer,
16
14
  :partition,
17
15
  :topic,
16
+ :scheduled_at,
18
17
  keyword_init: true
19
- ) do
20
- # @return [Boolean] is the last offset known or unknown
21
- def unknown_last_offset?
22
- unknown_last_offset
23
- end
24
- end
18
+ )
25
19
  end
26
20
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Messages
5
+ module Builders
6
+ # Builder for creating batch metadata object based on the batch informations.
7
+ module BatchMetadata
8
+ class << self
9
+ # Creates metadata based on the kafka batch data.
10
+ #
11
+ # @param kafka_batch [Array<Rdkafka::Consumer::Message>] raw fetched messages
12
+ # @param topic [Karafka::Routing::Topic] topic for which we've fetched the batch
13
+ # @param scheduled_at [Time] moment when the batch was scheduled for processing
14
+ # @return [Karafka::Messages::BatchMetadata] batch metadata object
15
+ def call(kafka_batch, topic, scheduled_at)
16
+ Karafka::Messages::BatchMetadata.new(
17
+ size: kafka_batch.count,
18
+ first_offset: kafka_batch.first.offset,
19
+ last_offset: kafka_batch.last.offset,
20
+ deserializer: topic.deserializer,
21
+ partition: kafka_batch[0].partition,
22
+ topic: topic.name,
23
+ scheduled_at: scheduled_at
24
+ ).freeze
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Messages
5
+ # Builders encapsulate logic related to creating messages related objects.
6
+ module Builders
7
+ # Builder of a single message based on raw rdkafka message.
8
+ module Message
9
+ class << self
10
+ # @param kafka_message [Rdkafka::Consumer::Message] raw fetched message
11
+ # @param topic [Karafka::Routing::Topic] topic for which this message was fetched
12
+ # @param received_at [Time] moment when we've received the message
13
+ # @return [Karafka::Messages::Message] message object with payload and metadata
14
+ def call(kafka_message, topic, received_at)
15
+ # @see https://github.com/appsignal/rdkafka-ruby/issues/168
16
+ kafka_message.headers.transform_keys!(&:to_s)
17
+
18
+ metadata = Karafka::Messages::Metadata.new(
19
+ timestamp: kafka_message.timestamp,
20
+ headers: kafka_message.headers,
21
+ key: kafka_message.key,
22
+ offset: kafka_message.offset,
23
+ deserializer: topic.deserializer,
24
+ partition: kafka_message.partition,
25
+ topic: topic.name,
26
+ received_at: received_at
27
+ ).freeze
28
+
29
+ Karafka::Messages::Message.new(
30
+ kafka_message.payload,
31
+ metadata
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Messages
5
+ module Builders
6
+ # Builder for creating message batch instances.
7
+ module Messages
8
+ class << self
9
+ # Creates messages batch with messages inside based on the incoming messages and the
10
+ # topic from which it comes.
11
+ #
12
+ # @param kafka_messages [Array<Rdkafka::Consumer::Message>] raw fetched messages
13
+ # @param topic [Karafka::Routing::Topic] topic for which we're received messages
14
+ # @param received_at [Time] moment in time when the messages were received
15
+ # @return [Karafka::Messages::Messages] messages batch object
16
+ def call(kafka_messages, topic, received_at)
17
+ messages_array = kafka_messages.map do |message|
18
+ Karafka::Messages::Builders::Message.call(
19
+ message,
20
+ topic,
21
+ received_at
22
+ )
23
+ end
24
+
25
+ metadata = BatchMetadata.call(
26
+ kafka_messages,
27
+ topic,
28
+ received_at
29
+ ).freeze
30
+
31
+ Karafka::Messages::Messages.new(
32
+ messages_array,
33
+ metadata
34
+ ).freeze
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Karafka
4
- # Params namespace encapsulating all the logic that is directly related to params handling
5
- module Params
4
+ # Messages namespace encapsulating all the logic that is directly related to messages handling
5
+ module Messages
6
6
  # It provides lazy loading not only until the first usage, but also allows us to skip
7
7
  # using deserializer until we execute our logic. That way we can operate with
8
8
  # heavy-deserialization data without slowing down the whole application.
9
- class Params
9
+ class Message
10
10
  extend Forwardable
11
11
 
12
12
  attr_reader :raw_payload, :metadata
@@ -14,7 +14,7 @@ module Karafka
14
14
  def_delegators :metadata, *Metadata.members
15
15
 
16
16
  # @param raw_payload [Object] incoming payload before deserialization
17
- # @param metadata [Karafka::Params::Metadata] message metadata object
17
+ # @param metadata [Karafka::Messages::Metadata] message metadata object
18
18
  def initialize(raw_payload, metadata)
19
19
  @raw_payload = raw_payload
20
20
  @metadata = metadata
@@ -33,21 +33,16 @@ module Karafka
33
33
  @payload
34
34
  end
35
35
 
36
- # @return [Boolean] did given params payload were deserialized already
36
+ # @return [Boolean] did we deserialize payload already
37
37
  def deserialized?
38
38
  @deserialized
39
39
  end
40
40
 
41
41
  private
42
42
 
43
- # @return [Object] tries de-serializes data
43
+ # @return [Object] deserialized data
44
44
  def deserialize
45
- Karafka.monitor.instrument('params.params.deserialize', caller: self) do
46
- metadata.deserializer.call(self)
47
- end
48
- rescue ::StandardError => e
49
- Karafka.monitor.instrument('params.params.deserialize.error', caller: self, error: e)
50
- raise e
45
+ metadata.deserializer.call(self)
51
46
  end
52
47
  end
53
48
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Messages
5
+ # Messages batch represents a set of messages received from Kafka of a single topic partition.
6
+ class Messages
7
+ include Enumerable
8
+
9
+ attr_reader :metadata
10
+
11
+ # @param messages_array [Array<Karafka::Messages::Message>] array with karafka messages
12
+ # @param metadata [Karafka::Messages::BatchMetadata]
13
+ # @return [Karafka::Messages::Messages] lazy evaluated messages batch object
14
+ def initialize(messages_array, metadata)
15
+ @messages_array = messages_array
16
+ @metadata = metadata
17
+ end
18
+
19
+ # @param block [Proc] block we want to execute per each message
20
+ # @note Invocation of this method will not cause loading and deserializing of messages.
21
+ def each(&block)
22
+ @messages_array.each(&block)
23
+ end
24
+
25
+ # Runs deserialization of all the messages and returns them
26
+ # @return [Array<Karafka::Messages::Message>]
27
+ def deserialize!
28
+ each(&:payload)
29
+ end
30
+
31
+ # @return [Array<Object>] array with deserialized payloads. This method can be useful when
32
+ # we don't care about metadata and just want to extract all the data payloads from the
33
+ # batch
34
+ def payloads
35
+ map(&:payload)
36
+ end
37
+
38
+ # @return [Array<String>] array with raw, not deserialized payloads
39
+ def raw_payloads
40
+ map(&:raw_payload)
41
+ end
42
+
43
+ # @return [Karafka::Messages::Message] first message
44
+ def first
45
+ @messages_array.first
46
+ end
47
+
48
+ # @return [Karafka::Messages::Message] last message
49
+ def last
50
+ @messages_array.last
51
+ end
52
+
53
+ # @return [Integer] number of messages in the batch
54
+ def size
55
+ @messages_array.size
56
+ end
57
+
58
+ # @return [Array<Karafka::Messages::Message>] pure array with messages
59
+ def to_a
60
+ @messages_array
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,18 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Karafka
4
- module Params
5
- # Single message / params metadata details that can be accessed without the need for the
6
- # payload deserialization
4
+ module Messages
5
+ # Single message metadata details that can be accessed without the need of deserialization.
7
6
  Metadata = Struct.new(
8
- :create_time,
7
+ :timestamp,
9
8
  :headers,
10
- :is_control_record,
11
9
  :key,
12
10
  :offset,
13
11
  :deserializer,
14
12
  :partition,
15
- :receive_time,
13
+ :received_at,
16
14
  :topic,
17
15
  keyword_init: true
18
16
  )
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Messages
5
+ # "Fake" message that we use as an abstraction layer when seeking back.
6
+ # This allows us to encapsulate a seek with a simple abstraction
7
+ Seek = Struct.new(:topic, :partition, :offset)
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ # Patches to external components
5
+ module Patches
6
+ # Rdkafka related patches
7
+ module Rdkafka
8
+ # Rdkafka::Consumer patches
9
+ module Consumer
10
+ # A method that allows us to get the native kafka producer name
11
+ # @return [String] producer instance name
12
+ # @note We need this to make sure that we allocate proper dispatched events only to
13
+ # callback listeners that should publish them
14
+ def name
15
+ ::Rdkafka::Bindings.rd_kafka_name(@native_kafka)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ ::Rdkafka::Consumer.include ::Karafka::Patches::Rdkafka::Consumer