karafka 2.0.13 → 2.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a38f7b176a40f09dc07c58b8abc8b13863f66fc2f3bff4d61fcd889c0bbb213
4
- data.tar.gz: 9962f21cb52e566843e4b6994da8ac182752523a937cffa18bd735eefadc409f
3
+ metadata.gz: 5d21c47bcabdc50e520f7eb81f17fd31f00916a902aba6e596a2a70e1586a927
4
+ data.tar.gz: eff93034c3d6275d067c5b29b1b7b8d7ad2fb8cca73b08bd64398917338077af
5
5
  SHA512:
6
- metadata.gz: 5b722f8b0baced05f35e907dadb523c129fe12f64066f5efe9bd81fdc0634ccfb824d463baa8f34b7918410c3f6e8a451ceaf5645bb08a8adeaaccff99ec29f1
7
- data.tar.gz: d2649dda560f9c171f804da107df33c1a7fdf81c21bb92d33b7b967aee950a042868c566b63fb32cd495d870e4164246f8ee15aca7c169c66fb82f0994b321ff
6
+ metadata.gz: adc869f03b9f3774f9c5f4351980be5f12f36606242da7062f8abe2886d85fb8576fa10c8f2615f4e0e30a3329abd27b943ae4bbcfe842607e32d26b988ef58a
7
+ data.tar.gz: a7412443e62cb84dcba452aedd230769860c1a8ecb84ede8c34d20b47995117fae22cb60598190436347f50f38811b0a05d55e4219b4b41c3dc382a0f5117511
checksums.yaml.gz.sig CHANGED
Binary file
@@ -18,7 +18,7 @@ jobs:
18
18
  strategy:
19
19
  fail-fast: false
20
20
  steps:
21
- - uses: actions/checkout@v2
21
+ - uses: actions/checkout@v3
22
22
  with:
23
23
  fetch-depth: 0
24
24
 
@@ -39,7 +39,7 @@ jobs:
39
39
  strategy:
40
40
  fail-fast: false
41
41
  steps:
42
- - uses: actions/checkout@v2
42
+ - uses: actions/checkout@v3
43
43
  with:
44
44
  fetch-depth: 0
45
45
  - name: Run Coditsu
@@ -64,7 +64,7 @@ jobs:
64
64
  - ruby: '3.1'
65
65
  coverage: 'true'
66
66
  steps:
67
- - uses: actions/checkout@v2
67
+ - uses: actions/checkout@v3
68
68
  - name: Install package dependencies
69
69
  run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends $APT_DEPS"
70
70
 
@@ -97,7 +97,7 @@ jobs:
97
97
  - ruby: '3.1'
98
98
  coverage: 'true'
99
99
  steps:
100
- - uses: actions/checkout@v2
100
+ - uses: actions/checkout@v3
101
101
  - name: Install package dependencies
102
102
  run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends $APT_DEPS"
103
103
 
data/.rspec CHANGED
@@ -1 +1,3 @@
1
1
  --require spec_helper
2
+ # Integration specs run with their one framework, not via RSpec
3
+ --exclude-pattern "spec/integrations/**/*_spec.rb"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Karafka framework changelog
2
2
 
3
+ ## 2.0.14 (2022-10-16)
4
+ - Prevent consecutive stop signals from starting multiple supervision shutdowns.
5
+ - Provide `Karafka::Embedded` to simplify the start/stop process when running Karafka from within other process (Puma, Sidekiq, etc).
6
+ - Fix a race condition when un-pausing a long-running-job exactly upon listener resuming would crash the listener loop (#1072).
7
+
3
8
  ## 2.0.13 (2022-10-14)
4
9
  - Early exit upon attempts to commit current or earlier offset twice.
5
10
  - Add more integration specs covering edge cases.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.0.13)
4
+ karafka (2.0.14)
5
5
  karafka-core (>= 2.0.2, < 3.0.0)
6
6
  rdkafka (>= 0.12)
7
7
  thor (>= 0.20)
