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
data/karafka.gemspec CHANGED
@@ -5,28 +5,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  require 'karafka/version'
7
7
 
8
- # rubocop:disable Metrics/BlockLength
9
8
  Gem::Specification.new do |spec|
10
9
  spec.name = 'karafka'
11
10
  spec.version = ::Karafka::VERSION
12
11
  spec.platform = Gem::Platform::RUBY
13
- spec.authors = ['Maciej Mensfeld', 'Pavlo Vavruk', 'Adam Gwozdowski']
14
- spec.email = %w[maciej@mensfeld.pl pavlo.vavruk@gmail.com adam99g@gmail.com]
15
- spec.homepage = 'https://github.com/karafka/karafka'
12
+ spec.authors = ['Maciej Mensfeld']
13
+ spec.email = %w[maciej@mensfeld.pl]
14
+ spec.homepage = 'https://karafka.io'
16
15
  spec.summary = 'Ruby based framework for working with Apache Kafka'
17
16
  spec.description = 'Framework used to simplify Apache Kafka based Ruby applications development'
18
- spec.license = 'MIT'
19
-
20
- spec.add_dependency 'dry-configurable', '~> 0.8'
21
- spec.add_dependency 'dry-inflector', '~> 0.1'
22
- spec.add_dependency 'dry-monitor', '~> 0.3'
23
- spec.add_dependency 'dry-validation', '~> 1.2'
24
- spec.add_dependency 'envlogic', '~> 1.1'
25
- spec.add_dependency 'irb', '~> 1.0'
26
- spec.add_dependency 'ruby-kafka', '>= 1.3.0'
17
+ spec.license = 'LGPL-3.0'
18
+
19
+ spec.add_dependency 'dry-configurable', '~> 0.13'
20
+ spec.add_dependency 'dry-monitor', '~> 0.5'
21
+ spec.add_dependency 'dry-validation', '~> 1.7'
22
+ spec.add_dependency 'rdkafka', '>= 0.10'
27
23
  spec.add_dependency 'thor', '>= 0.20'
28
- spec.add_dependency 'waterdrop', '~> 1.4.0'
29
- spec.add_dependency 'zeitwerk', '~> 2.1'
24
+ spec.add_dependency 'waterdrop', '>= 2.1.0', '< 3.0.0'
25
+ spec.add_dependency 'zeitwerk', '~> 2.3'
30
26
 
31
27
  spec.required_ruby_version = '>= 2.6.0'
32
28
 
@@ -36,7 +32,11 @@ Gem::Specification.new do |spec|
36
32
 
37
33
  spec.cert_chain = %w[certs/mensfeld.pem]
38
34
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
39
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
35
+ spec.executables = %w[karafka]
40
36
  spec.require_paths = %w[lib]
37
+
38
+ spec.metadata = {
39
+ 'source_code_uri' => 'https://github.com/karafka/karafka',
40
+ 'rubygems_mfa_required' => 'true'
41
+ }
41
42
  end
