#212

memory usage?

Status: Fixed
Priority: Should have
Milestone: 1.1
Component: Changesets & ...
 
Dimitrij Denissenko
Assigned To:

Jun 01 2007 * 13:48
Ming-Wei Shih

The memory usage increases fast. The box will OOM after few days. Thsi is after more than 1 day:

root     26087  6.1 21.7 119776 113976 ?       S    May30 158:08 /usr/bin/ruby18 /usr/bin/mongrel_rails start -d -e production

after a restart: bc. root 12866 22.0 3.9 23716 20624 ? R 13:47 0:00 /usr/bin/ruby18 /usr/bin/mongrel_rails start -d -e production

This this the expected behaviour?

Comments and Changes

Jun 02 2007 * 16:20
Dimitrij
  • Status changed from Open to WorksForMe
  • Assigned user set to dim

Hi!

Yes, this is the expected behaviour when you are working with with SVN repositories. Retrospectiva (as a typical RubyOnRails application) uses ~40M of RAM, the rest is consumed by the Ruby-SVN bindings (for various reasons). The memory consumption is directly proportional to the size of your repository, but your demand of ~100M of RAM seems quite reasonable to me.

Please don’t worry being Out Of Memory after a few days, the memory allocation will not grow any further.

Regards
Dimitrij

Anonymous
Jun 05 2007 * 00:44
Anonymous

Hi Dimitrij, It does grow further and seems never to stop

3926 root 16 0 168m 154m 3184 S 0.0 30.1 100:26.82 mongrel_rails

Is the SVN binding keeping a cache? And It does get OOM and the boxe crashed once a few days.

Jun 05 2007 * 14:09
Dimitrij Denissenko
  • Status changed from WorksForMe to Open
  • Milestone set to 1.0

Please have a look on lib/patches.rb, line 76

Try to change

  @@gc_count = (@@gc_count + 1) % 20

to a lower value, eg:

  @@gc_count = (@@gc_count + 1) % 10

