5

I am working on an application that has more than 500 specs when I run these specs on my local machine it successfully passes. But when these specs run on CircleCI, the process is killed. I tried to investigate the problem by tracing the memory on my local machine. I have been surprised when I saw that the ruby process takes over 4gb of memory, and this is what triggers CircleCI to kill the process.

I am unsure as to the reason in which my specs takes all this memory I have already searched for a configuration that cleans the memory after each spec but to no avail.

Here's my rails_helper.rb

require "mongoid-rspec"

require "spec_helper"
ENV["RAILS_ENV"] ||= "test"
require File.expand_path("../../config/environment", __FILE__)
if Rails.env.production?
  abort("The Rails environment is running in production mode!")
end

require "database_cleaner"

require "rspec/rails"

#
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

RSpec.configure do |config|
  config.infer_spec_type_from_file_location!

  # FactoryGirl
  config.include FactoryGirl::Syntax::Methods

  # Render
  config.render_views

  # Filter lines from Rails gems in backtraces.
  config.filter_rails_from_backtrace!
  # arbitrary gems may also be filtered via:
  # config.filter_gems_from_backtrace("gem name")
  config.include Mongoid::Matchers, type: :model

  config.include Requests::JSONHelpers, type: :request
  config.include Requests::AuthHelpers, type: :request

  config.include Requests::JSONHelpers, type: :controller
  config.include Requests::AuthHelpers, type: :controller

  config.before(:suite) do
    DatabaseCleaner.orm = "mongoid"
    DatabaseCleaner.strategy = :truncation, { except: %w[roles] }
    DatabaseCleaner.clean_with(:truncation)
    Rails.application.load_seed # loading seeds
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

  config.after(:suite) do
    DatabaseCleaner.strategy = :truncation
    DatabaseCleaner.clean
  end
end

And Here's my spec_helper.rb

require "mongoid-rspec"
require "webmock/rspec"
require "pundit/rspec"
require "excon"

WebMock.disable_net_connect!(allow_localhost: true)

RSpec::Matchers.define :match_response_schema do |schema|
  match do
    schema_directory = "#{Dir.pwd}/spec/schemas"
    schema_path = "#{schema_directory}/#{schema}.json"
    JSON::Validator.validate!(schema_path, parsed_json, strict: true)
  end
end
RSpec.configure do |config|
  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.shared_context_metadata_behavior = :apply_to_host_groups

  config.include Mongoid::Matchers

  config.before(:all) do
    Excon.defaults[:mock] = true
    Excon.stub({}, body: "{}", status: 200)
  end
end

Update: I fixed it with starting garbage collector after each example

in rails_helper.rb

config.after(:each) do |example|
    GC.start
end

But, I hope to find a solution better than that

Ahmad Rezk
  • 198
  • 1
  • 13

1 Answers1

2

The fact that GC.start helps, suggests it's not a memory leak. Maybe you have some quirky GC settings. If so - try some tuning: https://blog.evanweaver.com/2009/04/09/ruby-gc-tuning/

[I wrote the memory-leak hunting part before I realized that your issue is probably not that, but I'll leave it as it might be useful for some]

What I'd do is to run profiling with ruby-prof, set it to collect MEMORY stats

Here's and example:

require 'ruby-prof'
RubyProf.measure_mode = RubyProf::ALLOCATIONS

result = RubyProf.profile(:track_allocations => true) do
   100.times { rand(1) ? "*" * rand(100) : 20* rand(100) }  
end  

printer = RubyProf::GraphHtmlPrinter.new(result)
printer.print(File.new("/tmp/report.html", "w+"))

The report will look a bit like this [![enter image description here][1]][1]

So, what you'd need to do is something like this:

around(:suite) do |suite|
  require 'ruby-prof'
  RubyProf.measure_mode = RubyProf::ALLOCATIONS

  result = RubyProf.profile(:track_allocations => true) do
   suite.run 
  end

  printer = RubyProf::GraphHtmlPrinter.new(result)
  printer.print(File.new("/tmp/report.html", "w+"))
end

Maybe you'll be able to find which objects are overrepresented, and from there you might find the leak. Experiment with different mearure_modes and printers to get better perspectives. [1]: https://i.stack.imgur.com/2ZFcO.png

Greg
  • 5,862
  • 1
  • 25
  • 52