42
- # rubocop:enable Metrics/BlockLength
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ # This is the consumer for ActiveJob that eats the messages enqueued with it one after another.
5
+ # It marks the offset after each message, so we make sure, none of the jobs is executed twice
6
+ class Consumer < Karafka::BaseConsumer
7
+ # Executes the ActiveJob logic
8
+ # @note ActiveJob does not support batches, so we just run one message after another
9
+ def consume
10
+ messages.each do |message|
11
+ ActiveJob::Base.execute(
12
+ # We technically speaking could set this as deserializer and reference it from the
13
+ # message instead of using the `#raw_payload`. This is not done on purpose to simplify
14
+ # the ActiveJob setup here
15
+ ActiveSupport::JSON.decode(message.raw_payload)
16
+ )
17
+
18
+ mark_as_consumed(message)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_job'
4
+ require 'active_job/queue_adapters'
5
+ require 'active_job/consumer'
6
+ require 'active_job/routing_extensions'
7
+ require 'active_job/queue_adapters/karafka_adapter'
8
+
9
+ module ActiveJob
10
+ # Namespace for usage simplification outside of Rails where Railtie will not kick in.
11
+ # That way a require 'active_job/karafka' should be enough to use it
12
+ module Karafka
13
+ end
14
+ end
15
+
16
+ # We extend routing builder by adding a simple wrapper for easier jobs topics defining
17
+ ::Karafka::Routing::Builder.include ActiveJob::RoutingExtensions
18
+ ::Karafka::Routing::Proxy.include ActiveJob::RoutingExtensions
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ActiveJob components to allow for jobs consumption with Karafka
4
+ module ActiveJob
5
+ # ActiveJob queue adapters
6
+ module QueueAdapters
7
+ # Karafka adapter for enqueuing jobs
8
+ class KarafkaAdapter
9
+ # Enqueues the job by sending all the payload to a dedicated topic in Kafka that will be
10
+ # later on consumed by a special ActiveJob consumer
11
+ #
12
+ # @param job [Object] job that should be enqueued
13
+ def enqueue(job)
14
+ ::Karafka.producer.produce_async(
15
+ topic: job.queue_name,
16
+ payload: ActiveSupport::JSON.encode(job.serialize)
17
+ )
18
+ end
19
+
20
+ # Raises info, that Karafka backend does not support scheduling jobs
21
+ #
22
+ # @param _job [Object] job we cannot enqueue
23
+ # @param _timestamp [Time] time when job should run
24
+ def enqueue_at(_job, _timestamp)
25
+ raise NotImplementedError, 'This queueing backend does not support scheduling jobs.'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ # Routing extensions for ActiveJob
5
+ module RoutingExtensions
6
+ # This method simplifies routes definition for ActiveJob topics / queues by auto-injecting the
7
+ # consumer class
8
+ # @param name [String, Symbol] name of the topic where ActiveJobs jobs should go
9
+ def active_job_topic(name)
10
+ topic(name) do
11
+ consumer ActiveJob::Consumer
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/karafka/app.rb CHANGED
@@ -6,31 +6,23 @@ module Karafka
6
6
  extend Setup::Dsl
7
7
 
8
8
  class << self
9
- # Sets up all the internal components and bootstrap whole app
10
- # We need to know details about consumers in order to setup components,
11
- # that's why we don't setup them after std setup is done
12
- # @raise [Karafka::Errors::InvalidConfigurationError] raised when configuration
13
- # doesn't match with the config contract
14
- def boot!
15
- initialize!
16
- Setup::Config.validate!
17
- Setup::Config.setup_components
18
- initialized!
19
- end
20
-
21
- # @return [Karafka::Routing::Builder] consumers builder instance
9
+ # @return [Karafka::Routing::Builder] consumers builder instance alias
22
10
  def consumer_groups
23
- config.internal.routing_builder
11
+ config
12
+ .internal
13
+ .routing_builder
24
14
  end
25
15
 
26
- # Triggers reload of all cached Karafka app components, so we can use in-process
27
- # in-development hot code reloading without Karafka process restart
28
- def reload
29
- Karafka::Persistence::Consumers.clear
30
- Karafka::Persistence::Topics.clear
31
- config.internal.routing_builder.reload
16
+ # @return [Array<Karafka::Routing::SubscriptionGroup>] active subscription groups
17
+ def subscription_groups
18
+ consumer_groups
19
+ .active
20
+ .flat_map(&:subscription_groups)
32
21
  end
33
22
 
23
+ # Just a nicer name for the consumer groups
24
+ alias routes consumer_groups
25
+
34
26
  Status.instance_methods(false).each do |delegated|
35
27
  define_method(delegated) do
36
28
  App.config.internal.status.send(delegated)
@@ -42,6 +34,7 @@ module Karafka
42
34
  root
43
35
  env
44
36
  logger
37
+ producer
45
38
  monitor
46
39
  ].each do |delegated|
47
40
  define_method(delegated) do
