diff --git a/Gemfile b/Gemfile index 12e9d142932..8645810dec1 100644 --- a/Gemfile +++ b/Gemfile @@ -174,14 +174,13 @@ gem 'rack-mini-profiler', require: false gem 'unicorn', require: false gem 'puma', require: false gem 'rbtrace', require: false, platform: :mri +gem 'gc_tracer', require: false, platform: :mri # required for feed importing and embedding # gem 'ruby-readability', require: false - gem 'simple-rss', require: false -gem 'gctools', require: false, platform: :mri_21 begin gem 'stackprof', require: false, platform: [:mri_21, :mri_22, :mri_23] diff --git a/Gemfile.lock b/Gemfile.lock index 2c80477ca02..6ade9231740 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,7 +113,7 @@ GEM foreman (0.82.0) thor (~> 0.19.1) fspath (2.1.1) - gctools (0.2.3) + gc_tracer (1.5.1) globalid (0.3.7) activesupport (>= 4.1.0) guess_html_encoding (0.0.11) @@ -414,7 +414,7 @@ DEPENDENCIES fast_xs flamegraph foreman - gctools + gc_tracer highline hiredis htmlentities diff --git a/lib/middleware/unicorn_oobgc.rb b/lib/middleware/unicorn_oobgc.rb deleted file mode 100644 index 0764b27e51e..00000000000 --- a/lib/middleware/unicorn_oobgc.rb +++ /dev/null @@ -1,141 +0,0 @@ -# THIS FILE IS TO BE EXTRACTED FROM DISCOURSE IT IS LICENSED UNDER THE MIT LICENSE -# -# The MIT License (MIT) -# -# Copyright (c) 2013 Discourse -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -# Hook into unicorn, unicorn middleware, not rack middleware -# -# Since we need no knowledge about the request we can simply -# hook unicorn -module Middleware::UnicornOobgc - - MIN_REQUESTS_PER_OOBGC = 3 - - # TUNE ME, for Discourse this number is good - MIN_FREE_SLOTS = 50_000 - - # The oobgc implementation is far more efficient in 2.1 - # as we have a bunch of profiling hooks to hook it - # use @tmm1s implementation - def use_gctools? - if @use_gctools.nil? - @use_gctools = - if RUBY_VERSION >= "2.1.0" - require "gctools/oobgc" - true - else - false - end - end - @use_gctools - end - - def verbose(msg=nil) - @verbose ||= ENV["OOBGC_VERBOSE"] == "1" ? :true : :false - if @verbose == :true - if(msg) - puts msg - end - - true - end - end - - def self.init - # hook up HttpServer intercept - ObjectSpace.each_object(Unicorn::HttpServer) do |s| - s.extend(self) - end - rescue - puts "Attempted to patch Unicorn but it is not loaded" - end - - # the closer this is to the GC run the more accurate it is - def estimate_live_num_at_gc(stat) - stat[:heap_live_num] + stat[:heap_free_num] - end - - def process_client(client) - - if use_gctools? - super(client) - GC::OOB.run - return - end - - stat = GC.stat - - @num_requests ||= 0 - @num_requests += 1 - - gc_count = stat[:count] - live_num = stat[:heap_live_num] - - @expect_gc_at ||= estimate_live_num_at_gc(stat) - - super(client) # Unicorn::HttpServer#process_client - - # at this point client is serviced - stat = GC.stat - new_gc_count = stat[:count] - new_live_num = stat[:heap_live_num] - - # no GC happened during the request - if new_gc_count == gc_count - delta = new_live_num - live_num - - @max_delta ||= delta - - if delta > @max_delta - new_delta = (@max_delta * 1.5).to_i - @max_delta = [new_delta, delta].min - else - # this may seem like a very tiny decay rate, but some apps using caching - # can really mess stuff up, if our delta is too low the algorithm fails - new_delta = (@max_delta * 0.99).to_i - @max_delta = [new_delta, delta].max - end - - if @max_delta < MIN_FREE_SLOTS - @max_delta = MIN_FREE_SLOTS - end - - if @num_requests > MIN_REQUESTS_PER_OOBGC && @max_delta * 2 + new_live_num > @expect_gc_at - t = Time.now - GC.start - stat = GC.stat - @expect_gc_at = estimate_live_num_at_gc(stat) - verbose "OobGC hit pid: #{Process.pid} req: #{@num_requests} max delta: #{@max_delta} expect at: #{@expect_gc_at} #{((Time.now - t) * 1000).to_i}ms saved" - @num_requests = 0 - end - else - - verbose "OobGC miss pid: #{Process.pid} reqs: #{@num_requests} max delta: #{@max_delta}" - - @num_requests = 0 - @expect_gc_at = estimate_live_num_at_gc(stat) - - end - - end - -end