karafka 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +2 -0
  3. data.tar.gz.sig +0 -0
  4. data/.coditsu/ci.yml +3 -0
  5. data/.console_irbrc +1 -3
  6. data/.github/FUNDING.yml +3 -0
  7. data/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
  8. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  9. data/.gitignore +1 -0
  10. data/.ruby-version +1 -1
  11. data/.travis.yml +35 -16
  12. data/CHANGELOG.md +151 -2
  13. data/CONTRIBUTING.md +6 -7
  14. data/Gemfile +3 -3
  15. data/Gemfile.lock +96 -70
  16. data/README.md +29 -23
  17. data/bin/karafka +1 -1
  18. data/certs/mensfeld.pem +25 -0
  19. data/config/errors.yml +38 -5
  20. data/karafka.gemspec +19 -10
  21. data/lib/karafka.rb +15 -12
  22. data/lib/karafka/app.rb +19 -18
  23. data/lib/karafka/attributes_map.rb +15 -14
  24. data/lib/karafka/backends/inline.rb +1 -2
  25. data/lib/karafka/base_consumer.rb +57 -0
  26. data/lib/karafka/base_responder.rb +72 -31
  27. data/lib/karafka/cli.rb +1 -1
  28. data/lib/karafka/cli/console.rb +11 -9
  29. data/lib/karafka/cli/flow.rb +0 -1
  30. data/lib/karafka/cli/info.rb +3 -1
  31. data/lib/karafka/cli/install.rb +29 -8
  32. data/lib/karafka/cli/server.rb +11 -7
  33. data/lib/karafka/code_reloader.rb +67 -0
  34. data/lib/karafka/connection/{config_adapter.rb → api_adapter.rb} +67 -24
  35. data/lib/karafka/connection/batch_delegator.rb +51 -0
  36. data/lib/karafka/connection/builder.rb +16 -0
  37. data/lib/karafka/connection/client.rb +117 -0
  38. data/lib/karafka/connection/listener.rb +37 -17
  39. data/lib/karafka/connection/message_delegator.rb +36 -0
  40. data/lib/karafka/consumers/callbacks.rb +71 -0
  41. data/lib/karafka/consumers/includer.rb +63 -0
  42. data/lib/karafka/consumers/metadata.rb +10 -0
  43. data/lib/karafka/consumers/responders.rb +24 -0
  44. data/lib/karafka/{controllers → consumers}/single_params.rb +3 -3
  45. data/lib/karafka/contracts.rb +10 -0
  46. data/lib/karafka/contracts/config.rb +21 -0
  47. data/lib/karafka/contracts/consumer_group.rb +206 -0
  48. data/lib/karafka/contracts/consumer_group_topic.rb +19 -0
  49. data/lib/karafka/contracts/responder_usage.rb +54 -0
  50. data/lib/karafka/contracts/server_cli_options.rb +29 -0
  51. data/lib/karafka/errors.rb +23 -15
  52. data/lib/karafka/fetcher.rb +6 -12
  53. data/lib/karafka/helpers/class_matcher.rb +19 -9
  54. data/lib/karafka/helpers/config_retriever.rb +3 -3
  55. data/lib/karafka/helpers/inflector.rb +26 -0
  56. data/lib/karafka/helpers/multi_delegator.rb +0 -1
  57. data/lib/karafka/instrumentation/logger.rb +57 -0
  58. data/lib/karafka/instrumentation/monitor.rb +70 -0
  59. data/lib/karafka/instrumentation/proctitle_listener.rb +36 -0
  60. data/lib/karafka/instrumentation/stdout_listener.rb +138 -0
  61. data/lib/karafka/params/builders/metadata.rb +33 -0
  62. data/lib/karafka/params/builders/params.rb +36 -0
  63. data/lib/karafka/params/builders/params_batch.rb +25 -0
  64. data/lib/karafka/params/metadata.rb +35 -0
  65. data/lib/karafka/params/params.rb +35 -95
  66. data/lib/karafka/params/params_batch.rb +38 -18
  67. data/lib/karafka/patches/ruby_kafka.rb +25 -12
  68. data/lib/karafka/persistence/client.rb +29 -0
  69. data/lib/karafka/persistence/consumers.rb +45 -0
  70. data/lib/karafka/persistence/topics.rb +48 -0
  71. data/lib/karafka/process.rb +5 -8
  72. data/lib/karafka/responders/builder.rb +15 -14
  73. data/lib/karafka/responders/topic.rb +6 -8
  74. data/lib/karafka/routing/builder.rb +37 -9
  75. data/lib/karafka/routing/consumer_group.rb +1 -1
  76. data/lib/karafka/routing/consumer_mapper.rb +10 -9
  77. data/lib/karafka/routing/proxy.rb +10 -1
  78. data/lib/karafka/routing/router.rb +1 -1
  79. data/lib/karafka/routing/topic.rb +8 -12
  80. data/lib/karafka/routing/topic_mapper.rb +16 -18
  81. data/lib/karafka/serialization/json/deserializer.rb +27 -0
  82. data/lib/karafka/serialization/json/serializer.rb +31 -0
  83. data/lib/karafka/server.rb +45 -24
  84. data/lib/karafka/setup/config.rb +95 -37
  85. data/lib/karafka/setup/configurators/water_drop.rb +12 -5
  86. data/lib/karafka/setup/dsl.rb +21 -0
  87. data/lib/karafka/status.rb +7 -3
  88. data/lib/karafka/templates/{application_controller.rb.example → application_consumer.rb.erb} +2 -2
  89. data/lib/karafka/templates/{application_responder.rb.example → application_responder.rb.erb} +0 -0
  90. data/lib/karafka/templates/karafka.rb.erb +92 -0
  91. data/lib/karafka/version.rb +1 -1
  92. metadata +126 -57
  93. metadata.gz.sig +0 -0
  94. data/.github/ISSUE_TEMPLATE.md +0 -2
  95. data/lib/karafka/base_controller.rb +0 -60
  96. data/lib/karafka/connection/consumer.rb +0 -121
  97. data/lib/karafka/connection/processor.rb +0 -61
  98. data/lib/karafka/controllers/callbacks.rb +0 -54
  99. data/lib/karafka/controllers/includer.rb +0 -51
  100. data/lib/karafka/controllers/responders.rb +0 -19
  101. data/lib/karafka/loader.rb +0 -29
  102. data/lib/karafka/logger.rb +0 -53
  103. data/lib/karafka/monitor.rb +0 -98
  104. data/lib/karafka/parsers/json.rb +0 -38
  105. data/lib/karafka/patches/dry_configurable.rb +0 -31
  106. data/lib/karafka/persistence/consumer.rb +0 -25
  107. data/lib/karafka/persistence/controller.rb +0 -38
  108. data/lib/karafka/schemas/config.rb +0 -21
  109. data/lib/karafka/schemas/consumer_group.rb +0 -65
  110. data/lib/karafka/schemas/consumer_group_topic.rb +0 -18
  111. data/lib/karafka/schemas/responder_usage.rb +0 -39
  112. data/lib/karafka/schemas/server_cli_options.rb +0 -43
  113. data/lib/karafka/setup/configurators/base.rb +0 -35
  114. data/lib/karafka/templates/karafka.rb.example +0 -41
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Instrumentation
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
7
+ class StdoutListener
8
+ # Log levels that we use in this particular listener
9
+ USED_LOG_LEVELS = %i[
10
+ debug
11
+ info
12
+ error
13
+ fatal
14
+ ].freeze
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].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
+ error "Params deserialization error for #{event[:caller].topic} topic: #{event[:error]}"
56
+ end
57
+
58
+ # Logs errors that occurred in a listener fetch loop
59
+ # @param event [Dry::Events::Event] event details including payload
60
+ # @note It's an error as we can recover from it not a fatal
61
+ def on_connection_listener_fetch_loop_error(event)
62
+ error "Listener fetch loop error: #{event[:error]}"
63
+ end
64
+
65
+ # Logs errors that are related to the connection itself
66
+ # @param event [Dry::Events::Event] event details including payload
67
+ # @note Karafka will attempt to reconnect, so an error not a fatal
68
+ def on_connection_client_fetch_loop_error(event)
69
+ error "Client fetch loop error: #{event[:error]}"
70
+ end
71
+
72
+ # Logs info about crashed fetcher
73
+ # @param event [Dry::Events::Event] event details including payload
74
+ # @note If this happens, Karafka will shutdown as it means a critical error
75
+ # in one of the threads
76
+ def on_fetcher_call_error(event)
77
+ fatal "Fetcher crash due to an error: #{event[:error]}"
78
+ end
79
+
80
+ # Logs info about processing of a certain dataset with an inline backend
81
+ # @param event [Dry::Events::Event] event details including payload
82
+ def on_backends_inline_process(event)
83
+ count = event[:caller].send(:params_batch).to_a.size
84
+ topic = event[:caller].topic.name
85
+ time = event[:time]
86
+ info "Inline processing of topic #{topic} with #{count} messages took #{time} ms"
87
+ end
88
+
89
+ # Logs info about system signals that Karafka received
90
+ # @param event [Dry::Events::Event] event details including payload
91
+ def on_process_notice_signal(event)
92
+ info "Received #{event[:signal]} system signal"
93
+ end
94
+
95
+ # Logs info about responder usage withing a controller flow
96
+ # @param event [Dry::Events::Event] event details including payload
97
+ def on_consumers_responders_respond_with(event)
98
+ calling = event[:caller]
99
+ responder = calling.topic.responder
100
+ data = event[:data]
101
+ info "Responded from #{calling.class} using #{responder} with following data #{data}"
102
+ end
103
+
104
+ # Logs info that we're initializing Karafka app
105
+ # @param _event [Dry::Events::Event] event details including payload
106
+ def on_app_initializing(_event)
107
+ info "Initializing Karafka server #{::Process.pid}"
108
+ end
109
+
110
+ # Logs info that we're running Karafka app
111
+ # @param _event [Dry::Events::Event] event details including payload
112
+ def on_app_running(_event)
113
+ info "Running Karafka server #{::Process.pid}"
114
+ end
115
+
116
+ # Logs info that we're going to stop the Karafka server
117
+ # @param _event [Dry::Events::Event] event details including payload
118
+ def on_app_stopping(_event)
119
+ # We use a separate thread as logging can't be called from trap context
120
+ Thread.new { info "Stopping Karafka server #{::Process.pid}" }
121
+ end
122
+
123
+ # Logs an error that Karafka was unable to stop the server gracefully and it had to do a
124
+ # forced exit
125
+ # @param _event [Dry::Events::Event] event details including payload
126
+ def on_app_stopping_error(_event)
127
+ # We use a separate thread as logging can't be called from trap context
128
+ Thread.new { error "Forceful Karafka server #{::Process.pid} stop" }
129
+ end
130
+
131
+ USED_LOG_LEVELS.each do |log_level|
132
+ define_method log_level do |*args|
133
+ Karafka.logger.send(log_level, *args)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Params
5
+ module Builders
6
+ # Builder for creating metadata object based on the message or batch informations
7
+ # @note We have 2 ways of creating metadata based on the way ruby-kafka operates
8
+ module Metadata
9
+ class << self
10
+ # Creates metadata based on the kafka batch data
11
+ # @param kafka_batch [Kafka::FetchedBatch] kafka batch details
12
+ # @param topic [Karafka::Routing::Topic] topic for which we've fetched the batch
13
+ # @return [Karafka::Params::Metadata] metadata object
14
+ def from_kafka_batch(kafka_batch, topic)
15
+ Karafka::Params::Metadata
16
+ .new
17
+ .merge!(
18
+ 'batch_size' => kafka_batch.messages.count,
19
+ 'first_offset' => kafka_batch.first_offset,
20
+ 'highwater_mark_offset' => kafka_batch.highwater_mark_offset,
21
+ 'last_offset' => kafka_batch.last_offset,
22
+ 'offset_lag' => kafka_batch.offset_lag,
23
+ 'deserializer' => topic.deserializer,
24
+ 'partition' => kafka_batch.partition,
25
+ 'topic' => kafka_batch.topic,
26
+ 'unknown_last_offset' => kafka_batch.unknown_last_offset?
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Params
5
+ # Due to the fact, that we create params related objects in couple contexts / places
6
+ # plus backends can build up them their own way we have this namespace.
7
+ # It allows to isolate actual params objects from their building process that can be
8
+ # context dependent.
9
+ module Builders
10
+ # Builder for params
11
+ module Params
12
+ class << self
13
+ # @param kafka_message [Kafka::FetchedMessage] message fetched from Kafka
14
+ # @param topic [Karafka::Routing::Topic] topic for which this message was fetched
15
+ # @return [Karafka::Params::Params] params object
16
+ def from_kafka_message(kafka_message, topic)
17
+ Karafka::Params::Params
18
+ .new
19
+ .merge!(
20
+ 'create_time' => kafka_message.create_time,
21
+ 'headers' => kafka_message.headers || {},
22
+ 'is_control_record' => kafka_message.is_control_record,
23
+ 'key' => kafka_message.key,
24
+ 'offset' => kafka_message.offset,
25
+ 'deserializer' => topic.deserializer,
26
+ 'partition' => kafka_message.partition,
27
+ 'receive_time' => Time.now,
28
+ 'topic' => kafka_message.topic,
29
+ 'payload' => kafka_message.value
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Params
5
+ module Builders
6
+ # Builder for creating params batch instances
7
+ module ParamsBatch
8
+ class << self
9
+ # Creates params batch with params inside based on the incoming messages
10
+ # and the topic from which it comes
11
+ # @param kafka_messages [Array<Kafka::FetchedMessage>] raw fetched messages
12
+ # @param topic [Karafka::Routing::Topic] topic for which we're received messages
13
+ # @return [Karafka::Params::ParamsBatch<Karafka::Params::Params>] batch with params
14
+ def from_kafka_messages(kafka_messages, topic)
15
+ params_array = kafka_messages.map! do |message|
16
+ Karafka::Params::Builders::Params.from_kafka_message(message, topic)
17
+ end
18
+
19
+ Karafka::Params::ParamsBatch.new(params_array)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Params
5
+ # Simple metadata object that stores all non-message information received from Kafka cluster
6
+ # while fetching the data
7
+ class Metadata < Hash
8
+ # Attributes that should be accessible as methods as well (not only hash)
9
+ METHOD_ATTRIBUTES = %w[
10
+ batch_size
11
+ first_offset
12
+ highwater_mark_offset
13
+ last_offset
14
+ offset_lag
15
+ deserializer
16
+ partition
17
+ topic
18
+ ].freeze
19
+
20
+ private_constant :METHOD_ATTRIBUTES
21
+
22
+ METHOD_ATTRIBUTES.each do |attr|
23
+ # Defines a method call accessor to a particular hash field.
24
+ define_method(attr) do
25
+ self[attr]
26
+ end
27
+ end
28
+
29
+ # @return [Boolean] is the last offset known or unknown
30
+ def unknown_last_offset?
31
+ self['unknown_last_offset']
32
+ end
33
+ end
34
+ end
35
+ end
@@ -3,125 +3,65 @@
3
3
  module Karafka
4
4
  # Params namespace encapsulating all the logic that is directly related to params handling
5
5
  module Params
6
- # Class-wrapper for hash with indifferent access with additional lazy loading feature
7
6
  # It provides lazy loading not only until the first usage, but also allows us to skip
8
- # using parser until we execute our logic. That way we can operate with
9
- # heavy-parsing data without slowing down the whole application.
10
- class Params < HashWithIndifferentAccess
11
- # Kafka::FetchedMessage attributes that we want to use inside of params
12
- KAFKA_MESSAGE_ATTRIBUTES = %i[
13
- value
14
- partition
15
- offset
16
- key
17
- create_time
18
- ].freeze
19
-
7
+ # using deserializer until we execute our logic. That way we can operate with
8
+ # heavy-deserialization data without slowing down the whole application.
9
+ class Params < Hash
20
10
  # Params attributes that should be available via a method call invocation for Kafka
21
11
  # client compatibility.
22
12
  # Kafka passes internally Kafka::FetchedMessage object and the ruby-kafka consumer
23
13
  # uses those fields via method calls, so in order to be able to pass there our params
24
14
  # objects, have to have same api.
25
- PARAMS_METHOD_ATTRIBUTES = %i[
26
- topic
27
- partition
28
- offset
29
- key
15
+ METHOD_ATTRIBUTES = %w[
30
16
  create_time
17
+ headers
18
+ is_control_record
19
+ key
20
+ offset
21
+ deserializer
22
+ deserialized
23
+ partition
24
+ receive_time
25
+ topic
26
+ payload
31
27
  ].freeze
32
28
 
33
- class << self
34
- # We allow building instances only via the #build method
35
-
36
- # @param message [Kafka::FetchedMessage, Hash] message that we get out of Kafka
37
- # in case of building params inside main Karafka process in
38
- # Karafka::Connection::Consumer, or a hash when we retrieve data that is already parsed
39
- # @param parser [Class] parser class that we will use to unparse data
40
- # @return [Karafka::Params::Params] Karafka params object not yet used parser for
41
- # retrieving data that we've got from Kafka
42
- # @example Build params instance from a hash
43
- # Karafka::Params::Params.build({ key: 'value' }) #=> params object
44
- # @example Build params instance from a Kafka::FetchedMessage object
45
- # Karafka::Params::Params.build(message) #=> params object
46
- def build(message, parser)
47
- # Hash case happens inside backends that interchange data
48
- if message.is_a?(Hash)
49
- new(parser: parser).send(:merge!, message)
50
- else
51
- # This happens inside Kafka::FetchedProcessor
52
- new(
53
- parser: parser,
54
- parsed: false,
55
- received_at: Time.now
56
- ).tap do |instance|
57
- KAFKA_MESSAGE_ATTRIBUTES.each do |attribute|
58
- instance[attribute] = message.send(attribute)
59
- end
60
-
61
- # When we get raw messages, they might have a topic, that was modified by a
62
- # topic mapper. We need to "reverse" this change and map back to the non-modified
63
- # format, so our internal flow is not corrupted with the mapping
64
- instance[:topic] = Karafka::App.config.topic_mapper.incoming(message.topic)
65
- end
66
- end
67
- end
29
+ private_constant :METHOD_ATTRIBUTES
68
30
 
31
+ METHOD_ATTRIBUTES.each do |attr|
69
32
  # Defines a method call accessor to a particular hash field.
70
33
  # @note Won't work for complex key names that contain spaces, etc
71
34
  # @param key [Symbol] name of a field that we want to retrieve with a method call
72
35
  # @example
73
36
  # key_attr_reader :example
74
- # params.example #=> 'my example value'
75
- def key_attr_reader(key)
76
- define_method key do
77
- self[key]
78
- end
37
+ # params.example #=> 'my example payload'
38
+ define_method(attr) do
39
+ self[attr]
79
40
  end
80
41
  end
81
42
 
82
- # @return [Karafka::Params::Params] this will trigger parser execution. If we decide to
83
- # retrieve data, parser will be executed to parse data. Output of parsing will be merged
84
- # to the current object. This object will be also marked as already parsed, so we won't
85
- # parse it again.
86
- def retrieve!
87
- return self if self[:parsed]
43
+ # @return [Karafka::Params::Params] This method will trigger deserializer execution. If we
44
+ # decide to retrieve data, deserializer will be executed to get data. Output of that will
45
+ # be merged to the current object. This object will be also marked as already deserialized,
46
+ # so we won't deserialize it again.
47
+ def deserialize!
48
+ return self if self['deserialized']
88
49
 
89
- merge!(parse(delete(:value)))
50
+ self['deserialized'] = true
51
+ self['payload'] = deserialize
52
+ self
90
53
  end
91
54
 
92
- PARAMS_METHOD_ATTRIBUTES.each(&method(:key_attr_reader))
93
-
94
55
  private
95
56
 
96
- # Overwritten merge! method - it behaves differently for keys that are the same in our hash
97
- # and in a other_hash - it will not replace keys that are the same in our hash
98
- # and in the other one
99
- # @param other_hash [Hash, HashWithIndifferentAccess] hash that we want to merge into current
100
- # @return [Karafka::Params::Params] our parameters hash with merged values
101
- # @example Merge with hash without same keys
102
- # new(a: 1, b: 2).merge!(c: 3) #=> { a: 1, b: 2, c: 3 }
103
- # @example Merge with hash with same keys (symbol based)
104
- # new(a: 1).merge!(a: 2) #=> { a: 1 }
105
- # @example Merge with hash with same keys (string based)
106
- # new(a: 1).merge!('a' => 2) #=> { a: 1 }
107
- # @example Merge with hash with same keys (current string based)
108
- # new('a' => 1).merge!(a: 2) #=> { a: 1 }
109
- def merge!(other_hash)
110
- super(other_hash) { |_key, base_value, _new_value| base_value }
111
- end
112
-
113
- # @param value [String] Raw data that we want to parse using controller's parser
114
- # @note If something goes wrong, it will return raw data in a hash with a message key
115
- # @return [Hash] parsed data or a hash with message key containing raw data if something
116
- # went wrong during parsing
117
- def parse(value)
118
- self[:parser].parse(value)
119
- # We catch both of them, because for default JSON - we use JSON parser directly
120
- rescue ::Karafka::Errors::ParserError => e
121
- Karafka.monitor.notice_error(self.class, e)
57
+ # @return [Object] deserialized data
58
+ def deserialize
59
+ Karafka.monitor.instrument('params.params.deserialize', caller: self) do
60
+ self['deserializer'].call(self)
61
+ end
62
+ rescue ::StandardError => e
63
+ Karafka.monitor.instrument('params.params.deserialize.error', caller: self, error: e)
122
64
  raise e
123
- ensure
124
- self[:parsed] = true
125
65
  end
126
66
  end
127
67
  end
@@ -3,38 +3,58 @@
3
3
  module Karafka
4
4
  module Params
5
5
  # Params batch represents a set of messages received from Kafka.
6
- # @note Params internally are lazy loaded before first use. That way we can skip parsing
7
- # process if we have after_fetched that rejects some incoming messages without using params
8
- # It can be also used when handling really heavy data (in terms of parsing).
6
+ # @note Params internally are lazy loaded before first use. That way we can skip
7
+ # deserialization process if we have after_fetch that rejects some incoming messages
8
+ # without using params It can be also used when handling really heavy data.
9
9
  class ParamsBatch
10
10
  include Enumerable
11
11
 
12
- # Builds up a params batch based on raw kafka messages
13
- # @param messages_batch [Array<Kafka::FetchedMessage>] messages batch
14
- # @param topic_parser [Class] topic parser for unparsing messages values
15
- def initialize(messages_batch, topic_parser)
16
- @params_batch = messages_batch.map do |message|
17
- Karafka::Params::Params.build(message, topic_parser)
18
- end
12
+ # @param params_array [Array<Karafka::Params::Params>] array with karafka params
13
+ # @return [Karafka::Params::ParamsBatch] lazy evaluated params batch object
14
+ def initialize(params_array)
15
+ @params_array = params_array
19
16
  end
20
17
 
21
- # @yieldparam [Karafka::Params::Params] each parsed and loaded params instance
22
- # @note Invocation of this method will cause loading and parsing each param after another.
23
- # If you want to get access without parsing, please access params_batch directly
18
+ # @yieldparam [Karafka::Params::Params] each deserialized and loaded params instance
19
+ # @note Invocation of this method will cause loading and deserializing each param after
20
+ # another. If you want to get access without deserializing, please access params_array
21
+ # directly
24
22
  def each
25
- @params_batch.each { |param| yield(param.retrieve!) }
23
+ @params_array.each { |param| yield(param.deserialize!) }
26
24
  end
27
25
 
28
26
  # @return [Array<Karafka::Params::Params>] returns all the params in a loaded state, so they
29
27
  # can be used for batch insert, etc. Without invoking all, up until first use, they won't
30
- # be parsed
31
- def parsed
28
+ # be deserialized
29
+ def deserialize!
32
30
  each(&:itself)
33
31
  end
34
32
 
35
- # @return [Array<Karafka::Params::Params>] pure array with params (not parsed)
33
+ # @return [Array<Object>] array with deserialized payloads. This method can be useful when
34
+ # we don't care about metadata and just want to extract all the data payloads from the
35
+ # batch
36
+ def payloads
37
+ deserialize!.map(&:payload)
38
+ end
39
+
40
+ # @return [Karafka::Params::Params] first element after the deserialization process
41
+ def first
42
+ @params_array.first.deserialize!
43
+ end
44
+
45
+ # @return [Karafka::Params::Params] last element after the deserialization process
46
+ def last
47
+ @params_array.last.deserialize!
48
+ end
49
+
50
+ # @return [Array<Karafka::Params::Params>] pure array with params (not deserialized)
36
51
  def to_a
37
- @params_batch
52
+ @params_array
53
+ end
54
+
55
+ # @return [Integer] number of messages in the batch
56
+ def size
57
+ @params_array.size
38
58
  end
39
59
  end
40
60
  end