@@ -4,48 +4,80 @@
4
4
  module Karafka
5
5
  # Base consumer from which all Karafka consumers should inherit
6
6
  class BaseConsumer
7
- extend Forwardable
8
-
9
- # Allows us to mark messages as consumed for non-automatic mode without having
10
- # to use consumer client directly. We do this that way, because most of the people should not
11
- # mess with the client instance directly (just in case)
12
- %i[
13
- mark_as_consumed
14
- mark_as_consumed!
15
- trigger_heartbeat
16
- trigger_heartbeat!
17
- ].each do |delegated_method_name|
18
- def_delegator :client, delegated_method_name
19
-
20
- private delegated_method_name
21
- end
22
-
23
7
  # @return [Karafka::Routing::Topic] topic to which a given consumer is subscribed
24
- attr_reader :topic
25
- # @return [Karafka::Params:ParamsBatch] current params batch
26
- attr_accessor :params_batch
27
-
28
- # Assigns a topic to a consumer and builds up proper consumer functionalities
29
- # so that it can cooperate with the topic settings
30
- # @param topic [Karafka::Routing::Topic]
31
- def initialize(topic)
32
- @topic = topic
33
- Consumers::Includer.call(self)
34
- end
8
+ attr_accessor :topic
9
+ # @return [Karafka::Messages::Messages] current messages batch
10
+ attr_accessor :messages
11
+ # @return [Karafka::Connection::Client] kafka connection client
12
+ attr_accessor :client
13
+ # @return [Karafka::TimeTrackers::Pause] current topic partition pause
14
+ attr_accessor :pause
15
+ # @return [Waterdrop::Producer] producer instance
16
+ attr_accessor :producer
35
17
 
36
18
  # Executes the default consumer flow.
37
- def call
38
- process
19
+ #
20
+ # @note We keep the seek offset tracking, and use it to compensate for async offset flushing
21
+ # that may not yet kick in when error occurs. That way we pause always on the last processed
22
+ # message.
23
+ def on_consume
24
+ Karafka.monitor.instrument('consumer.consume', caller: self) do
25
+ consume
26
+ end
27
+
28
+ pause.reset
29
+
30
+ # Mark as consumed only if manual offset management is not on
31
+ return if topic.manual_offset_management
32
+
33
+ # We use the non-blocking one here. If someone needs the blocking one, can implement it with
34
+ # manual offset management
35
+ mark_as_consumed(messages.last)
36
+ rescue StandardError => e
37
+ Karafka.monitor.instrument(
38
+ 'error.occurred',
39
+ error: e,
40
+ caller: self,
41
+ type: 'consumer.consume.error'
42
+ )
43
+ client.pause(topic.name, messages.first.partition, @seek_offset || messages.first.offset)
44
+ pause.pause
39
45
  end
40
46
 
41
- private
47
+ # Trigger method for running on shutdown.
48
+ #
49
+ # @private
50
+ def on_revoked
51
+ Karafka.monitor.instrument('consumer.revoked', caller: self) do
52
+ revoked
53
+ end
54
+ rescue StandardError => e
55
+ Karafka.monitor.instrument(
56
+ 'error.occurred',
57
+ error: e,
58
+ caller: self,
59
+ type: 'consumer.revoked.error'
60
+ )
61
+ end
42
62
 
43
- # @return [Karafka::Connection::Client] messages consuming client that can be used to
44
- # commit manually offset or pause / stop consumer based on the business logic
45
- def client
46
- Persistence::Client.read
63
+ # Trigger method for running on shutdown.
64
+ #
65
+ # @private
66
+ def on_shutdown
67
+ Karafka.monitor.instrument('consumer.shutdown', caller: self) do
68
+ shutdown
69
+ end
70
+ rescue StandardError => e
71
+ Karafka.monitor.instrument(
72
+ 'error.occurred',
73
+ error: e,
74
+ caller: self,
75
+ type: 'consumer.shutdown.error'
76
+ )
47
77
  end
