Don't miss

I created an open source development tool to quickly & dynamically mock API endpoints. Check it out at GitHub: DuckRails

Heads up

I will no longer maintain arubystory. For new posts, find me at my personal blog. CU there!

Tuesday, October 14, 2014

Rails - Rescue from memory leak

Lately, I've been trying to find possible memory leaks in a rails 3.2.x application.




NOT easy, believe me...

Anyway, after visiting almost every single result from googling "rails memory leak", I learned something I didn't know. Symbols in ruby never die.

Given that, I wrote some code to track the creation of symbols in the application by rendering a debugging kind of panel at the bottom of my application's layout which kept the previously generated symbols in an array and in each request, if new symbols where created, they were rendered as strings.




Something like this:

class Performance
  @@current_symbols = []

  def self.current_symbols
    @@current_symbols
  end

  def self.new_symbols
    all_symbols = Symbol.all_symbols
    result = all_symbols - @@current_symbols
    @@current_symbols = all_symbols
    result
  end
end

And in my partial something like:
<% Performance.new_symbols.each do |symb| %> <%= symb %> <% end %>

While browsing pages of my application, I noticed that a symbol with a specific pattern was being created:

  __bind_xxxxxxxxx

Searching my code for this pattern had no results so I started searching in my application's gems. And there, it was in rails' active support Proc.rb.

# File activesupport/lib/active_support/core_ext/proc.rb, line 4
  def bind(object)
    block, time = self, Time.now
    object.class_eval do
      method_name = "__bind_#{time.to_i}_#{time.usec}"
      define_method(method_name, &block)
      method = instance_method(method_name)
      remove_method(method_name)
      method
    end.bind(object)
  end
After a lot of debugging, I found out that the cause if this symbol generation was caused by a

  rescue_from Exception do |exception|
    xxx
    xxx
  end

in my application controller.
The code above adds a rescue handler in the controller and when the time comes to handle, the following code from active support rescuable.rb is being executed:
def handler_for_rescue(exception)
      # We go from right to left because pairs are pushed onto rescue_handlers
      # as rescue_from declarations are found.
      _, rescuer = self.class.rescue_handlers.reverse.detect do |klass_name, handler|
        # The purpose of allowing strings in rescue_from is to support the
        # declaration of handler associations for exception classes whose
        # definition is yet unknown.
        #
        # Since this loop needs the constants it would be inconsistent to
        # assume they should exist at this point. An early raised exception
        # could trigger some other handler and the array could include
        # precisely a string whose corresponding constant has not yet been
        # seen. This is why we are tolerant to unknown constants.
        #
        # Note that this tolerance only matters if the exception was given as
        # a string, otherwise a NameError will be raised by the interpreter
        # itself when rescue_from CONSTANT is executed.
        klass = self.class.const_get(klass_name) rescue nil
        klass ||= klass_name.constantize rescue nil
        exception.is_a?(klass) if klass
      end

      case rescuer
      when Symbol
        method(rescuer)
      when Proc
        rescuer.bind(self)
      end
    end

As you can see, if I didn't define my "rescue_from" part as a block but with a symbol of the method that would handle it, I wouldn't have the leak:

rescue_from Exception, :handle_my_exception

protected

def handle_my_exception
  xxx
  xxx
end

Post a Comment