diff --git a/Gemfile b/Gemfile index f7d1a2a874056d2a8b5f66c4d57fbee95fd35a8b..f9507d71e4e67fa3c6c20d45592957976c640e62 100644 --- a/Gemfile +++ b/Gemfile @@ -275,7 +275,7 @@ gem 'rack', '~> 2.2.9' # rubocop:todo Gemfile/MissingFeatureCategory gem 'rack-timeout', '~> 0.7.0', require: 'rack/timeout/base' # rubocop:todo Gemfile/MissingFeatureCategory group :puma do - gem 'puma', '= 6.4.0', require: false, feature_category: :shared + gem 'puma', '= 6.4.3', require: false, feature_category: :shared gem 'sd_notify', '~> 0.1.0', require: false # rubocop:todo Gemfile/MissingFeatureCategory end diff --git a/Gemfile.checksum b/Gemfile.checksum index b10fb5b6958e067f0071713d3b941c04439eba2f..5b566aa8fedcbf43014d21e778cfc44bf1c44a63 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -525,8 +525,8 @@ {"name":"pry-rails","version":"0.3.9","platform":"ruby","checksum":"468662575abb6b67f4a9831219f99290d5eae7bf186e64dd810d0a3e4a8cc4b1"}, {"name":"pry-shell","version":"0.6.4","platform":"ruby","checksum":"ad024882d29912b071a7de65ebea538b242d2dc1498c60c7c2352ef94769f208"}, {"name":"public_suffix","version":"6.0.1","platform":"ruby","checksum":"61d44e1cab5cbbbe5b31068481cf16976dd0dc1b6b07bd95617ef8c5e3e00c6f"}, -{"name":"puma","version":"6.4.0","platform":"java","checksum":"eb27679e9e665882bab85dfa84704b0615b4f77cec46de014f05b90a5ab36cfe"}, -{"name":"puma","version":"6.4.0","platform":"ruby","checksum":"d5dda11362744df9f4694708a62e3cfddf72eba7498c16016ebbb30f106712f9"}, +{"name":"puma","version":"6.4.3","platform":"java","checksum":"373fcfacacaafd0f5a24db18cb99b3f2decb5c5316470169852559aa80adc8ab"}, +{"name":"puma","version":"6.4.3","platform":"ruby","checksum":"24a4645c006811d83f2480057d1f54a96e7627b6b90e1c99b260b9dc630eb43e"}, {"name":"pyu-ruby-sasl","version":"0.0.3.3","platform":"ruby","checksum":"5683a6bc5738db5a1bf5ceddeaf545405fb241b4184dd4f2587e679a7e9497e5"}, {"name":"raabro","version":"1.4.0","platform":"ruby","checksum":"d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882"}, {"name":"racc","version":"1.6.2","platform":"java","checksum":"0880781e7dfde09e665d0b6160b583e01ed52fcc2955d7891447d33c2d1d2cf1"}, diff --git a/Gemfile.lock b/Gemfile.lock index dc42b2d8cc66416c571f3f539ebf73c99ee3659d..5ef32293dab666eefebf103555461db179b74b01 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1451,7 +1451,7 @@ GEM tty-markdown tty-prompt public_suffix (6.0.1) - puma (6.4.0) + puma (6.4.3) nio4r (~> 2.0) pyu-ruby-sasl (0.0.3.3) raabro (1.4.0) @@ -2232,7 +2232,7 @@ DEPENDENCIES pry-byebug pry-rails (~> 0.3.9) pry-shell (~> 0.6.4) - puma (= 6.4.0) + puma (= 6.4.3) rack (~> 2.2.9) rack-attack (~> 6.7.0) rack-cors (~> 2.0.1) diff --git a/Gemfile.next.checksum b/Gemfile.next.checksum index 3c3bca8eac0e68a3083deb260671a9a33734320a..f290ce5989e0ef7ec6fdb68a438ce9184b5f5ab5 100644 --- a/Gemfile.next.checksum +++ b/Gemfile.next.checksum @@ -535,8 +535,8 @@ {"name":"psych","version":"5.1.2","platform":"java","checksum":"1dd68dc609eddbc884e6892e11da942e16f7256bd30ebde9d35449d43043a6fe"}, {"name":"psych","version":"5.1.2","platform":"ruby","checksum":"337322f58fc2bf24827d2b9bd5ab595f6a72971867d151bb39980060ea40a368"}, {"name":"public_suffix","version":"6.0.1","platform":"ruby","checksum":"61d44e1cab5cbbbe5b31068481cf16976dd0dc1b6b07bd95617ef8c5e3e00c6f"}, -{"name":"puma","version":"6.4.0","platform":"java","checksum":"eb27679e9e665882bab85dfa84704b0615b4f77cec46de014f05b90a5ab36cfe"}, -{"name":"puma","version":"6.4.0","platform":"ruby","checksum":"d5dda11362744df9f4694708a62e3cfddf72eba7498c16016ebbb30f106712f9"}, +{"name":"puma","version":"6.4.3","platform":"java","checksum":"373fcfacacaafd0f5a24db18cb99b3f2decb5c5316470169852559aa80adc8ab"}, +{"name":"puma","version":"6.4.3","platform":"ruby","checksum":"24a4645c006811d83f2480057d1f54a96e7627b6b90e1c99b260b9dc630eb43e"}, {"name":"pyu-ruby-sasl","version":"0.0.3.3","platform":"ruby","checksum":"5683a6bc5738db5a1bf5ceddeaf545405fb241b4184dd4f2587e679a7e9497e5"}, {"name":"raabro","version":"1.4.0","platform":"ruby","checksum":"d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882"}, {"name":"racc","version":"1.6.2","platform":"java","checksum":"0880781e7dfde09e665d0b6160b583e01ed52fcc2955d7891447d33c2d1d2cf1"}, diff --git a/Gemfile.next.lock b/Gemfile.next.lock index cecfa8787a49e1364dbf36aba9277f9cbc046de1..bad9b5a7eb9c25f592844cb5d37465eb76bd0a29 100644 --- a/Gemfile.next.lock +++ b/Gemfile.next.lock @@ -1456,7 +1456,7 @@ GEM psych (5.1.2) stringio public_suffix (6.0.1) - puma (6.4.0) + puma (6.4.3) nio4r (~> 2.0) pyu-ruby-sasl (0.0.3.3) raabro (1.4.0) @@ -2247,7 +2247,7 @@ DEPENDENCIES pry-byebug pry-rails (~> 0.3.9) pry-shell (~> 0.6.4) - puma (= 6.4.0) + puma (= 6.4.3) rack (~> 2.2.9) rack-attack (~> 6.7.0) rack-cors (~> 2.0.1) diff --git a/config/initializers/puma_patch.rb b/config/initializers/puma_patch.rb new file mode 100644 index 0000000000000000000000000000000000000000..9f8c690b9a0b6f8c75d35def7bd4961ddb8216d1 --- /dev/null +++ b/config/initializers/puma_patch.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +return unless Gitlab::Runtime.puma? + +require 'puma' +require 'puma/cluster' + +# Ruby 3.1 and 3.2 have bugs that prevents Puma from reaping child processes properly: +# https://bugs.ruby-lang.org/issues/20490 +# https://bugs.ruby-lang.org/issues/19837 +# +# https://github.com/puma/puma/pull/3314 fixes this in Puma, but a release +# has not been forthcoming. +if Gem::Version.new(Puma::Const::PUMA_VERSION) > Gem::Version.new('6.5') + raise 'This patch should not be needed after Puma 6.5.0.' +end + +# rubocop:disable Style/RedundantBegin -- These are upstream changes +# rubocop:disable Cop/LineBreakAfterGuardClauses -- These are upstream changes +# rubocop:disable Layout/EmptyLineAfterGuardClause -- These are upstream changes +module Puma + class Cluster < Runner + # loops thru @workers, removing workers that exited, and calling + # `#term` if needed + def wait_workers + # Reap all children, known workers or otherwise. + # If puma has PID 1, as it's common in containerized environments, + # then it's responsible for reaping orphaned processes, so we must reap + # all our dead children, regardless of whether they are workers we spawned + # or some reattached processes. + reaped_children = {} + loop do + begin + pid, status = Process.wait2(-1, Process::WNOHANG) + break unless pid + reaped_children[pid] = status + rescue Errno::ECHILD + break + end + end + + @workers.reject! do |w| + next false if w.pid.nil? + begin + # We may need to check the PID individually because: + # 1. From Ruby versions 2.6 to 3.2, `Process.detach` can prevent or delay + # `Process.wait2(-1)` from detecting a terminated process: https://bugs.ruby-lang.org/issues/19837. + # 2. When `fork_worker` is enabled, some worker may not be direct children, + # but grand children. Because of this they won't be reaped by `Process.wait2(-1)`. + if reaped_children.delete(w.pid) || Process.wait(w.pid, Process::WNOHANG) + true + else + w.term if w.term? + nil + end + rescue Errno::ECHILD + begin + Process.kill(0, w.pid) + # child still alive but has another parent (e.g., using fork_worker) + w.term if w.term? + false + rescue Errno::ESRCH, Errno::EPERM + true # child is already terminated + end + end + end + + # Log unknown children + reaped_children.each do |pid, status| + log "! reaped unknown child process pid=#{pid} status=#{status}" + end + end + end +end +# rubocop:enable Style/RedundantBegin +# rubocop:enable Cop/LineBreakAfterGuardClauses +# rubocop:enable Layout/EmptyLineAfterGuardClause