48
78
 
79
+ private
80
+
49
81
  # Method that will perform business logic and on data received from Kafka (it will consume
50
82
  # the data)
51
83
  # @note This method needs bo be implemented in a subclass. We stub it here as a failover if
@@ -53,5 +85,42 @@ module Karafka
53
85
  def consume
54
86
  raise NotImplementedError, 'Implement this in a subclass'
55
87
  end
88
+
89
+ # Method that will be executed when a given topic partition is revoked. You can use it for
90
+ # some teardown procedures (closing file handler, etc).
91
+ def revoked; end
92
+
93
+ # Method that will be executed when the process is shutting down. You can use it for
94
+ # some teardown procedures (closing file handler, etc).
95
+ def shutdown; end
96
+
97
+ # Marks message as consumed in an async way.
98
+ #
99
+ # @param message [Messages::Message] last successfully processed message.
100
+ def mark_as_consumed(message)
101
+ client.mark_as_consumed(message)
102
+ @seek_offset = message.offset + 1
103
+ end
104
+
105
+ # Marks message as consumed in a sync way.
106
+ #
107
+ # @param message [Messages::Message] last successfully processed message.
108
+ def mark_as_consumed!(message)
109
+ client.mark_as_consumed!(message)
110
+ @seek_offset = message.offset + 1
111
+ end
112
+
113
+ # Seeks in the context of current topic and partition
114
+ #
115
+ # @param offset [Integer] offset where we want to seek
116
+ def seek(offset)
117
+ client.seek(
118
+ Karafka::Messages::Seek.new(
119
+ messages.metadata.topic,
120
+ messages.metadata.partition,
121
+ offset
122
+ )
123
+ )
124
+ end
56
125
  end
57
126
  end
@@ -43,16 +43,16 @@ module Karafka
43
43
  end
44
44
 
45
45
  # Allows to set description of a given cli command
46
- # @param args [Array] All the arguments that Thor desc method accepts
47
- def desc(*args)
48
- @desc ||= args
46
+ # @param desc [String] Description of a given cli command
47
+ def desc(desc)
48
+ @desc ||= desc
49
49
  end
50
50
 
51
51
  # This method will bind a given Cli command into Karafka Cli
52
52
  # This method is a wrapper to way Thor defines its commands
53
53
  # @param cli_class [Karafka::Cli] Karafka cli_class
54
54
  def bind_to(cli_class)
55
- cli_class.desc name, *@desc
55
+ cli_class.desc name, @desc
56
56
 
57
57
  (@options || []).each { |option| cli_class.option(*option) }
58
58
 
@@ -7,24 +7,59 @@ module Karafka
7
7
  class Info < Base
8
8
  desc 'Print configuration details and other options of your application'
9
9
 
10
+ # Nice karafka banner
11
+ BANNER = <<~BANNER
12
+
13
+ @@@ @@@@@ @@@
14
+ @@@ @@@ @@@
15
+ @@@ @@@ @@@@@@@@@ @@@ @@@ @@@@@@@@@ @@@@@@@@ @@@ @@@@ @@@@@@@@@
16
+ @@@@@@ @@@ @@@ @@@@@ @@@ @@@ @@@ @@@@@@@ @@@ @@@
17
+ @@@@@@@ @@@ @@@ @@@ @@@@ @@@ @@@ @@@@@@@ @@@ @@@
18
+ @@@ @@@@ @@@@@@@@@@ @@@ @@@@@@@@@@ @@@ @@@ @@@@ @@@@@@@@@@
19
+
20
+ BANNER
21
+
10
22
  # Print configuration details and other options of your application
11
23
  def call
24
+ Karafka.logger.info(BANNER)
25
+ Karafka.logger.info((core_info + license_info).join("\n"))
26
+ end
27
+
28
+ private
29
+
30
+ # @return [Array<String>] core framework related info
31
+ def core_info
12
32
  config = Karafka::App.config
13
33
 
