unicorn-worker-killer-2 1.0.0
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 +7 -0
- data/lib/unicorn/worker-killer-2/max_requests.rb +43 -0
- data/lib/unicorn/worker-killer-2/oom.rb +51 -0
- data/lib/unicorn/worker-killer-2.rb +38 -0
- metadata +80 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 978787e2d1d75226ad11e4bcd96c84c39a9102c3
         | 
| 4 | 
            +
              data.tar.gz: f59f3e586709b9ee51972ee9d3698829ba74a529
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 657ff2fc32758ff7255c9d999acb3bce1e36cc145d2bf35a602399544f72bf9ebc4a0ffc019cb83fb5dcbc6967ebe4916f21e469f7a04c515c6ed79f6a4929f9
         | 
| 7 | 
            +
              data.tar.gz: '0903a412426f770137ef330f505db9245df49c8c8df4aadd0e5889e5eb82f3b67996f31f6334d6351617f38e731b70cf9148953cd40c12fd3bf68aaaf7756657'
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Unicorn
         | 
| 4 | 
            +
              module WorkerKiller
         | 
| 5 | 
            +
                module MaxRequests
         | 
| 6 | 
            +
                  module MonkeyPatch
         | 
| 7 | 
            +
                    def process_client(client)
         | 
| 8 | 
            +
                      super(client) # Unicorn::HttpServer#process_client
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                      return if @_worker_max_requests_min.zero? &&
         | 
| 11 | 
            +
                                @_worker_max_requests_max.zero?
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                      logger.info "#{self}: worker (pid: #{Process.pid}) has #{@_worker_request_limit} left before being killed" if @_verbose
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                      @_worker_request_limit -= 1
         | 