This should slow-down your memory growth (and the SVN processing speed :-().

Dimitrij

@Kou: Do you have any idea how to solve this problem in the long term?

Jun 08 2007 * 09:57
Ming-Wei Shih

I have done this

 @@gc_count = (@@gc_count + 1) % 10

and the memory usage is increasing much slower, after 2 days:

root     27862  0.8 20.6 163004 108344 ?       S    Jun06  28:43 /usr/bin/ruby18 /usr/bin/mongrel_rails start -d -e production
Jun 12 2007 * 06:55
Kouhei Sutou

Is it really Subversion bindings problem? I can’t confirm that Subversion related features (log list, diff and so on) increase memory usage.

I think we need to profile which feature increases memory usage.

Jun 12 2007 * 10:34
Dimitrij

Hi Kou!

Yes, the problem is definitely SVN related, since the memory consumption grows with the number of allocated Pools and Roots, but please don’t worry about this problem anymore, I’ve solved it over the weekend and also created a test to confirm it’s resolution. I will commit the both within the next few days.

Dimitrij

Jun 12 2007 * 13:47
Kouhei Sutou

Great!

If I should fix the Subversion bindings, please tell me.

Jun 16 2007 * 09:14
Dimitrij

Well, I was too enthusiastic, my patch fixes a specific memory leak, but unfortunately opens a new one.

@Ming-Wei: Could you please add the following code to your environment.rb?

module MemoryLogging
  def self.included(base)
    base.send(:include, InstanceMethods)
    base.send(:after_filter, :log_mem_stat)
  end

  module InstanceMethods
    protected

      def log_mem_stat(additional_position_message = nil)
        return unless PLATFORM =~ /linux/
        vm_info = File.read("/proc/self/status").grep(/^Vm(RSS|Size)/).map do |l| 
          l.chomp.gsub(/\t/, ' ').gsub(/ +/, ' ')
        end
        rss, size = vm_info.map{|l| l.scan(/\d+/).first.to_i}
        rss_diff = $vm_rss ? rss - $vm_rss : 0
        size_diff = $vm_size ? size - $vm_size : 0

        if rss_diff.abs > 300 || size_diff.abs > 300
          info = "PID #{$$}, #{vm_info.join(', ')}, Size Diff #{size_diff} kB, RSS Diff #{rss_diff} kB" 
          additional_position_message ||= request.env['REQUEST_URI']
          mem_logger.info "#{info} - #{additional_position_message}" 
        end
        $vm_rss, $vm_size = rss, size
      end

      def mem_logger
        @mem_logger ||= Logger.new(File.join(RAILS_ROOT, 'log', 'memlog.log'))
      end

  end
end
ActionController::Base.send(:include, MemoryLogging)

Could you please let you Retrospectiva run for at least 24h (don’t forget to delete tmp/cache first) and send me the resulting log/memlog.log. My memory consumption does no grow as fast as yours and I’d like to investigate why.

Thanks
Dimitrij

Jun 16 2007 * 09:57
Dimitrij Denissenko

@Kou:

I’ve tested my memory allocation using Subversion 1.4.2 & 1.4.3 It seems that 1.4.3 works much more efficiently (less memory consumption). Could you think of a specific reason for that?

Cheers
Dimitrij

Jun 16 2007 * 15:45
Kouhei Sutou

There are any the Ruby bindings changes between 1.4.2 and 1.4.3. Subversion 1.4.3 may be improved memory usage.

Jun 16 2007 * 16:14
Kouhei Sutou

There are → There aren’t

Jun 18 2007 * 15:03
Ming-Wei Shih

ok, I’ve added that block.

root     12175 18.5  3.9  23716 20640 ?        R    15:01   0:00 /usr/bin/ruby18 /usr/bin/mongrel_rails start -d -e production
Jun 18 2007 * 15:09
Ming-Wei Shih

cleaned the tmp/cache, restarted mongrel, I will send the log file.

Jun 20 2007 * 14:09
Ming-Wei Shih

the memory.log

Jun 30 2007 * 12:40
Dimitrij Denissenko
  • Status changed from Open to Fixed
  • Milestone changed from 1.0 to 1.1

Hi again!

I did a full rewrite of the repository handling in [140]. My own memory consumption has dropped by 50% and seems to remain stable, please give me some feedback about how the changes are working out for you. I will create a backport for the 1.0 branch, once I know that this solution works.

If you’d like to trace the memory footprint add the following code to the config/environment.rb:

module MemoryLogging
  def self.included(base)
    base.send(:include, InstanceMethods)
    base.send(:after_filter, :log_mem_stat)
  end

  module InstanceMethods
    protected

      @@vm_diff_logger = Logger.new(File.join(RAILS_ROOT, 'log', 'mem-diff.log'))
      @@vm_time_logger = Logger.new(File.join(RAILS_ROOT, 'log', 'mem-time.log'))

      @@vm_mstart = Time.now
      @@vm_time_iv = 60.minutes
      @@vm_time_ts = @@vm_mstart + @@vm_time_iv
      @@vm_reqs = 0

      def log_mem_stat
        return unless PLATFORM =~ /linux/
        @@vm_reqs += 1

        vm_info = File.read("/proc/self/status").grep(/^Vm(RSS|Size)/).map do |l| 
          l.chomp.gsub(/\t/, ' ').gsub(/ +/, ' ')
        end        
        virt, res    = vm_info.map{|l| l.scan(/\d+/).first.to_i}
        vdiff, rdiff = ($vm_virt ? virt - $vm_virt : 0), ($vm_res ? res - $vm_res : 0)
        $vm_virt, $vm_res = virt, res

        info = "PID #{$$}, VIRT #{virt} kB, RES #{res} kB, VDIFF #{vdiff} kB, RDIFF #{rdiff} kB" 
        @@vm_diff_logger.info("#{info} | #{request.env['REQUEST_URI']}")

        if Time.now > @@vm_time_ts 
          mins = ((@@vm_time_ts - @@vm_mstart) / 60).to_i
          @@vm_time_ts += @@vm_time_iv
          @@vm_time_logger.info("#{info} | #{mins} minutes, #{@@vm_reqs} requests, #{@@vm_reqs/mins} rpm")        
        end
      end

  end
end
ActionController::Base.send(:include, MemoryLogging)
Jul 08 2007 * 20:28
Dimitrij

As promised, I’ve created a backport of my patch for the v1.0 branch and it seems to work much better (at least for me).

I made a test-script (attached), it works on Linux only and uses the test-environment database. To try it, copy the file to test/other/memory_test.rb and run it with the repository path as parameter. Example:

  ruby test/other/memory_test.rb /var/svn/myrepo

I did the test with the v1.0rc3:

  ...
  [+] Started 'test_02_process_changed_nodes' (VIRT 42080 kB, RES 33564 kB, VDIFF 0 kB, RDIFF 8 kB)
      Processing 10000 nodes
      Status after 2000 nodes (VIRT 275280 kB, RES 246784 kB, VDIFF 233200 kB, RDIFF 213220 kB)
      Status after 4000 nodes (VIRT 490584 kB, RES 437140 kB, VDIFF 215304 kB, RDIFF 190356 kB)
      Status after 6000 nodes (VIRT 724408 kB, RES 555148 kB, VDIFF 233824 kB, RDIFF 118008 kB)
      Status after 8000 nodes (VIRT 928256 kB, RES 611852 kB, VDIFF 203848 kB, RDIFF 56704 kB)
      Status after 10000 nodes (VIRT 1094612 kB, RES 635776 kB, VDIFF 166356 kB, RDIFF 23924 kB)
  [=] Finished after 505s (VIRT 1094612 kB, RES 635780 kB, VDIFF 0 kB, RDIFF 4 kB)
  ...

And this is my result after the patch:

  ...
  [+] Started 'test_02_process_changed_nodes' (VIRT 43264 kB, RES 34744 kB, VDIFF 0 kB, RDIFF 8 kB)
      Processing 10000 nodes
      Status after 2000 nodes (VIRT 68004 kB, RES 59548 kB, VDIFF 24740 kB, RDIFF 24804 kB)
      Status after 4000 nodes (VIRT 91948 kB, RES 81680 kB, VDIFF 23944 kB, RDIFF 22132 kB)
      Status after 6000 nodes (VIRT 135620 kB, RES 121516 kB, VDIFF 43672 kB, RDIFF 39836 kB)
      Status after 8000 nodes (VIRT 144064 kB, RES 129772 kB, VDIFF 8444 kB, RDIFF 8256 kB)
      Status after 10000 nodes (VIRT 154716 kB, RES 140412 kB, VDIFF 10652 kB, RDIFF 10640 kB)
  [=] Finished after 268s (VIRT 154716 kB, RES 140412 kB, VDIFF 0 kB, RDIFF 0 kB)
  ...

Please tell me if it works for you as well as for me.

Dimitrij

PS: I used a well-sized repository (3500 commits, 50MB) of a C application (similar to CallWeaver) for my tests.