karafka 1.2.8 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.coditsu/ci.yml +3 -0
  5. data/.console_irbrc +1 -3
  6. data/.diffend.yml +3 -0
  7. data/.github/FUNDING.yml +3 -0
  8. data/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
  9. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  10. data/.github/workflows/ci.yml +52 -0
  11. data/.gitignore +1 -0
  12. data/.ruby-version +1 -1
  13. data/CHANGELOG.md +134 -14
  14. data/CODE_OF_CONDUCT.md +1 -1
  15. data/CONTRIBUTING.md +1 -1
  16. data/Gemfile +4 -5
  17. data/Gemfile.lock +92 -81
  18. data/README.md +9 -12
  19. data/bin/karafka +1 -1
  20. data/certs/mensfeld.pem +25 -0
  21. data/config/errors.yml +38 -5
  22. data/docker-compose.yml +17 -0
  23. data/karafka.gemspec +18 -17
  24. data/lib/karafka.rb +10 -16
  25. data/lib/karafka/app.rb +14 -6
  26. data/lib/karafka/attributes_map.rb +5 -10
  27. data/lib/karafka/base_consumer.rb +19 -30
  28. data/lib/karafka/base_responder.rb +45 -27
  29. data/lib/karafka/cli.rb +2 -2
  30. data/lib/karafka/cli/console.rb +11 -9
  31. data/lib/karafka/cli/flow.rb +9 -7
  32. data/lib/karafka/cli/info.rb +4 -2
  33. data/lib/karafka/cli/install.rb +30 -6
  34. data/lib/karafka/cli/server.rb +11 -6
  35. data/lib/karafka/code_reloader.rb +67 -0
  36. data/lib/karafka/connection/api_adapter.rb +22 -9
  37. data/lib/karafka/connection/batch_delegator.rb +55 -0
  38. data/lib/karafka/connection/builder.rb +5 -3
  39. data/lib/karafka/connection/client.rb +31 -31
  40. data/lib/karafka/connection/listener.rb +26 -15
  41. data/lib/karafka/connection/message_delegator.rb +36 -0
  42. data/lib/karafka/consumers/batch_metadata.rb +10 -0
  43. data/lib/karafka/consumers/callbacks.rb +32 -15
  44. data/lib/karafka/consumers/includer.rb +31 -18
  45. data/lib/karafka/consumers/responders.rb +2 -2
  46. data/lib/karafka/contracts.rb +10 -0
  47. data/lib/karafka/contracts/config.rb +21 -0
  48. data/lib/karafka/contracts/consumer_group.rb +206 -0
  49. data/lib/karafka/contracts/consumer_group_topic.rb +19 -0
  50. data/lib/karafka/contracts/responder_usage.rb +54 -0
  51. data/lib/karafka/contracts/server_cli_options.rb +31 -0
  52. data/lib/karafka/errors.rb +17 -16
  53. data/lib/karafka/fetcher.rb +28 -30
  54. data/lib/karafka/helpers/class_matcher.rb +12 -2
  55. data/lib/karafka/helpers/config_retriever.rb +1 -1
  56. data/lib/karafka/helpers/inflector.rb +26 -0
  57. data/lib/karafka/helpers/multi_delegator.rb +0 -1
  58. data/lib/karafka/instrumentation/logger.rb +9 -6
  59. data/lib/karafka/instrumentation/monitor.rb +15 -9
  60. data/lib/karafka/instrumentation/proctitle_listener.rb +36 -0
  61. data/lib/karafka/instrumentation/stdout_listener.rb +140 -0
  62. data/lib/karafka/params/batch_metadata.rb +26 -0
  63. data/lib/karafka/params/builders/batch_metadata.rb +30 -0
  64. data/lib/karafka/params/builders/params.rb +38 -0
  65. data/lib/karafka/params/builders/params_batch.rb +25 -0
  66. data/lib/karafka/params/metadata.rb +20 -0
  67. data/lib/karafka/params/params.rb +54 -0
  68. data/lib/karafka/params/params_batch.rb +35 -21
  69. data/lib/karafka/patches/ruby_kafka.rb +21 -8
  70. data/lib/karafka/persistence/client.rb +15 -11
  71. data/lib/karafka/persistence/{consumer.rb → consumers.rb} +20 -13
  72. data/lib/karafka/persistence/topics.rb +48 -0
  73. data/lib/karafka/process.rb +0 -2
  74. data/lib/karafka/responders/builder.rb +1 -1
  75. data/lib/karafka/responders/topic.rb +6 -8
  76. data/lib/karafka/routing/builder.rb +36 -8
  77. data/lib/karafka/routing/consumer_group.rb +1 -1
  78. data/lib/karafka/routing/consumer_mapper.rb +9 -9
  79. data/lib/karafka/routing/proxy.rb +10 -1
  80. data/lib/karafka/routing/topic.rb +5 -3
  81. data/lib/karafka/routing/topic_mapper.rb +16 -18
  82. data/lib/karafka/serialization/json/deserializer.rb +27 -0
  83. data/lib/karafka/serialization/json/serializer.rb +31 -0
  84. data/lib/karafka/server.rb +29 -28
  85. data/lib/karafka/setup/config.rb +67 -37
  86. data/lib/karafka/setup/configurators/water_drop.rb +7 -3
  87. data/lib/karafka/setup/dsl.rb +0 -1
  88. data/lib/karafka/status.rb +7 -3
  89. data/lib/karafka/templates/{application_consumer.rb.example → application_consumer.rb.erb} +2 -1
  90. data/lib/karafka/templates/{application_responder.rb.example → application_responder.rb.erb} +0 -0
  91. data/lib/karafka/templates/karafka.rb.erb +92 -0
  92. data/lib/karafka/version.rb +1 -1
  93. metadata +94 -72
  94. metadata.gz.sig +0 -0
  95. data/.travis.yml +0 -21
  96. data/lib/karafka/callbacks.rb +0 -30
  97. data/lib/karafka/callbacks/config.rb +0 -22
  98. data/lib/karafka/callbacks/dsl.rb +0 -16
  99. data/lib/karafka/connection/delegator.rb +0 -46
  100. data/lib/karafka/instrumentation/listener.rb +0 -112
  101. data/lib/karafka/loader.rb +0 -28
  102. data/lib/karafka/params/dsl.rb +0 -156
  103. data/lib/karafka/parsers/json.rb +0 -38
  104. data/lib/karafka/patches/dry_configurable.rb +0 -35
  105. data/lib/karafka/persistence/topic.rb +0 -29
  106. data/lib/karafka/schemas/config.rb +0 -24
  107. data/lib/karafka/schemas/consumer_group.rb +0 -78
  108. data/lib/karafka/schemas/consumer_group_topic.rb +0 -18
  109. data/lib/karafka/schemas/responder_usage.rb +0 -39
  110. data/lib/karafka/schemas/server_cli_options.rb +0 -43
  111. data/lib/karafka/setup/configurators/base.rb +0 -29
  112. data/lib/karafka/setup/configurators/params.rb +0 -25
  113. data/lib/karafka/templates/karafka.rb.example +0 -54
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Contracts
5
+ # Consumer group topic validation rules
6
+ class ConsumerGroupTopic < Dry::Validation::Contract
7
+ params do
8
+ required(:id).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
9
+ required(:name).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
10
+ required(:backend).filled(included_in?: %i[inline sidekiq])
11
+ required(:consumer).filled
12
+ required(:deserializer).filled
13
+ required(:max_bytes_per_partition).filled(:int?, gteq?: 0)
14
+ required(:start_from_beginning).filled(:bool?)
15
+ required(:batch_consuming).filled(:bool?)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Contracts
5
+ # Validator to check responder topic usage
6
+ class ResponderUsageTopic < Dry::Validation::Contract
7
+ config.messages.load_paths << File.join(Karafka.gem_root, 'config', 'errors.yml')
8
+
9
+ params do
10
+ required(:name).filled(:str?, format?: Karafka::Contracts::TOPIC_REGEXP)
11
+ required(:required).filled(:bool?)
12
+ required(:usage_count).filled(:int?, gteq?: 0)
13
+ required(:registered).filled(eql?: true)
14
+ required(:async).filled(:bool?)
15
+ required(:serializer).filled
16
+ end
17
+
18
+ rule(:required, :usage_count) do
19
+ key(:name).failure(:required_usage_count) if values[:required] && values[:usage_count] < 1
20
+ end
21
+ end
22
+
23
+ # Validator to check that everything in a responder flow matches responder rules
24
+ class ResponderUsage < Dry::Validation::Contract
25
+ include Dry::Core::Constants
26
+
27
+ # Contract for verifying the topic usage details
28
+ TOPIC_CONTRACT = ResponderUsageTopic.new.freeze
29
+
30
+ private_constant :TOPIC_CONTRACT
31
+
32
+ params do
33
+ required(:used_topics)
34
+ required(:registered_topics)
35
+ end
36
+
37
+ rule(:used_topics) do
38
+ (value || EMPTY_ARRAY).each do |used_topic|
39
+ TOPIC_CONTRACT.call(used_topic).errors.each do |error|
40
+ key([:used_topics, used_topic, error.path[0]]).failure(error.text)
41
+ end
42
+ end
43
+ end
44
+
45
+ rule(:registered_topics) do
46
+ (value || EMPTY_ARRAY).each do |used_topic|
47
+ TOPIC_CONTRACT.call(used_topic).errors.each do |error|
48
+ key([:registered_topics, used_topic, error.path[0]]).failure(error.text)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Contracts
5
+ # Contract for validating correctness of the server cli command options
6
+ # We validate some basics + the list of consumer_groups on which we want to use, to make
7
+ # sure that all of them are defined, plus that a pidfile does not exist
8
+ class ServerCliOptions < Dry::Validation::Contract
9
+ config.messages.load_paths << File.join(Karafka.gem_root, 'config', 'errors.yml')
10
+
11
+ params do
12
+ optional(:pid).filled(:str?)
13
+ optional(:daemon).filled(:bool?)
14
+ optional(:consumer_groups).value(:array, :filled?)
15
+ end
16
+
17
+ rule(:pid) do
18
+ key(:pid).failure(:pid_already_exists) if value && File.exist?(value)
19
+ end
20
+
21
+ rule(:consumer_groups) do
22
+ # If there were no consumer_groups declared in the server cli, it means that we will
23
+ # run all of them and no need to validate them here at all
24
+ if !value.nil? &&
25
+ !(value - Karafka::App.config.internal.routing_builder.map(&:name)).empty?
26
+ key(:consumer_groups).failure(:consumer_groups_inclusion)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -6,17 +6,18 @@ module Karafka
6
6
  # Base class for all the Karafka internal errors