34
+ postfix = config.license.token ? ' + Pro' : ''
35
+
14
36
  info = [
15
- "Karafka version: #{Karafka::VERSION}",
37
+ "Karafka version: #{Karafka::VERSION}#{postfix}",
16
38
  "Ruby version: #{RUBY_VERSION}",
17
- "Ruby-kafka version: #{::Kafka::VERSION}",
39
+ "Rdkafka version: #{::Rdkafka::VERSION}",
40
+ "Subscription groups count: #{Karafka::App.subscription_groups.size}",
41
+ "Workers count: #{Karafka::App.config.concurrency}",
18
42
  "Application client id: #{config.client_id}",
19
- "Backend: #{config.backend}",
20
- "Batch fetching: #{config.batch_fetching}",
21
- "Batch consuming: #{config.batch_consuming}",
22
43
  "Boot file: #{Karafka.boot_file}",
23
- "Environment: #{Karafka.env}",
24
- "Kafka seed brokers: #{config.kafka.seed_brokers}"
44
+ "Environment: #{Karafka.env}"
25
45
  ]
46
+ end
47
+
48
+ # @return [Array<String>] license related info
49
+ def license_info
50
+ config = Karafka::App.config
26
51
 
27
- Karafka.logger.info(info.join("\n"))
52
+ if config.license.token
53
+ [
54
+ 'License: Commercial',
55
+ "License entity: #{config.license.entity}",
56
+ "License expires on: #{config.license.expires_on}"
57
+ ]
58
+ else
59
+ [
60
+ 'License: LGPL-3.0'
61
+ ]
62
+ end
28
63
  end
29
64
  end
30
65
  end
@@ -12,19 +12,15 @@ module Karafka
12
12
  # Directories created by default
13
13
  INSTALL_DIRS = %w[
14
14
  app/consumers
15
- app/responders
16
- app/workers
17
15
  config
18
- lib
19
16
  log
20
- tmp/pids
21
17
  ].freeze
22
18
 
23
19
  # Where should we map proper files from templates
24
20
  INSTALL_FILES_MAP = {
25
21
  'karafka.rb.erb' => Karafka.boot_file.basename,
26
22
  'application_consumer.rb.erb' => 'app/consumers/application_consumer.rb',
27
- 'application_responder.rb.erb' => 'app/responders/application_responder.rb'
23
+ 'example_consumer.rb.erb' => 'app/consumers/example_consumer.rb'
28
24
  }.freeze
29
25
 
30
26
  # @param args [Array] all the things that Thor CLI accepts
@@ -35,6 +31,7 @@ module Karafka
35
31
  Bundler.default_lockfile
36
32
  )
37
33
  ).dependencies
34
+
38
35
  @rails = dependencies.key?('railties') || dependencies.key?('rails')
39
36
  end
40
37
 
@@ -48,9 +45,7 @@ module Karafka
48
45
  target = Karafka.root.join(target)
49
46
 
50
47
  template = File.read(Karafka.core_root.join("templates/#{source}"))
51
- # @todo Replace with the keyword argument version once we don't have to support
52
- # Ruby < 2.6
53
- render = ::ERB.new(template, nil, '-').result(binding)
48
+ render = ::ERB.new(template, trim_mode: '-').result(binding)
54
49
 
55
50
  File.open(target, 'w') { |file| file.write(render) }
56
51
  end
@@ -12,21 +12,15 @@ module Karafka
12
12
 
13
13
  desc 'Start the Karafka server (short-cut alias: "s")'
14
14
  option aliases: 's'
15
- option :daemon, default: false, type: :boolean, aliases: :d
16
- option :pid, default: 'tmp/pids/karafka', type: :string, aliases: :p
17
15
  option :consumer_groups, type: :array, default: nil, aliases: :g
18
16
 
19
17
  # Start the Karafka server
20
18
  def call
21
- cli.info
19
+ # Print our banner and info in the dev mode
20
+ print_marketing_info if Karafka::App.env.development?
22
21
 
