capelinhos 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c089836d43a3a2dc882eb95afbbf02a121b6e856c52221b7fcd7d4bc9198f962
4
- data.tar.gz: 30c81f93a1d1e4235ec1e0c946ff793d0ebcd91f8389855d3db1156fbd0f92e5
3
+ metadata.gz: 78e1daf01cf2cdb53f94de1f68f88d014ae4f5d849a1efd13f5db6bec159b0b6
4
+ data.tar.gz: 44a494709edc3bd69d4a00a76dc2ca3ddb28b9950fe17414c34768d26a0b1e71
5
5
  SHA512:
6
- metadata.gz: 6b776e7007ecda0f30d8cee0584d4ba79f3b4e8cf464b2487a633110b57e66bd6eed795a4bd9eba84ca45d0911fbd5870a14a5ae899837835e0ed6b3d73c953c
7
- data.tar.gz: 69e90fc92715ffe7f2f0c28737c21fa005f170d9885a85d45a2f0cffc5349b3f0c3412f03cf5712d1607d38ac359d181db6c9f7aa8e3b293e69d4fed9d3af784
6
+ metadata.gz: 7b4fdaecc769d87f5b0649bb9b00f90e8aade2c5c5c0901b5eef16c348b24b75ee4ec190cc9f6b2cf87e8721dd4d8726ba08ec984df0a42c97c5260850cbe397
7
+ data.tar.gz: fd64de034e829cf6dbe88a7d00bda14a7e536cb3fd09bf56124d2420ff31f1695d9ec7fd0686cca79a17f8ef61dc04eec22f474fd710ae0104f87e2715c16217
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-03-30
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Adam Hallett
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Capelinhos
2
+
3
+ This gem gracefully stops Passenger Ruby processes that have exceeded a memory threshold. It is meant to be added to cron and run at an interval of your choosing.
4
+
5
+ If Phusion Passenger is configured with `PassengerMinInstances` and the number of processes is under that number, then a new process should replace the processes that were shutdown.
6
+
7
+ Note: This doesn't skip active long running sessions. This feature be added as a default in the future.
8
+
9
+ ## Installation
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ $ bundle add capelinhos
14
+
15
+ If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ $ gem install capelinhos
18
+
19
+ ## Usage
20
+
21
+ # kill up to 3 processes that use more than 400 megabytes
22
+ $ capelinos --max-memory 400 --max-processes 3
23
+
24
+ ## Development
25
+
26
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
27
+
28
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
29
+
30
+ ## Contributing
31
+
32
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/capelinhos.
33
+
34
+ ## License
35
+
36
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -0,0 +1,15 @@
1
+ {
2
+ "folders":
3
+ [
4
+ {
5
+ "path": ".",
6
+ "folder_exclude_patterns": []
7
+ }
8
+ ],
9
+ "settings": {
10
+ "ensure_newline_at_eof_on_save": true,
11
+ "tab_size": 2,
12
+ "translate_tabs_to_spaces": true,
13
+ "trim_trailing_white_space_on_save": true
14
+ }
15
+ }
data/exe/capelinhos ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'optparse'
6
+ require 'capelinhos'
7
+
8
+ source_root = File.expand_path("..", File.dirname(__FILE__))
9
+ $LOAD_PATH.unshift("#{source_root}/src/ruby_supportlib")
10
+ begin
11
+ require 'rubygems'
12
+ rescue LoadError
13
+ end
14
+ require 'phusion_passenger'
15
+
16
+ PhusionPassenger.locate_directories
17
+ PhusionPassenger.require_passenger_lib 'constants'
18
+ PhusionPassenger.require_passenger_lib 'platform_info'
19
+ PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry'
20
+ PhusionPassenger.require_passenger_lib 'platform_info/ruby'
21
+ PhusionPassenger.require_passenger_lib 'admin_tools/memory_stats'
22
+ PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry'
23
+ PhusionPassenger.require_passenger_lib 'config/utils'
24
+ PhusionPassenger.require_passenger_lib 'utils/ansi_colors'
25
+ include PhusionPassenger
26
+ include PhusionPassenger::SharedConstants
27
+ include PhusionPassenger::AdminTools
28
+ include PhusionPassenger::Utils::AnsiColors
29
+
30
+ options = {}
31
+ OptionParser.new do |opts|
32
+ opts.banner = "Usage: capelinhos --max-memory 320 --max-processes 3"
33
+
34
+ opts.on("--max-memory", "--max-memory MAX_MEMORY", Numeric, "Maximum memory in megabytes to trigger shutdown.") do |n|
35
+ options[:max_memory] = n
36
+ end
37
+
38
+ opts.on("--max-processes", "--max-processes=NUMBER", Numeric, "Maximum number of processes to gracefully shutdown.") do |n|
39
+ options[:max_processes] = n
40
+ end
41
+
42
+ opts.on("-h", "--help", "Prints this help") do
43
+ puts opts
44
+ exit
45
+ end
46
+ end.parse!
47
+
48
+ if !options[:max_memory]
49
+ puts "Version: #{Capelinhos::VERSION}"
50
+ puts "--max-memory is required"
51
+ exit(0)
52
+ end
53
+
54
+ if !options[:max_processes]
55
+ puts "Version: #{Capelinhos::VERSION}"
56
+ puts "--max-processes is required"
57
+ exit(0)
58
+ end
59
+
60
+
61
+ processor = Capelinhos::Processor.new(memory_threshold: options[:max_memory],
62
+ process_limit: options[:max_processes])
63
+ processor.kill_memory_hogs!()
@@ -0,0 +1,72 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'byebug' if ENV['DEBUG']
4
+
5
+ module Capelinhos
6
+ class Processor
7
+ def initialize(memory_threshold:, process_limit:)
8
+ @memory_threshold = memory_threshold
9
+ @process_limit = process_limit
10
+ end
11
+
12
+ def kill_memory_hogs!
13
+ processes_killed = 0
14
+ stats = AdminTools::MemoryStats.new
15
+ processes = stats.passenger_processes
16
+
17
+ if !supports_private_dirty_rss?(processes)
18
+ puts "This system doesn't support private dirty RSS. Falling back to RSS"
19
+ end
20
+
21
+ rss_type = supports_private_dirty_rss?(processes) ? :private_dirty_rss : :rss
22
+
23
+ processes.each do |process|
24
+ if process.name.include?('RubyApp')
25
+ rss_megabytes = process.send(rss_type) / 1024
26
+ if rss_megabytes > @memory_threshold
27
+ shutdown_process(process.pid)
28
+ processes_killed += 1
29
+ end
30
+
31
+ if processes_killed >= @process_limit
32
+ break
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def supports_private_dirty_rss?(processes)
41
+ processes.any?{|p| !p.private_dirty_rss.nil?}
42
+ end
43
+
44
+ def shutdown_process(pid)
45
+ _instance = instance
46
+ request = Net::HTTP::Post.new("/pool/detach_process.json")
47
+ request.body = JSON.generate({pid: pid})
48
+ try_performing_ro_admin_basic_auth(request, _instance)
49
+ response = _instance.http_request("agents.s/core_api", request)
50
+
51
+ if response.code.to_i / 100 == 2
52
+ puts "Successfully detached PID: #{pid}"
53
+ else
54
+ puts "Process (#{pid}) could not be shutdown: #{response.body}"
55
+ end
56
+ end
57
+
58
+ def instance
59
+ instances = InstanceRegistry.new.list
60
+ instances.first
61
+ end
62
+
63
+ def try_performing_ro_admin_basic_auth(request, instance)
64
+ begin
65
+ password = instance.read_only_admin_password
66
+ rescue Errno::EACCES
67
+ return
68
+ end
69
+ request.basic_auth("ro_admin", password)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capelinhos
4
+ VERSION = "0.6.0"
5
+ end
data/lib/capelinhos.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "capelinhos/version"
4
+ require_relative "capelinhos/processor"
5
+
6
+ module Capelinhos
7
+ class Error < StandardError; end
8
+ end
@@ -0,0 +1,4 @@
1
+ module Capelinhos
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capelinhos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Hallett
@@ -55,10 +55,21 @@ dependencies:
55
55
  description:
56
56
  email:
57
57
  - adam.t.hallett@gmail.com
58
- executables: []
58
+ executables:
59
+ - capelinhos
59
60
  extensions: []
60
61
  extra_rdoc_files: []
61
- files: []
62
+ files:
63
+ - CHANGELOG.md
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - capelinhos.sublime-project
68
+ - exe/capelinhos
69
+ - lib/capelinhos.rb
70
+ - lib/capelinhos/processor.rb
71
+ - lib/capelinhos/version.rb
72
+ - sig/capelinhos.rbs
62
73
  homepage: https://github.com/atomical/capelinhos
63
74
  licenses:
64
75
  - MIT