7
7
  BaseError = Class.new(StandardError)
8
8
 
9
- # Should be raised when we attemp to parse incoming params but parsing fails
10
- # If this error (or its descendant) is detected, we will pass the raw message
11
- # into params and proceed further
12
- ParserError = Class.new(BaseError)
9
+ # Should be raised when we have that that we cannot serialize
10
+ SerializationError = Class.new(BaseError)
11
+
12
+ # Should be raised when we tried to deserialize incoming data but we failed
13
+ DeserializationError = Class.new(BaseError)
13
14
 
14
15
  # Raised when router receives topic name which does not correspond with any routes
15
16
  # This can only happen in a case when:
16
17
  # - you've received a message and we cannot match it with a consumer
17
18
  # - you've changed the routing, so router can no longer associate your topic to
18
19
  # any consumer
19
- # - or in a case when you do a lot of metaprogramming and you change routing/etc on runtime
20
+ # - or in a case when you do a lot of meta-programming and you change routing/etc on runtime
20
21
  #
21
22
  # In case this happens, you will have to create a temporary route that will allow
22
23
  # you to "eat" everything from the Sidekiq queue.
@@ -25,26 +26,26 @@ module Karafka
25
26
 
26
27
  # Raised when we don't use or use responder not in the way it expected to based on the