data/bin/integrations CHANGED
@@ -24,7 +24,7 @@ ROOT_PATH = Pathname.new(File.expand_path(File.join(File.dirname(__FILE__), '../
24
24
  # When the value is high, there's a problem with thread allocation on Github CI, tht is why
25
25
  # we limit it. Locally we can run a lot of those, as many of them have sleeps and do not use a lot
26
26
  # of CPU
27
- CONCURRENCY = ENV.key?('CI') ? 3 : Etc.nprocessors * 2
27
+ CONCURRENCY = ENV.key?('CI') ? 4 : Etc.nprocessors * 2
28
28
 
29
29
  # How may bytes do we want to keep from the stdout in the buffer for when we need to print it
30
30
  MAX_BUFFER_OUTPUT = 51_200
@@ -39,10 +39,10 @@ class Scenario
39
39
  # This includes exactly those
40
40
  EXIT_CODES = {
41
41
  default: [0],
42
- 'consumption/worker_critical_error_behaviour.rb' => [0, 2].freeze,
43
- 'shutdown/on_hanging_jobs_and_a_shutdown.rb' => [2].freeze,
44
- 'shutdown/on_hanging_on_shutdown_job_and_a_shutdown.rb' => [2].freeze,
45
- 'shutdown/on_hanging_listener_and_shutdown.rb' => [2].freeze
42
+ 'consumption/worker_critical_error_behaviour_spec.rb' => [0, 2].freeze,
43
+ 'shutdown/on_hanging_jobs_and_a_shutdown_spec.rb' => [2].freeze,
44
+ 'shutdown/on_hanging_on_shutdown_job_and_a_shutdown_spec.rb' => [2].freeze,
45
+ 'shutdown/on_hanging_listener_and_shutdown_spec.rb' => [2].freeze
46
46
  }.freeze
47
47
 
48
48
  private_constant :MAX_RUN_TIME, :EXIT_CODES
@@ -202,7 +202,7 @@ class Scenario
202
202
  end
203
203
 
204
204
  # Load all the specs
205
- specs = Dir[ROOT_PATH.join('spec/integrations/**/*.rb')]
205
+ specs = Dir[ROOT_PATH.join('spec/integrations/**/*_spec.rb')]
206
206
 
207
207
  # If filters is provided, apply
208
208
  # Allows to provide several filters one after another and applies all of them
@@ -210,6 +210,7 @@ ARGV.each do |filter|
210
210
  specs.delete_if { |name| !name.include?(filter) }
211
211
  end
212
212
 
213
+
213
214
  raise ArgumentError, "No integration specs with filters: #{ARGV.join(', ')}" if specs.empty?
214
215
 
215
216
  # Randomize order
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ # Allows to start and stop Karafka as part of a different process
5
+ module Embedded
6
+ class << self
7
+ # Starts Karafka without supervision and without ownership of signals in a background thread
8
+ # so it won't interrupt other things running
9
+ def start
10
+ Thread.new { Karafka::Server.start }
11
+ end
12
+
13
+ # Stops Karafka upon any event
14
+ #
15
+ # @note This method is blocking because we want to wait until Karafka is stopped with final
16
+ # process shutdown
17
+ def stop
18
+ # Stop needs to be blocking to wait for all the things to finalize
19
+ Karafka::Server.stop
20
+ end
21
+ end
22
+ end
23
+ end
@@ -29,12 +29,19 @@ module Karafka
29
29
  # Creates an instance of process and creates empty hash for callbacks
30
30
  def initialize
31
31
  @callbacks = Hash.new { |hsh, key| hsh[key] = [] }
32
+ @supervised = false
32
33
  end
33
34
 
34
35
  # Method catches all HANDLED_SIGNALS and performs appropriate callbacks (if defined)
35
36
  # @note If there are no callbacks, this method will just ignore a given signal that was sent
36
37
  def supervise
37
38
  HANDLED_SIGNALS.each { |signal| trap_signal(signal) }
39
+ @supervised = true
40
+ end
41
+
42
+ # Is the current process supervised and are trap signals installed
43
+ def supervised?
44
+ @supervised
38
45
  end
39
46
 
40
47
  private
@@ -31,6 +31,7 @@ module Karafka
31
31
  process.on_sigint { Thread.new { stop } }
32
32
  process.on_sigquit { Thread.new { stop } }
33
33
  process.on_sigterm { Thread.new { stop } }
34
+ process.supervise
34
35
 
35
36
  # Start is blocking until stop is called and when we stop, it will wait until
36
37
  # all of the things are ready to stop
@@ -61,7 +62,6 @@ module Karafka
61
62
  # @note We don't need to sleep because Karafka::Fetcher is locking and waiting to
62
63
  # finish loop (and it won't happen until we explicitly want to stop)
63
64
  def start
64
- process.supervise
65
65
  Karafka::App.run!
66
66
  Karafka::Runner.new.call
67
67
  end
@@ -73,6 +73,9 @@ module Karafka
73
73
  # lock them forever. If you need to run Karafka shutdown from within workers threads,
74
74
  # please start a separate thread to do so.
75
75
  def stop
76
+ # Initialize the stopping process only if Karafka was running
77
+ return unless Karafka::App.running?
78
+
76
79
  Karafka::App.stop!
77
80
 
78
81
  timeout = Karafka::App.config.shutdown_timeout
@@ -110,8 +113,12 @@ module Karafka
110
113
 
111
114
  Karafka::App.producer.close
112
115
 
116
+ # We also do not forcefully terminate everything when running in the embedded mode,
117
+ # otherwise we would overwrite the shutdown process of the process that started Karafka
118
+ return unless process.supervised?
119
+
113
120
  # exit! is not within the instrumentation as it would not trigger due to exit
114
- Kernel.exit! FORCEFUL_EXIT_CODE
121
+ Kernel.exit!(FORCEFUL_EXIT_CODE)
115
122
  ensure
116
123
  Karafka::App.stopped!
117
124
  end
@@ -25,6 +25,9 @@ module Karafka
25
25
  end
26
26
 
27
27
  define_method transition do
28
+ # Do nothing if the state change would change nothing (same state)
29
+ return if @status == state
30
+
28
31
  @status = state
29
32
 
30
33
  # Skip on creation (initializing)
@@ -3,6 +3,12 @@
3
3
  module Karafka
4
4
  module TimeTrackers
5
5
  # Handles Kafka topic partition pausing and resuming with exponential back-offs.
6
+ # Since expiring and pausing can happen from both consumer and listener, this needs to be
7
+ # thread-safe.
8
+ #
9
+ # @note We do not have to worry about performance implications of a mutex wrapping most of the
10
+ # code here, as this is not a frequently used tracker. It is active only once per batch in
11
+ # case of long-running-jobs and upon errors.
6
12
  class Pause < Base
7
13
  attr_reader :count
8
14
 
@@ -36,6 +42,7 @@ module Karafka
36
42
  @timeout = timeout
37
43
  @max_timeout = max_timeout
38
44
  @exponential_backoff = exponential_backoff
45
+ @mutex = Mutex.new
39
46
  super()
40
47
  end
41
48
 
@@ -45,35 +52,47 @@ module Karafka
45
52
  # @note Providing this value can be useful when we explicitly want to pause for a certain
46
53
  # period of time, outside of any regular pausing logic
47
54
  def pause(timeout = backoff_interval)
48
- @started_at = now
49
- @ends_at = @started_at + timeout
50
- @count += 1
55
+ @mutex.synchronize do
56
+ @started_at = now
57
+ @ends_at = @started_at + timeout
58
+ @count += 1
59
+ end
51
60
  end
52
61
 
53
62
  # Marks the pause as resumed.
54
63
  def resume
55
- @started_at = nil
56
- @ends_at = nil
64
+ @mutex.synchronize do
65
+ @started_at = nil
66
+ @ends_at = nil
67
+ end
57
68
  end
58
69
 
59
70
  # Expires the pause, so it can be considered expired
60
71
  def expire
61
- @ends_at = nil
72
+ @mutex.synchronize do
73
+ @ends_at = nil
74
+ end
62
75
  end
63
76
 
64
77
  # @return [Boolean] are we paused from processing
65
78
  def paused?
66
- !@started_at.nil?
79
+ @mutex.synchronize do
80
+ !@started_at.nil?
81
+ end
67
82
  end
68
83
 
69
84
  # @return [Boolean] did the pause expire
70
85
  def expired?
71
- @ends_at ? now >= @ends_at : true
86
+ @mutex.synchronize do
87
+ @ends_at ? now >= @ends_at : true
88
+ end
72
89
  end
73
90
 
74
91
  # Resets the pause counter.
75
92
  def reset
76
- @count = 0
93
+ @mutex.synchronize do
94
+ @count = 0
95
+ end
77
96
  end
78
97
 
79
98
  private
@@ -2,7 +2,8 @@
2
2
 
3
3
  module Karafka
4
4
  module TimeTrackers
5
- # Object used to keep track of time we've used running certain operations.
5
+ # Object used to keep track of time we've used running certain operations. Polling is
6
+ # running in a single thread, thus we do not have to worry about this being thread-safe.
6
7
  #
7
8
  # @example Keep track of sleeping and stop after 3 seconds of 0.1 sleep intervals
8
9
  # time_poll = Poll.new(3000)
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '2.0.13'
6
+ VERSION = '2.0.14'
7
7
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.13
4
+ version: 2.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -35,7 +35,7 @@ cert_chain:
35
35
  Qf04B9ceLUaC4fPVEz10FyobjaFoY4i32xRto3XnrzeAgfEe4swLq8bQsR3w/EF3
36
36
  MGU0FeSV2Yj7Xc2x/7BzLK8xQn5l7Yy75iPF+KP3vVmDHnNl
37
37
  -----END CERTIFICATE-----
38
- date: 2022-10-14 00:00:00.000000000 Z
38
+ date: 2022-10-16 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: karafka-core
@@ -188,6 +188,7 @@ files:
188
188
  - lib/karafka/contracts/consumer_group.rb
189
189
  - lib/karafka/contracts/consumer_group_topic.rb
190
190
  - lib/karafka/contracts/server_cli_options.rb
191
+ - lib/karafka/embedded.rb
191
192
  - lib/karafka/env.rb
192
193
  - lib/karafka/errors.rb
193
194
  - lib/karafka/helpers/async.rb
metadata.gz.sig CHANGED
Binary file