23
22
  validate!
24
23
 
25
- if cli.options[:daemon]
26
- FileUtils.mkdir_p File.dirname(cli.options[:pid])
27
- daemonize
28
- end
29
-
30
24
  # We assign active topics on a server level, as only server is expected to listen on
31
25
  # part of the topics
32
26
  Karafka::Server.consumer_groups = cli.options[:consumer_groups]
@@ -36,6 +30,21 @@ module Karafka
36
30
 
37
31
  private
38
32
 
33
+ # Prints marketing info
34
+ def print_marketing_info
35
+ Karafka.logger.info Info::BANNER
36
+
37
+ if Karafka::App.config.license.token
38
+ Karafka.logger.info(
39
+ "\033[0;32mThank you for investing in the Karafka Pro subscription!\033[0m\n"
40
+ )
41
+ else
42
+ Karafka.logger.info(
43
+ "\033[0;31mYou like Karafka? Please consider getting a Pro subscription!\033[0m\n"
44
+ )
45
+ end
46
+ end
47
+
39
48
  # Checks the server cli configuration
40
49
  # options validations in terms of app setup (topics, pid existence, etc)
41
50
  def validate!
@@ -44,28 +53,6 @@ module Karafka
44
53
 
45
54
  raise Errors::InvalidConfigurationError, result.errors.to_h
46
55
  end
47
-
48
- # Detaches current process into background and writes its pidfile
49
- def daemonize
50
- ::Process.daemon(true)
51
- File.open(
52
- cli.options[:pid],
53
- 'w'
54
- ) { |file| file.write(::Process.pid) }
55
-
56
- # Remove pidfile on stop, just before the server instance is going to be GCed
57
- # We want to delay the moment in which the pidfile is removed as much as we can,
58
- # so instead of removing it after the server stops running, we rely on the gc moment
59
- # when this object gets removed (it is a bit later), so it is closer to the actual
60
- # system process end. We do that, so monitoring and deployment tools that rely on a pid
61
- # won't alarm or start new system process up until the current one is finished
62
- ObjectSpace.define_finalizer(self, proc { send(:clean) })
63
- end
64
-
65
- # Removes a pidfile (if exist)
66
- def clean
67
- FileUtils.rm_f(cli.options[:pid]) if cli.options[:pid]
68
- end
69
56
  end
70
57
  end
71
58
  end
data/lib/karafka/cli.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Karafka
4
4
  # Karafka framework Cli
5
+ #
5
6
  # If you want to add/modify command that belongs to CLI, please review all commands
6
7
  # available in cli/ directory inside Karafka source code.
7
8
  #
@@ -10,24 +11,16 @@ module Karafka
10
11
  class Cli < Thor
11
12
  package_name 'Karafka'
12
13
 
13
- default_task :missingno
14
-
15
14
  class << self
16
- # Loads all Cli commands into Thor framework
15
+ # Loads all Cli commands into Thor framework.
17
16
  # This method should be executed before we run Karafka::Cli.start, otherwise we won't
18
- # have any Cli commands available
17
+ # have any Cli commands available.
19
18
  def prepare
20
19
  cli_commands.each do |action|
21
20
  action.bind_to(self)
22
21
  end
23
22
  end
24
23
 
25
- # When there is a CLI crash, exit
26
- # @return [true]
27
- def exit_on_failure?
28
- true
29
- end
30
-
31
24
  private
32
25
 
33
26
  # @return [Array<Class>] Array with Cli action classes that can be used as commands
@@ -42,7 +35,7 @@ module Karafka
42
35
  end
43
36
  end
44
37
 
45
- # This is kinda trick - since we don't have a autoload and other magic stuff
38
+ # This is kinda tricky - since we don't have an autoload and other magic stuff
46
39
  # like Rails does, so instead this method allows us to replace currently running
47
40
  # console with a new one via Kernel.exec. It will start console with new code loaded
48
41
  # Yes, we know that it is not turbo fast, however it is turbo convenient and small