27
28
  # topics usage definitions
28
- InvalidResponderUsage = Class.new(BaseError)
29
+ InvalidResponderUsageError = Class.new(BaseError)
29
30
 
30
- # Raised when options that we provide to the responder to respond aren't what the schema
31
+ # Raised when options that we provide to the responder to respond aren't what the contract
31
32
  # requires
32
- InvalidResponderMessageOptions = Class.new(BaseError)
33
+ InvalidResponderMessageOptionsError = Class.new(BaseError)
33
34
 
34
- # Raised when configuration doesn't match with validation schema
35
- InvalidConfiguration = Class.new(BaseError)
35
+ # Raised when configuration doesn't match with validation contract
36
+ InvalidConfigurationError = Class.new(BaseError)
36
37
 
37
- # Raised when we try to use Karafka CLI commands (except install) without a bootfile
38
- MissingBootFile = Class.new(BaseError)
38
+ # Raised when we try to use Karafka CLI commands (except install) without a boot file
39
+ MissingBootFileError = Class.new(BaseError)
39
40
 
40
41
  # Raised when we want to read a persisted thread messages consumer but it is unavailable
41
42
  # This should never happen and if it does, please contact us
42
- MissingClient = Class.new(BaseError)
43
+ MissingClientError = Class.new(BaseError)
43
44
 