| 16 | 
            +
                      return if @_worker_request_limit.positive?
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds max number of requests (limit: #{@_worker_max_requests})"
         | 
| 19 | 
            +
                      WorkerKiller.kill_self(logger, @_worker_process_start)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def self.monkey_patch(opts = {})
         | 
| 24 | 
            +
                    min = opts[:max_requests_min] || 3072
         | 
| 25 | 
            +
                    max = opts[:max_requests_max] || 4096
         | 
| 26 | 
            +
                    verbose = opts[:verbose] || false
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    ObjectSpace.each_object(HttpServer) do |s|
         | 
| 29 | 
            +
                      s.extend(MonkeyPatch)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      s.instance_variable_set(:@_worker_process_start, WorkerKiller.now)
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                      s.instance_variable_set(:@_worker_max_requests_min, min)
         | 
| 34 | 
            +
                      s.instance_variable_set(:@_worker_max_requests_max, max)
         | 
| 35 | 
            +
                      s.instance_variable_set(:@_verbose, verbose)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      r = WorkerKiller.randomize(max - min + 1)
         | 
| 38 | 
            +
                      s.instance_variable_set(:@_worker_request_limit, min + r)
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'get_process_mem'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Unicorn
         | 
| 6 | 
            +
              module WorkerKiller
         | 
| 7 | 
            +
                module Oom
         | 
| 8 | 
            +
                  module MonkeyPatch
         | 
| 9 | 
            +
                    def process_client(client)
         | 
| 10 | 
            +
                      super(client) # Unicorn::HttpServer#process_client
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                      return if @_worker_memory_limit_min.zero? &&
         | 
| 13 | 
            +
                                @_worker_memory_limit_max.zero?
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                      @_worker_check_count ||= 0
         | 
| 16 | 
            +
                      @_worker_check_count += 1
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      return unless (@_worker_check_count % @_worker_check_cycle).zero?
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      rss = GetProcessMem.new.bytes
         | 
| 21 | 
            +
                      logger.info "#{self}: worker (pid: #{Process.pid}) using #{rss} bytes." if @_verbose
         | 
| 22 | 
            +
                      return if rss < @_worker_memory_limit
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds memory limit (#{rss} bytes > #{@_worker_memory_limit} bytes)"
         | 
| 25 | 
            +
                      WorkerKiller.kill_self(logger, @_worker_process_start)
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def self.monkey_patch(opts = {})
         | 
| 30 | 
            +
                    min = opts[:memory_limit_min] || (1024**3)
         | 
| 31 | 
            +
                    max = opts[:memory_limit_max] || 2 * (1024**3)
         | 
| 32 | 
            +
                    check_cycle = opts[:check_cycle] || 16
         | 
| 33 | 
            +
                    verbose = opts[:verbose] || false
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    ObjectSpace.each_object(HttpServer) do |s|
         | 
| 36 | 
            +
                      s.extend(MonkeyPatch)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      s.instance_variable_set(:@_worker_process_start, WorkerKiller.now)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                      s.instance_variable_set(:@_worker_memory_limit_min, min)
         | 
| 41 | 
            +
                      s.instance_variable_set(:@_worker_memory_limit_max, max)
         | 
| 42 | 
            +
                      s.instance_variable_set(:@_worker_check_cycle, check_cycle)
         | 
| 43 | 
            +
                      s.instance_variable_set(:@_verbose, verbose)
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      r = WorkerKiller.randomize(max - min + 1)
         | 
| 46 | 
            +
                      s.instance_variable_set(:@_worker_memory_limit, min + r)
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
            end
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'unicorn'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require_relative 'worker-killer-2/oom'
         | 
| 6 | 
            +
            require_relative 'worker-killer-2/max_requests'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            module Unicorn
         | 
| 9 | 
            +
              module WorkerKiller
         | 
| 10 | 
            +
                # Kill the current process by telling it to send signals to itself. If the
         | 
| 11 | 
            +
                # process isn't killed after 5 QUIT signals, send 10 TERM signals. Finally,
         | 
| 12 | 
            +
                # send a KILL signal. A single signal is sent per request.
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                # @see http://unicorn.bogomips.org/SIGNALS.html
         | 
| 15 | 
            +
                def self.kill_self(logger, start_time)
         | 
| 16 | 
            +
                  alive_sec = (now - start_time).round
         | 
| 17 | 
            +
                  worker_pid = Process.pid
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  @kill_attempts ||= 0
         | 
| 20 | 
            +
                  @kill_attempts += 1
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  sig = :QUIT
         | 
| 23 | 
            +
                  sig = :TERM if @kill_attempts > 10
         | 
| 24 | 
            +
                  sig = :KILL if @kill_attempts > 15
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  logger.warn "#{self} send SIG#{sig} (pid: #{worker_pid}) alive: #{alive_sec} sec (trial #{@kill_attempts})"
         | 
| 27 | 
            +
                  Process.kill sig, worker_pid
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def self.randomize(integer)
         | 
| 31 | 
            +
                  Random.rand(integer.abs)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def self.now
         | 
| 35 | 
            +
                  Process.clock_gettime(Process::CLOCK_MONOTONIC)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,80 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: unicorn-worker-killer-2
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 1.0.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Chris Elsworth
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2019-03-17 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: get_process_mem
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: unicorn
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - ">="
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '4'
         | 
| 34 | 
            +
                - - "<"
         | 
| 35 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 36 | 
            +
                    version: '6'
         | 
| 37 | 
            +
              type: :runtime
         | 
| 38 | 
            +
              prerelease: false
         | 
| 39 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 40 | 
            +
                requirements:
         | 
| 41 | 
            +
                - - ">="
         | 
| 42 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 43 | 
            +
                    version: '4'
         | 
| 44 | 
            +
                - - "<"
         | 
| 45 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 46 | 
            +
                    version: '6'
         | 
| 47 | 
            +
            description: Kill Unicorn child processes when they exceed memory/request limits
         | 
| 48 | 
            +
            email:
         | 
| 49 | 
            +
            - chris.elsworth@bytemark.co.uk
         | 
| 50 | 
            +
            executables: []
         | 
| 51 | 
            +
            extensions: []
         | 
| 52 | 
            +
            extra_rdoc_files: []
         | 
| 53 | 
            +
            files:
         | 
| 54 | 
            +
            - lib/unicorn/worker-killer-2.rb
         | 
| 55 | 
            +
            - lib/unicorn/worker-killer-2/max_requests.rb
         | 
| 56 | 
            +
            - lib/unicorn/worker-killer-2/oom.rb
         | 
| 57 | 
            +
            homepage: https://gitlab.bytemark.co.uk/bytemark/unicorn-worker-killer-2
         | 
| 58 | 
            +
            licenses: []
         | 
| 59 | 
            +
            metadata: {}
         | 
| 60 | 
            +
            post_install_message: 
         | 
| 61 | 
            +
            rdoc_options: []
         | 
| 62 | 
            +
            require_paths:
         | 
| 63 | 
            +
            - lib
         | 
| 64 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
              requirements:
         | 
| 66 | 
            +
              - - ">="
         | 
| 67 | 
            +
                - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                  version: '0'
         | 
| 69 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 70 | 
            +
              requirements:
         | 
| 71 | 
            +
              - - ">="
         | 
| 72 | 
            +
                - !ruby/object:Gem::Version
         | 
| 73 | 
            +
                  version: '0'
         | 
| 74 | 
            +
            requirements: []
         | 
| 75 | 
            +
            rubyforge_project: 
         | 
| 76 | 
            +
            rubygems_version: 2.6.13
         | 
| 77 | 
            +
            signing_key: 
         | 
| 78 | 
            +
            specification_version: 4
         | 
| 79 | 
            +
            summary: Rewrite of unicorn-worker-kill
         | 
| 80 | 
            +
            test_files: []
         |