tunemygc 1.0.1

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.
@@ -0,0 +1,156 @@
1
+ #include "tunemygc_ext.h"
2
+
3
+ VALUE rb_mTunemygc;
4
+ static ID id_tunemygc_tracepoint;
5
+ static ID id_tunemygc_raw_snapshot;
6
+
7
+ static VALUE sym_gc_cycle_started;
8
+ static VALUE sym_gc_cycle_ended;
9
+
10
+ /* For 2.2.x incremental GC */
11
+ #ifdef RUBY_INTERNAL_EVENT_GC_ENTER
12
+ static VALUE sym_gc_cycle_entered;
13
+ static VALUE sym_gc_cycle_exited;
14
+ #endif
15
+
16
+ /* From @tmm1/gctools */
17
+ static double _tunemygc_walltime()
18
+ {
19
+ struct timespec ts;
20
+ #ifdef HAVE_CLOCK_GETTIME
21
+ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
22
+ rb_sys_fail("clock_gettime");
23
+ }
24
+ #else
25
+ {
26
+ struct timeval tv;
27
+ if (gettimeofday(&tv, 0) < 0) {
28
+ rb_sys_fail("gettimeofday");
29
+ }
30
+ ts.tv_sec = tv.tv_sec;
31
+ ts.tv_nsec = tv.tv_usec * 1000;
32
+ }
33
+ #endif
34
+ return ts.tv_sec + ts.tv_nsec * 1e-9;
35
+ }
36
+
37
+ static VALUE tunemygc_walltime(VALUE mod)
38
+ {
39
+ return DBL2NUM(_tunemygc_walltime());
40
+ }
41
+
42
+ /* Postponed job callback that fires when the VM is in a consistent state again (sometime
43
+ * after the GC cycle, notably RUBY_INTERNAL_EVENT_GC_END_SWEEP)
44
+ */
45
+ static void tunemygc_invoke_gc_snapshot(void *data)
46
+ {
47
+ tunemygc_stat_record *stat = (tunemygc_stat_record *)data;
48
+ VALUE snapshot = tunemygc_get_stat_record(stat);
49
+ rb_funcall(rb_mTunemygc, id_tunemygc_raw_snapshot, 1, snapshot);
50
+ free(stat);
51
+ }
52
+
53
+ /* GC tracepoint hook. Snapshots GC state using new low level helpers which are safe
54
+ * to call from within tracepoint handlers as they don't allocate and change the heap state
55
+ */
56
+ static void tunemygc_gc_hook_i(VALUE tpval, void *data)
57
+ {
58
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
59
+ rb_event_flag_t flag = rb_tracearg_event_flag(tparg);
60
+
61
+ tunemygc_stat_record *stat = ((tunemygc_stat_record*)malloc(sizeof(tunemygc_stat_record)));
62
+ stat->ts = _tunemygc_walltime();
63
+ stat->peak_rss = getPeakRSS();
64
+ stat->current_rss = getCurrentRSS();
65
+
66
+ switch (flag) {
67
+ case RUBY_INTERNAL_EVENT_GC_START:
68
+ stat->stage = sym_gc_cycle_started;
69
+ break;
70
+ case RUBY_INTERNAL_EVENT_GC_END_SWEEP:
71
+ stat->stage = sym_gc_cycle_ended;
72
+ break;
73
+ #ifdef RUBY_INTERNAL_EVENT_GC_ENTER
74
+ case RUBY_INTERNAL_EVENT_GC_ENTER:
75
+ stat->stage = sym_gc_cycle_entered;
76
+ break;
77
+ case RUBY_INTERNAL_EVENT_GC_EXIT:
78
+ stat->stage = sym_gc_cycle_exited;
79
+ break;
80
+ #endif
81
+ }
82
+
83
+ tunemygc_set_stat_record(stat);
84
+ rb_postponed_job_register(0, tunemygc_invoke_gc_snapshot, (void *)stat);
85
+ }
86
+
87
+ /* Installs the GC tracepoint and declare interest only in start of the cycle and end of sweep
88
+ * events
89
+ */
90
+ static VALUE tunemygc_install_gc_tracepoint(VALUE mod)
91
+ {
92
+ rb_event_flag_t events;
93
+ VALUE tunemygc_tracepoint = rb_ivar_get(rb_mTunemygc, id_tunemygc_tracepoint);
94
+ if (!NIL_P(tunemygc_tracepoint)) {
95
+ rb_tracepoint_disable(tunemygc_tracepoint);
96
+ rb_ivar_set(rb_mTunemygc, id_tunemygc_tracepoint, Qnil);
97
+ }
98
+ events = RUBY_INTERNAL_EVENT_GC_START | RUBY_INTERNAL_EVENT_GC_END_SWEEP;
99
+ tunemygc_tracepoint = rb_tracepoint_new(0, events, tunemygc_gc_hook_i, (void *)0);
100
+ if (NIL_P(tunemygc_tracepoint)) rb_warn("Could not install GC tracepoint!");
101
+ rb_tracepoint_enable(tunemygc_tracepoint);
102
+ rb_ivar_set(rb_mTunemygc, id_tunemygc_tracepoint, tunemygc_tracepoint);
103
+ return Qnil;
104
+ }
105
+
106
+ /* Removes a previously enabled GC tracepoint */
107
+ static VALUE tunemygc_uninstall_gc_tracepoint(VALUE mod)
108
+ {
109
+ VALUE tunemygc_tracepoint = rb_ivar_get(rb_mTunemygc, id_tunemygc_tracepoint);
110
+ if (!NIL_P(tunemygc_tracepoint)) {
111
+ rb_tracepoint_disable(tunemygc_tracepoint);
112
+ rb_ivar_set(rb_mTunemygc, id_tunemygc_tracepoint, Qnil);
113
+ }
114
+ return Qnil;
115
+ }
116
+
117
+ static VALUE tunemygc_peak_rss(VALUE mod)
118
+ {
119
+ return SIZET2NUM(getPeakRSS());
120
+ }
121
+
122
+ static VALUE tunemygc_current_rss(VALUE mod)
123
+ {
124
+ return SIZET2NUM(getCurrentRSS());
125
+ }
126
+
127
+ void Init_tunemygc_ext()
128
+ {
129
+ /* Warm up the symbol table */
130
+ id_tunemygc_tracepoint = rb_intern("__tunemygc_tracepoint");
131
+ id_tunemygc_raw_snapshot = rb_intern("raw_snapshot");
132
+ rb_funcall(rb_mGC, rb_intern("stat"), 0);
133
+ rb_funcall(rb_mGC, rb_intern("latest_gc_info"), 0);
134
+
135
+ /* Symbol warmup */
136
+ sym_gc_cycle_started = ID2SYM(rb_intern("GC_CYCLE_STARTED"));
137
+ sym_gc_cycle_ended = ID2SYM(rb_intern("GC_CYCLE_ENDED"));
138
+
139
+ /* For 2.2.x incremental GC */
140
+ #ifdef RUBY_INTERNAL_EVENT_GC_ENTER
141
+ sym_gc_cycle_entered = ID2SYM(rb_intern("GC_CYCLE_ENTERED"));
142
+ sym_gc_cycle_exited = ID2SYM(rb_intern("GC_CYCLE_EXITED"));
143
+ #endif
144
+
145
+ tunemygc_setup_trace_symbols();
146
+
147
+ rb_mTunemygc = rb_define_module("TuneMyGc");
148
+ rb_ivar_set(rb_mTunemygc, id_tunemygc_tracepoint, Qnil);
149
+
150
+ rb_define_module_function(rb_mTunemygc, "install_gc_tracepoint", tunemygc_install_gc_tracepoint, 0);
151
+ rb_define_module_function(rb_mTunemygc, "uninstall_gc_tracepoint", tunemygc_uninstall_gc_tracepoint, 0);
152
+
153
+ rb_define_module_function(rb_mTunemygc, "walltime", tunemygc_walltime, 0);
154
+ rb_define_module_function(rb_mTunemygc, "peak_rss", tunemygc_peak_rss, 0);
155
+ rb_define_module_function(rb_mTunemygc, "current_rss", tunemygc_current_rss, 0);
156
+ }
@@ -0,0 +1,21 @@
1
+ #ifndef TUNEMYGC_EXT_H
2
+ #define TUNEMYGC_EXT_H
3
+
4
+ #include "ruby/ruby.h"
5
+ #include "ruby/debug.h"
6
+
7
+ extern VALUE rb_mTunemygc;
8
+
9
+ #include <stddef.h>
10
+ /* for walltime */
11
+ #include <time.h>
12
+ #include <sys/time.h>
13
+
14
+ /* header we codegen'ed in extconf.rb from VM specific GC stats */
15
+ #include "tunemygc_env.h"
16
+
17
+ /* From getRSS.c */
18
+ size_t getPeakRSS();
19
+ size_t getCurrentRSS();
20
+
21
+ #endif
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ require "tunemygc/tunemygc_ext"
4
+ require "tunemygc/interposer"
5
+ require "tunemygc/snapshotter"
6
+ require "logger"
7
+
8
+ module TuneMyGc
9
+ MUTEX = Mutex.new
10
+
11
+ attr_accessor :logger, :interposer, :snapshotter
12
+
13
+ def snapshot(stage, timestamp = nil, meta = nil)
14
+ snapshotter.take(stage, timestamp, meta)
15
+ end
16
+
17
+ def raw_snapshot(snapshot)
18
+ snapshotter.take_raw(snapshot)
19
+ end
20
+
21
+ def log(message)
22
+ logger.info "[TuneMyGC] #{message}"
23
+ end
24
+
25
+ def reccommendations
26
+ MUTEX.synchronize do
27
+ require "tunemygc/syncer"
28
+ syncer = TuneMyGc::Syncer.new
29
+ config = syncer.sync(snapshotter)
30
+ require "tunemygc/configurator"
31
+ TuneMyGc::Configurator.new(config).configure
32
+ end
33
+ rescue Exception => e
34
+ log "Config reccommendation error (#{e.message})"
35
+ end
36
+
37
+ extend self
38
+
39
+ MUTEX.synchronize do
40
+ self.logger = Logger.new($stdout)
41
+ self.interposer = TuneMyGc::Interposer.new
42
+ self.snapshotter = TuneMyGc::Snapshotter.new
43
+ end
44
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/http'
4
+ require 'certified'
5
+ require 'timeout'
6
+ require 'optparse'
7
+
8
+ module TuneMyGc
9
+ class CLI
10
+ TIMEOUT = 10
11
+
12
+ attr_reader :uri, :client, :options
13
+
14
+ def self.start(args)
15
+ args = ["-h"] if args.empty?
16
+ options = {}
17
+ OptionParser.new do |opts|
18
+ opts.banner = "Usage: tunemygc [options]"
19
+
20
+ opts.on("-r ", "--register EMAIL", "Register this Rails app with the https://tunemygc.com service") do |email|
21
+ options[:email] = email
22
+ end
23
+ opts.on("-c ", "--config TOKEN", "Fetch the last known config for a given Rails app") do |token|
24
+ options[:config] = token
25
+ end
26
+ opts.on_tail("-h", "--help", "How to use the TuneMyGC agent CLI") do
27
+ puts opts
28
+ exit
29
+ end
30
+ end.parse!(args)
31
+ new(options)
32
+ end
33
+
34
+ def initialize(options)
35
+ @options = options
36
+ @uri = URI("http://#{TuneMyGc::HOST}")
37
+ @client = Net::HTTP.new(@uri.host, @uri.port)
38
+ @client.use_ssl = (uri.port == 443)
39
+ @client.read_timeout = TIMEOUT
40
+ register if options[:email]
41
+ fetch_config if options[:config]
42
+ end
43
+
44
+ def register
45
+ timeout do
46
+ registration = Net::HTTP::Post.new('/accounts')
47
+ registration.set_form_data(:email => options[:email])
48
+ response = client.request(registration)
49
+ if Net::HTTPUnprocessableEntity === response
50
+ puts "Registration error: #{response.body}"
51
+ elsif Net::HTTPSuccess === response
52
+ puts "Application registered. Use RUBY_GC_TOKEN=#{response.body} in your environment."
53
+ else
54
+ puts "Registration error: #{response.body}"
55
+ end
56
+ end
57
+ rescue Exception => e
58
+ puts "Registration error: #{e.inspect}"
59
+ end
60
+
61
+ def fetch_config
62
+ timeout do
63
+ config = Net::HTTP::Get.new("/apps/#{options[:config]}")
64
+ response = client.request(config)
65
+ if Net::HTTPNoContent === response
66
+ puts "There is no configuration for Rails app with token #{options[:config]} yet"
67
+ elsif Net::HTTPNotFound === response
68
+ puts "Rails app with token #{options[:config]} doesn't exist"
69
+ elsif Net::HTTPSuccess === response
70
+ puts "=== Suggested GC configuration:"
71
+ puts
72
+ puts response.body
73
+ else
74
+ puts "Config retrieval error: #{response.body}"
75
+ end
76
+ end
77
+ rescue Exception => e
78
+ puts "Config retrieval error: #{e.inspect}"
79
+ end
80
+
81
+ private
82
+ def timeout(&block)
83
+ Timeout.timeout(TIMEOUT + 1){ block.call }
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ module TuneMyGc
4
+ class Configurator
5
+ attr_reader :config
6
+
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ def configure
12
+ if Hash === config && !config.empty?
13
+ TuneMyGc.log "==== Recommended GC configs from #{config.delete("callback")}"
14
+ write_env("Speed")
15
+ end
16
+ end
17
+
18
+ private
19
+ def write_env(strategy)
20
+ config.delete(strategy).each do |var,val|
21
+ TuneMyGc.log "export #{var}=#{val}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ require 'active_support'
4
+ require 'tunemygc/spies/action_controller'
5
+
6
+ module TuneMyGc
7
+ class Interposer
8
+ attr_reader :spy
9
+ attr_accessor :installed
10
+
11
+ def initialize
12
+ reset
13
+ end
14
+
15
+ def on_initialized
16
+ GC.start(full_mark: true, :immediate_sweep => true)
17
+ TuneMyGc.install_gc_tracepoint
18
+ TuneMyGc.log "hooked: GC tracepoints"
19
+ TuneMyGc.snapshot(:BOOTED)
20
+
21
+ TuneMyGc.interposer.spy.install
22
+ end
23
+
24
+ def install
25
+ return if @installed
26
+ TuneMyGc.log "interposing"
27
+ ActiveSupport.on_load(:after_initialize) do
28
+ TuneMyGc.interposer.on_initialized
29
+ end
30
+ TuneMyGc.log "hooked: after_initialize"
31
+
32
+ at_exit do
33
+ if @installed
34
+ TuneMyGc.log "at_exit"
35
+ @spy.uninstall
36
+ TuneMyGc.snapshot(:TERMINATED)
37
+ TuneMyGc.reccommendations
38
+ end
39
+ end
40
+ TuneMyGc.log "hooked: at_exit"
41
+ @installed = true
42
+ TuneMyGc.log "interposed"
43
+ end
44
+
45
+ def check_uninstall
46
+ @spy.check_uninstall
47
+ end
48
+
49
+ def uninstall
50
+ @spy.uninstall
51
+ reset
52
+ end
53
+
54
+ private
55
+ def reset
56
+ @installed = false
57
+ @spy = TuneMyGc::Spies::ActionController.new
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tunemygc/agent'
4
+ require 'rails/railtie'
5
+
6
+ module TuneMyGc
7
+ class Railtie < Rails::Railtie
8
+ initializer 'tunemygc' do
9
+ TuneMyGc.logger = Rails.logger
10
+ TuneMyGc.interposer.install
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tunemygc/subscriber'
4
+
5
+ module TuneMyGc
6
+ class StartRequestSubscriber < Subscriber
7
+ def start(name, id, payload)
8
+ TuneMyGc.snapshot(:REQUEST_PROCESSING_STARTED)
9
+ end
10
+ end
11
+
12
+ class EndRequestSubscriber < Subscriber
13
+ def finish(name, id, payload)
14
+ TuneMyGc.snapshot(:REQUEST_PROCESSING_ENDED)
15
+ TuneMyGc.interposer.check_uninstall
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'thread'
4
+
5
+ module TuneMyGc
6
+ class Snapshotter
7
+ MAX_SAMPLES = 1000
8
+
9
+ attr_reader :buffer
10
+
11
+ def initialize(buf = Queue.new)
12
+ @buffer = buf
13
+ end
14
+
15
+ def take(stage, timestamp = nil, meta = nil)
16
+ _buffer([(timestamp || TuneMyGc.walltime), TuneMyGc.peak_rss, TuneMyGc.current_rss, stage, GC.stat, GC.latest_gc_info, meta])
17
+ end
18
+
19
+ # low level interface, for tests and GC callback
20
+ def take_raw(snapshot)
21
+ _buffer(snapshot)
22
+ end
23
+
24
+ def clear
25
+ @buffer.clear
26
+ end
27
+
28
+ def size
29
+ @buffer.size
30
+ end
31
+
32
+ def deq
33
+ @buffer.deq
34
+ end
35
+
36
+ def empty?
37
+ @buffer.empty?
38
+ end
39
+
40
+ private
41
+ def _buffer(snapshot)
42
+ if size < MAX_SAMPLES
43
+ @buffer << snapshot
44
+ else
45
+ TuneMyGc.log "Discarding snapshot #{snapshot.inspect} (max samples threshold of #{MAX_SAMPLES} reached)"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tunemygc/request_subscriber'
4
+
5
+ module TuneMyGc
6
+ module Spies
7
+ class ActionController
8
+ attr_reader :subscriptions
9
+
10
+ def initialize
11
+ @subscriptions = []
12
+ @requests_processed = 0
13
+ @requests_limit = nil
14
+ end
15
+
16
+ def install
17
+ @subscriptions << ActiveSupport::Notifications.subscribe(/^start_processing.action_controller$/, TuneMyGc::StartRequestSubscriber.new)
18
+ TuneMyGc.log "hooked: start_processing.action_controller"
19
+
20
+ @subscriptions << ActiveSupport::Notifications.subscribe(/^process_action.action_controller$/, TuneMyGc::EndRequestSubscriber.new)
21
+ TuneMyGc.log "hooked: process_action.action_controller"
22
+ end
23
+
24
+ def uninstall
25
+ TuneMyGc.uninstall_gc_tracepoint
26
+ TuneMyGc.log "uninstalled GC tracepoint"
27
+ @subscriptions.each{|s| ActiveSupport::Notifications.unsubscribe(s) }
28
+ @subscriptions.clear
29
+ TuneMyGc.log "cleared ActiveSupport subscriptions"
30
+ end
31
+
32
+ def check_uninstall
33
+ if ENV["RUBY_GC_TUNE_REQUESTS"]
34
+ @requests_limit ||= Integer(ENV["RUBY_GC_TUNE_REQUESTS"])
35
+ @requests_processed += 1
36
+ if @requests_processed == @requests_limit
37
+ uninstall
38
+ TuneMyGc.log "kamikaze after #{@requests_processed} of #{@requests_limit} requests"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ module TuneMyGc
4
+ class Subscriber
5
+ def start(name, id, payload)
6
+ end
7
+
8
+ def finish(name, id, payload)
9
+ end
10
+
11
+ def publish(name, *args)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/http'
4
+ require 'certified'
5
+ require 'timeout'
6
+
7
+ module TuneMyGc
8
+ class Syncer
9
+ TIMEOUT = 10 #seconds
10
+ ENVIRONMENT = [ENV['RUBY_GC_TOKEN'], RUBY_VERSION, Rails.version, ENV.select {|k,v| k =~ /RUBY_GC_/ }, TuneMyGc::VERSION, GC::OPTS, GC::INTERNAL_CONSTANTS].freeze
11
+
12
+ attr_reader :uri, :client
13
+
14
+ def initialize(host = TuneMyGc::HOST)
15
+ @uri = URI("http://#{host}/ruby")
16
+ @client = Net::HTTP.new(@uri.host, @uri.port)
17
+ @client.use_ssl = (uri.port == 443)
18
+ @client.read_timeout = TIMEOUT
19
+ end
20
+
21
+ def sync(snapshotter)
22
+ response = nil
23
+ timeout do
24
+ response = sync_with_tuner(snapshotter)
25
+ end
26
+ timeout do
27
+ process_config_callback(response)
28
+ end if response
29
+ end
30
+
31
+ private
32
+ def timeout(&block)
33
+ Timeout.timeout(TIMEOUT + 1){ block.call }
34
+ end
35
+
36
+ def sync_with_tuner(snapshotter)
37
+ snapshots = 0
38
+ # Fallback to Timeout if Net::HTTP read timeout fails
39
+ snapshots = snapshotter.size
40
+ TuneMyGc.log "Syncing #{snapshots} snapshots"
41
+ payload = [ENVIRONMENT]
42
+ debug = ENV["RUBY_GC_TUNE_DEBUG"]
43
+ TuneMyGc.log "=== Snapshots ===" if debug
44
+ while !snapshotter.empty?
45
+ snapshot = snapshotter.deq
46
+ TuneMyGc.log(snapshot) if debug
47
+ payload << snapshot
48
+ end
49
+ data = ActiveSupport::JSON.encode(payload)
50
+ response = client.post(uri.path, data, TuneMyGc::HEADERS)
51
+ if Net::HTTPNotFound === response
52
+ TuneMyGc.log "Invalid application token. Please generate one with 'bundle exec tunemygc <a_valid_email_address>' and set the RUBY_GC_TOKEN environment variable"
53
+ return false
54
+ elsif Net::HTTPNotImplemented === response
55
+ TuneMyGc.log "Ruby version #{RUBY_VERSION} or Rails version #{Rails.version} not supported. Failed to sync #{snapshots} snapshots"
56
+ return false
57
+ elsif Net::HTTPUpgradeRequired === response
58
+ TuneMyGc.log "Agent version #{response.body} required - please upgrade. Failed to sync #{snapshots} snapshots"
59
+ return false
60
+ elsif Net::HTTPPreconditionFailed === response
61
+ TuneMyGc.log "The GC is already tuned by environment variables (#{response.body}) - we respect that, doing nothing. Failed to sync #{snapshots} snapshots"
62
+ return false
63
+ elsif Net::HTTPBadRequest === response
64
+ TuneMyGc.log "Invalid payload (#{response.body}). Failed to sync #{snapshots} snapshots"
65
+ return false
66
+ elsif Net::HTTPInternalServerError === response
67
+ TuneMyGc.log "An internal error occurred (#{response.body}). Failed to sync #{snapshots} snapshots"
68
+ return false
69
+ elsif Net::HTTPSuccess === response
70
+ response
71
+ else
72
+ TuneMyGc.log "Unknown error: #{response.body}"
73
+ return false
74
+ end
75
+ rescue Exception => e
76
+ TuneMyGc.log "Failed to sync #{snapshots} snapshots (error: #{e})"
77
+ return false
78
+ ensure
79
+ # Prefer to loose data points than accumulate buffers indefinitely on error or other conditions
80
+ snapshotter.clear
81
+ end
82
+
83
+ def process_config_callback(response)
84
+ config = client.get(URI(response.body).path)
85
+ ActiveSupport::JSON.decode(config.body).merge('callback' => response.body)
86
+ rescue Exception => e
87
+ TuneMyGc.log "Failed to process config callback url #{response.body} (error: #{e})"
88
+ return false
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module TuneMyGc
4
+ VERSION = "1.0.1"
5
+ end
data/lib/tunemygc.rb ADDED
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ tunemygc_min_ruby_version = "2.1.0"
4
+
5
+ if RUBY_VERSION >= tunemygc_min_ruby_version
6
+ require "tunemygc/version" unless defined? TuneMyGc::VERSION
7
+
8
+ module TuneMyGc
9
+ HOST = (ENV['RUBY_GC_TUNE_HOST'] || "tunemygc.com:443").freeze
10
+ HEADERS = { "Content-Type" => "application/json",
11
+ "Accept" => "application/json",
12
+ "User-Agent" => "TuneMyGC #{TuneMyGc::VERSION}"}.freeze
13
+ end
14
+
15
+ if ENV["RUBY_GC_TUNE"] && defined?(Rails) && Rails.version >= "4.0"
16
+ require 'tunemygc/railtie'
17
+ else
18
+ puts "[TuneMyGC] not enabled"
19
+ end
20
+ else
21
+ puts "[TuneMyGC] requires a Ruby version #{tunemygc_min_ruby_version} or newer"
22
+ end
data/test/fixtures.rb ADDED
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'time'
4
+
5
+ module Fixtures
6
+ STAGE_BOOTED = [1420152606.1162581, "BOOTED", {"count"=>32, "heap_used"=>950, "heap_length"=>1519, "heap_increment"=>569, "heap_live_slot"=>385225, "heap_free_slot"=>2014, "heap_final_slot"=>0, "heap_swept_slot"=>101119, "heap_eden_page_length"=>950, "heap_tomb_page_length"=>0, "total_allocated_object"=>2184137, "total_freed_object"=>1798912, "malloc_increase"=>9665288, "malloc_limit"=>16777216, "minor_gc_count"=>26, "major_gc_count"=>6, "remembered_shady_object"=>5145, "remembered_shady_object_limit"=>6032, "old_object"=>230164, "old_object_limit"=>301030, "oldmalloc_increase"=>11715304, "oldmalloc_limit"=>24159190}, {"major_by"=>nil, "gc_by"=>"newobj", "have_finalizer"=>false, "immediate_sweep"=>false}, nil]
7
+
8
+ CONFIG = {"Memory"=>{"RUBY_GC_HEAP_INIT_SLOTS"=>307562,"RUBY_GC_HEAP_FREE_SLOTS"=>6151,"RUBY_GC_HEAP_GROWTH_FACTOR"=>0.07,"RUBY_GC_HEAP_GROWTH_MAX_SLOTS"=>6151,"RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR"=>0.11,"RUBY_GC_MALLOC_LIMIT"=>2000000,"RUBY_GC_MALLOC_LIMIT_MAX"=>4000000,"RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR"=>0.11,"RUBY_GC_OLDMALLOC_LIMIT"=>2000000,"RUBY_GC_OLDMALLOC_LIMIT_MAX"=>4000000,"RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR"=>0.11},"Speed"=>{"RUBY_GC_HEAP_INIT_SLOTS"=>369074,"RUBY_GC_HEAP_FREE_SLOTS"=>184537,"RUBY_GC_HEAP_GROWTH_FACTOR"=>1.2,"RUBY_GC_HEAP_GROWTH_MAX_SLOTS"=>123024,"RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR"=>2.0,"RUBY_GC_MALLOC_LIMIT"=>64000000,"RUBY_GC_MALLOC_LIMIT_MAX"=>128000000,"RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR"=>1.2,"RUBY_GC_OLDMALLOC_LIMIT"=>64000000,"RUBY_GC_OLDMALLOC_LIMIT_MAX"=>128000000,"RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR"=>1.2}, "callback"=>"https://tunemygc.com/configs/8d07e13cc5a7bba2da3510b9ca5e75f4.json"}
9
+ end