44
45
  # Raised when want to hook up to an event that is not registered and supported
45
- UnregisteredMonitorEvent = Class.new(BaseError)
46
+ UnregisteredMonitorEventError = Class.new(BaseError)
46
47
 
47
- # Raised when we've waited enough for shutting down an unresponding process
48
- ForcefulShutdown = Class.new(BaseError)
48
+ # Raised when we've waited enough for shutting down a non-responsive process
49
+ ForcefulShutdownError = Class.new(BaseError)
49
50
  end
50
51
  end
@@ -5,39 +5,37 @@ module Karafka
5
5
  # @note Creating multiple fetchers will result in having multiple connections to the same
6
6
  # topics, which means that if there are no partitions, it won't use them.
7
7
  class Fetcher
8
- class << self
9
- # Starts listening on all the listeners asynchronously
10
- # Fetch loop should never end, which means that we won't create more actor clusters
11
- # so we don't have to terminate them
12
- def call
13
- threads = listeners.map do |listener|
14
- # We abort on exception because there should be an exception handling developed for
15
- # each listener running in separate threads, so the exceptions should never leak
16
- # and if that happens, it means that something really bad happened and we should stop
17
- # the whole process
18
- Thread
19
- .new { listener.call }
20
- .tap { |thread| thread.abort_on_exception = true }
21
- end
22
-
23
- # We aggregate threads here for a supervised shutdown process
24
- threads.each { |thread| Karafka::Server.consumer_threads << thread }
25
- threads.each(&:join)
26
- # If anything crashes here, we need to raise the error and crush the runner because it means
27
- # that something terrible happened
28
- rescue StandardError => e
29
- Karafka.monitor.instrument('fetcher.call.error', caller: self, error: e)
30
- Karafka::App.stop!
31
- raise e
8
+ # Starts listening on all the listeners asynchronously
9
+ # Fetch loop should never end, which means that we won't create more actor clusters
10
+ # so we don't have to terminate them
11
+ def call
12
+ threads = listeners.map do |listener|
13
+ # We abort on exception because there should be an exception handling developed for
14
+ # each listener running in separate threads, so the exceptions should never leak
15
+ # and if that happens, it means that something really bad happened and we should stop
16
+ # the whole process
17
+ Thread
18
+ .new { listener.call }
19
+ .tap { |thread| thread.abort_on_exception = true }
32
20
  end
33
21
 
34
- private
22
+ # We aggregate threads here for a supervised shutdown process
23
+ threads.each { |thread| Karafka::Server.consumer_threads << thread }
24
+ threads.each(&:join)
25
+ # If anything crashes here, we need to raise the error and crush the runner because it means
26
+ # that something terrible happened
27
+ rescue StandardError => e
28
+ Karafka.monitor.instrument('fetcher.call.error', caller: self, error: e)
29
+ Karafka::App.stop!
30
+ raise e
31
+ end
32
+
33
+ private
35
34
 
36
- # @return [Array<Karafka::Connection::Listener>] listeners that will consume messages
37
- def listeners
38
- @listeners ||= App.consumer_groups.active.map do |consumer_group|
39
- Karafka::Connection::Listener.new(consumer_group)
40
- end
35
+ # @return [Array<Karafka::Connection::Listener>] listeners that will consume messages
36
+ def listeners
37
+ @listeners ||= App.consumer_groups.active.map do |consumer_group|
38
+ Karafka::Connection::Listener.new(consumer_group)
41
39
  end
42
40
  end
43
41
  end
@@ -8,7 +8,9 @@ module Karafka
8
8
  class ClassMatcher
9
9
  # Regexp used to remove any non classy like characters that might be in the consumer
10
10
  # class name (if defined dynamically, etc)
11
- CONSTANT_REGEXP = %r{[?!=+\-\*/\^\|&\[\]<>%~\#\:\s\(\)]}
11
+ CONSTANT_REGEXP = %r{[?!=+\-\*/\^\|&\[\]<>%~\#\:\s\(\)]}.freeze
12
+
13
+ private_constant :CONSTANT_REGEXP
12
14
 
13
15
  # @param klass [Class] class to which we want to find a corresponding class
14
16
  # @param from [String] what type of object is it (based on postfix name part)
@@ -30,6 +32,7 @@ module Karafka
30
32
  def match
31
33
  return nil if name.empty?
32
34
  return nil unless scope.const_defined?(name)
35
+
33
36
  matching = scope.const_get(name)
34
37
  same_scope?(matching) ? matching : nil
35
38
  end
@@ -41,7 +44,13 @@ module Karafka
41
44
  # @example From Namespaced::Super2Consumer matching responder
42
45
  # matcher.name #=> Super2Responder
43
46
  def name
44
- inflected = @klass.to_s.split('::').last.to_s
47
+ inflected = +@klass.to_s.split('::').last.to_s
48
+ # We inject the from into the name just in case it is missing as in a situation like
49
+ # that it would just sanitize the name without adding the "to" postfix.
50
+ # It could create cases when we want to build for example a responder to a consumer
51
+ # that does not have the "Consumer" postfix and would do nothing returning the same name.
52
+ # That would be bad as the matching classes shouldn't be matched to themselves.
53
+ inflected << @from unless inflected.include?(@from)
45
54
  inflected.gsub!(@from, @to)
46
55
  inflected.gsub!(CONSTANT_REGEXP, '')
47
56
  inflected
@@ -65,6 +74,7 @@ module Karafka
65
74
  def scope_of(klass)
66
75
  enclosing = klass.to_s.split('::')[0...-1]
67
76
  return ::Object if enclosing.empty?
77
+
68
78
  ::Object.const_get(enclosing.join('::'))
69
79
  end
70
80
 
@@ -5,7 +5,7 @@ module Karafka
5
5
  # A helper method that allows us to build methods that try to get a given
6
6
  # attribute from its instance value and if it fails, will fallback to
7
7
  # the default config or config.kafka value for a given attribute.
8
- # It is used to simplify the checkings.
8
+ # It is used to simplify the checks.
9
9
  # @note Worth noticing, that the value might be equal to false, so even
10
10
  # then we need to return it. That's why we check for nil?
11
11
  # @example Define config retried attribute for start_from_beginning
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Helpers
5
+ # Inflector provides inflection for the whole Karafka framework with additional inflection
6
+ # caching (due to the fact, that Dry::Inflector is slow)
7
+ module Inflector
8
+ # What inflection engine do we want to use
9
+ ENGINE = Dry::Inflector.new
10
+
11
+ @map = Concurrent::Hash.new
12
+
13
+ private_constant :ENGINE
14
+
15
+ class << self
16
+ # @param string [String] string that we want to convert to our underscore format
17
+ # @return [String] inflected string
18
+ # @example
19
+ # Karafka::Helpers::Inflector.map('Module/ControllerName') #=> 'module_controller_name'
20
+ def map(string)
21
+ @map[string] ||= ENGINE.underscore(string).tr('/', '_')
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -7,7 +7,6 @@ module Karafka
7
7
  # Multidelegator is used to delegate calls to multiple targets
8
8
  class MultiDelegator
9
9
  # @param targets to which we want to delegate methods
10
- #
11
10
  def initialize(*targets)
12
11
  @targets = targets
13
12
  end
@@ -5,8 +5,6 @@ module Karafka
5
5
  # Default logger for Event Delegator
6
6
  # @note It uses ::Logger features - providing basic logging
7
7
  class Logger < ::Logger
8
- include Singleton
9
-
10
8
  # Map containing information about log level for given environment
11
9
  ENV_MAP = {
12
10
  'production' => ::Logger::ERROR,
@@ -16,7 +14,11 @@ module Karafka
16
14
  'default' => ::Logger::INFO
17
15
  }.freeze
18
16
 
17
+ private_constant :ENV_MAP
18
+
19
19
  # Creates a new instance of logger ensuring that it has a place to write to
20
+ # @param _args Any arguments that we don't care about but that are needed in order to
21
+ # make this logger compatible with the default Ruby one
20
22
  def initialize(*_args)
21
23
  ensure_dir_exists
22
24
  super(target)
@@ -26,7 +28,7 @@ module Karafka
26
28
  private
27
29
 
28
30
  # @return [Karafka::Helpers::MultiDelegator] multi delegator instance
29
- # to which we will be writtng logs
31
+ # to which we will be writing logs
30
32
  # We use this approach to log stuff to file and to the STDOUT at the same time
31
33
  def target
32
34
  Karafka::Helpers::MultiDelegator
@@ -34,10 +36,11 @@ module Karafka
34
36
  .to(STDOUT, file)
35
37
  end
36
38
 
37
- # Makes sure the log directory exists
39
+ # Makes sure the log directory exists as long as we can write to it
38
40
  def ensure_dir_exists
39
- dir = File.dirname(log_path)
40
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
41
+ FileUtils.mkdir_p(File.dirname(log_path))
42
+ rescue Errno::EACCES
43
+ nil
41
44
  end
42
45
 
43
46
  # @return [Pathname] Path to a file to which we should log
@@ -6,13 +6,11 @@ module Karafka
6
6
  # Monitor is used to hookup external monitoring services to monitor how Karafka works
7
7
  # It provides a standardized API for checking incoming messages/enqueueing etc
8
8
  # Since it is a pub-sub based on dry-monitor, you can use as many subscribers/loggers at the
9
- # same time, which means that you might have for example file logging and newrelic at the same
9
+ # same time, which means that you might have for example file logging and NewRelic at the same
10
10
  # time
11
11
  # @note This class acts as a singleton because we are only permitted to have single monitor
12
12
  # per running process (just as logger)
13
13
  class Monitor < Dry::Monitor::Notifications
14
- include Singleton
15
-
16
14
  # List of events that we support in the system and to which a monitor client can hook up
17
15
  # @note The non-error once support timestamp benchmarking
18
16
  # @note Depending on Karafka extensions and additional engines, this might not be the
@@ -21,11 +19,14 @@ module Karafka
21
19
  # Last 4 events are from WaterDrop but for convenience we use the same monitor for the
22
20
  # whole karafka ecosystem
23
21
  BASE_EVENTS = %w[
24
- params.params.parse
25
- params.params.parse.error
22
+ params.params.deserialize
23
+ params.params.deserialize.error
24
+ connection.listener.before_fetch_loop
25
+ connection.listener.fetch_loop
26
26
  connection.listener.fetch_loop.error
27
27
  connection.client.fetch_loop.error
28
- connection.delegator.call
28
+ connection.batch_delegator.call
29
+ connection.message_delegator.call
29
30
  fetcher.call.error
30
31
  backends.inline.process
31
32
  process.notice_signal
@@ -34,8 +35,12 @@ module Karafka
34
35
  async_producer.call.retry
35
36
  sync_producer.call.error
36
37
  sync_producer.call.retry
37
- server.stop
38
- server.stop.error
38
+ app.initializing
39
+ app.initialized
40
+ app.running
41
+ app.stopping
42
+ app.stopping.error
43
+ app.stopped
39
44
  ].freeze
40
45
 
41
46
  private_constant :BASE_EVENTS
@@ -52,7 +57,8 @@ module Karafka
52
57
  def subscribe(event_name_or_listener)
53
58
  return super unless event_name_or_listener.is_a?(String)
54
59
  return super if available_events.include?(event_name_or_listener)
55
- raise Errors::UnregisteredMonitorEvent, event_name_or_listener
60
+
61
+ raise Errors::UnregisteredMonitorEventError, event_name_or_listener
56
62
  end
57
63
 
58
64
  # @return [Array<String>] names of available events to which we can subscribe
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Instrumentation
5
+ # Listener that sets a proc title with a nice descriptive value
6
+ class ProctitleListener
7
+ # Updates proc title to an initializing one
8
+ # @param _event [Dry::Events::Event] event details including payload
9
+ def on_app_initializing(_event)
10
+ setproctitle('initializing')
11
+ end
12
+
13
+ # Updates proc title to a running one
14
+ # @param _event [Dry::Events::Event] event details including payload
15
+ def on_app_running(_event)
16
+ setproctitle('running')
17
+ end
18
+
19
+ # Updates proc title to a stopping one
20
+ # @param _event [Dry::Events::Event] event details including payload
21
+ def on_app_stopping(_event)
22
+ setproctitle('stopping')
23
+ end
24
+
25
+ private
26
+
27
+ # Sets a proper proc title with our constant prefix
28
+ # @param status [String] any status we want to set
29
+ def setproctitle(status)
30
+ ::Process.setproctitle(
31
+ "karafka #{Karafka::App.config.client_id} (#{status})"
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end