11

I recently launched a new Ruby on Rails application that worked well in development mode. After the launch I have been experiencing the memory being used is constantly increasing:

Screen dump from New Relic, Dyno memory usage

UPDATED: When this screen dump from New Relic was taken. I have scheduled a web dyno restart every hour (one out of two web dynos). Thus, it does not reach the 500Mb-crash level and it actually gets a bit of a sig saw pattern. The problem is not at all resolved by this though, only some of the symptoms. As you can see the morning is not so busy but the afternoon is more busy.

UPDATED: When this screen dump (the one below) from New Relic was taken. I have scheduled a web dyno restart every hour (one out of two web dynos). Thus, it does not reach the 500Mb-crash level and it actually gets a bit of a sig saw pattern. The problem is not at all resolved by this though, only some of the symptoms. As you can see the morning is not so busy but the afternoon is more busy. I made an upload at 11.30 for a small detail, it could not have affected the problem even though it appears as such in the stats.

It could be noted as well that it is the MIN memory that keeps on increasing even though the graph shows AVG memory. Even when the graph seems to go down temporarily in the graph, the min memory stays the same or increases. The MIN memory never decreases!

The app would (without dyno restarts) increase in memory until it reached the maximum level at Heroku and the app crashes with execution expired-types of errors.

I am not a great programmer but I have made a few apps before without having this type of problem.

Troubleshooting performed

A. I thought the problem would lie within the before_filter in the application_controller (Will variables in application controller cause a memory leak in Rails?) but that wasn't the problem.

B. I installed oink but it does not give any results (at all). It creates an oink.log but does not give any results when I run "heroku run oink -m log/oink.log", no matter what threshold.

C. I tried bleak_house but it was deprecated and could not be installed

D. I have googled and read most articles in the topic but I am none the wiser.

E. I would love to test memprof but I can't install it (I have Ruby 1.9x and don't really know how to downgrade it to 1.8x)

My questions:

Q1. What I really would love to know is the name(s) of the variable(s) that are increasing for each request, or at least which controller is using the most memory.

Q2. Will a controller as the below code increase in memory?

related_feed_categories = []
@gift.tags.each do |tag|
  tag.category_connections.each do |cc|
    related_feed_categories << cc.category_from_feed
  end
end

(sorry, SO won't re-format the code to be easily readable for some reason).

Do I need to "kill off" related_feed_categories with "related_feed_categories = nil" afterwards or does the Garbage Collector handle that?

Q3. What would be my major things to look for? Right now I can't narrow it down AT ALL. I don't know which part of the code to look deeper into, and I don't really know what to look for.

Q4. In case I really cannot solve the problem. Are there any online consulting service where I can send my code and get them to find the problem?

Thanks!

UPDATED. After receiving comments it could have to do with sessions. This is a part of the code that I guess could be bad:

# Create sessions for last generation
friend_data_arr = [@generator.age, @generator.price_low, @generator.price_high]
friend_positive_tags_arr = []
friend_negative_tags_arr = []
friend_positive_tags_arr << @positive_tags
friend_negative_tags_arr << @negative_tags    
session["last_generator"] = [friend_data_arr, friend_positive_tags_arr, friend_negative_tags_arr]

# Clean variables
friend_data_arr = nil
friend_positive_tags_arr = nil
friend_negative_tags_arr = nil

it is used in the generator#show controller. When some gifts have been generated through my gift-generating-engine I save the input in a session (in case they want to use that info in a later stage). I never kill or expire these sessions so in case this could cause memory increase.

Updated again: I removed this piece of code but the memory still increases, so I guess this part is not it but similar code might causing the error?

Community
  • 1
  • 1
Christoffer
  • 2,271
  • 3
  • 26
  • 57
  • Have you tried new relic? They are a heroku add-on and their basic version is free. I think their tools would help debug this issue or at least narrow it down. – John Sep 25 '12 at 08:17
  • Yeah, the graph above is from New Relic. It has helped me understand the app is crashing due to memory problems but I haven't been able to narrow it down any further. – Christoffer Sep 25 '12 at 08:36
  • Assuming `category_from_feed` returns a string (or a symbol perhaps), a magnitude of 300MB increase is quite unlikely. You can roughly arrive at this by profiling this: `4_000_000.times {related_feed_categories << "Loooooooooooooong string" }` This snippet would shoot the memory usage up by about 250MB. I'd look at DB connections or methods that read a file and then don't close it. – Kashyap Sep 27 '12 at 13:39

2 Answers2

5

That's unlikely our related_feed_categories provoke this.

Are you using a lot of files ?

How long do you keep sessions datas ? Looks like you have an e-commerce site, are you keeping objects in sessions ?

Basically, i think it is files, or session, or an increase in temporary datas flushed when the server crash(memcache ?).

In the middle of the night, i guess that ou have fewer customer. Can you post the same memory chart, in peak hours ?

It may be related to this problem : Memory grows indefinitely in an empty Rails app

UPDATE :

Rails don't store all the datas on client side. I don't remember the default store, bu unless you choose the cookie::store, rails send only datas like session_id.

They are few guidelines about sessions, the ActiveRecord::SessionStore seem to be the best choice for performance purpose. And you shouldn't keep large objects nor secrets datas in sessions. More on session here : http://guides.rubyonrails.org/security.html#what-are-sessions

In the 2.9 part, you have an explanation to destroy sessions, unused for a certain time.

Instead of storing objects in sessions, i suggest you store the url giving the search results. You may even store it in database, offering the possibility to save few research to your customer, and/or by default load the last used.

But at these stage we are still, not totally sure that sessions are the culprits. In order to be sure, you may try on a test server, to stress test your application, with expiring sessions. So basically, you create a large number of sessions, and maybe 20 min later rails has to suppress them. If you find any difference in memory consumption, it will narrow things.

First case : memory drop significantly when sessions expires, you know that's is session related.

Second case : The memory increase at a faster rate, but don't drop when sessions expires, you know that it is user related, but not session related.

Third case : nothing change(memory increase at usual), so you know it do not depend on the number of user. But i don't know what could cause this.

When i said stress tests, i mean a significant number of sessions, not really a stress test. The number of sessions you need, depends on your average numbers of users. If you had 50 users, before your app crashed, 20 -30 sessions may be sginificant. So if you had them by hand, configure a higher expire time limit. We are just looking for differences in memory comsuption.

Update 2 :

So this is most likely a memory leak. So use object space, it has a count_objects method, which will display all the objets currently used. It should narrow things. Use it when memory have already increased a lot.

Otherwise, you have bleak_house, a gem able to find memory leaks, still ruby tools for memory leaks are not as efficient as java ones, but it's worth a try.

Github : https://github.com/evan/bleak_house

Update 3 :

This may be an explanation, this is not really memory leak, but it grows memory : http://www.tricksonrails.com/2010/06/avoid-memory-leaks-in-ruby-rails-code-and-protect-against-denial-of-service/

In short, symbols are keep in memory until your restart ruby. So if symbols are created with random name, memory will grow, until your app crash. This don't happen with Strings, the are GCed.

Bit old, but valid for ruby 1.9.x Try this : Symbol.all_symbols.size

Update 4:

So, your symbols are probably the memory leak. Now we still have to find where it occurs. Use Symbol.all_symbols. It gives you the list. I guess you may store this somewhere, and make a diff with the new array, in order to see what was added.

It may be i18n, or it may be something else generating in an implicit way like i18n. But anyway, this is probably generating symbols with random data in the name. And then these symbols are never used again.

Community
  • 1
  • 1
Perello
  • 633
  • 1
  • 7
  • 22
  • The website is a "gift idea"-website. I have about 1.000 gifts and each have a product image stored on the server. I also have additional 24.000 products, fetched from feeds, where I fetch the images externally (www.otherdomain.com/images/productfromfeed.jpg). Outside of images, cached files and the actual code, I do not have any other files on the server. – Christoffer Sep 27 '12 at 15:32
  • I am very unused to sessions so it is quite likely I am not handling this correctly. What should I be looking for here? Are not session data stored locally on each users computer (like cookies)? – Christoffer Sep 27 '12 at 15:33
  • Regarding your link. I had looked into that question as well and it could be related I guess but I don't think so. The reason for this is that 1. I have not gotten this problem with my other apps 2. I don't know much about memory optimization so it is quite likely I have done something wrong. – Christoffer Sep 27 '12 at 15:49
  • Thanks for the update, I did not know how to try the stress test but I removed all sessions from the app (they were just extra features) and the memory is still increasing so I assume sessions was not the case either! – Christoffer Sep 28 '12 at 11:40
  • Did it increase at te same rate ? Or was it different ? Are you using something else implying memory ? You may have memory leaks, it happens even if the ruby'gc is pretty smart. – Perello Sep 28 '12 at 13:07
  • It is very difficult to say since the memory increases all the time. I have done numerous things now and found some probable causes for leaks making the increase in memory significant less than before. I am pretty sure I do have a major memory leak, that is what the whole topic is about. The major problem I have is that I still have no clue how to narrow it down outside of going through every bit of code that might be the cause of it (also doing that without really knowing all factors causing memory leaks). I have no working memory profiling tool either. I am greatly thankful to your help! – Christoffer Sep 28 '12 at 13:16
  • Regarding your update about bleak_house. Bleak house is deprecated (and I have not been able to install it). It refers to Kiji (which I can't understand why since it seems to be something quite different). I have also tried memoryprof and ruby-prof but both required updates to my ruby which I am not sure how to do (and don't want to ruin the installation I have now). – Christoffer Sep 28 '12 at 13:24
  • Regarding my Q4-question: Is there any trusted service online where I can upload my code and get it investigated for this error? Obviously for a cost. – Christoffer Sep 28 '12 at 13:25
  • Object_space looks interesting. I will look into it! – Christoffer Sep 28 '12 at 13:27
  • You have given me a lot of tools now for me to look into it. Finally I have something to work with, thanks! Won't be able to work with this over the weekend but I will get back here with the results on Monday. THANKS! – Christoffer Sep 28 '12 at 14:15
  • Btw, if I use s = Symbol.all_symbols.size between Page A & B. And s=100 when I load A, s=100 if I reload A, s=110 when I load B, s=110 when I reload B and s=110 when I go back to A...there is something wrong, right? I.e. s has increased with 10 while going to B and back. – Christoffer Sep 28 '12 at 14:39
  • I put the Symbol.all_symbols.size and can confirm that it increases in size with pretty much each page I load. I don't really use symbols though! How can I see which actual symbols that keeps getting added? – Christoffer Sep 30 '12 at 19:50
  • Have you some i18N ? You can see all your symbols with Symbol.all_symbols The .size has been applyied upon an array. – Perello Oct 02 '12 at 11:05
  • I have no i18N...at least none that I have created knowingly. I do use <%=h yield(:meta_title) %> in my layout/application.html and <% content_for :meta_title, @page.meta_title %> etc in my pages. Could that be something? I have some 20.000 symbols so Symbol.all_symbols.each doesn't really help me much, or can they be grouped and numbered easily? – Christoffer Oct 02 '12 at 14:18
  • Hmmmm. Once you load our symbols, store them in session. And at the next page load. Do new_symbols_array - session_symbols_array. Then do your .each. http://stackoverflow.com/questions/80091/diff-a-ruby-string-or-array For the metas, it may, but i can't tell fo sure. Try the array diff. It will show you only the knew symbols. So you can grasp where and when they were created. – Perello Oct 03 '12 at 10:12
  • Thanks, but I am not quite sure what you mean. The last couple of days I have done hundreds of small fixes and the problem still persist but is nowhere near as bad as before. I will have to leave it for now, I have already spent over 4-5 working days on this bug! Thanks so much all of you who tried to help me!! – Christoffer Oct 03 '12 at 13:48
1

Assuming category_from_feed returns a string (or a symbol perhaps), a magnitude of 300MB increase is quite unlikely. You can roughly arrive at this by profiling this:

4_000_000.times {related_feed_categories << "Loooooooooooooong string" }

This snippet would shoot the memory usage up by about 110MB.

I'd look at DB connections or methods that read a file and then don't close it. I can see that it's related to feeds which probably means you might be using XML. That can be a starting point too.


Posting this as answer because this looks bad in comments :/

Kashyap
  • 4,696
  • 24
  • 26
  • Thanks. I am reading feeds but only once a week and the memory is increasing every minute. If I have a controller with the code given above (the one with related_feed_categories). Do I need to "close it" with related_feed_categories = nil ? Or is that handled automatically once the code has been read? I mean, is that something I need to look into? – Christoffer Sep 27 '12 at 15:29
  • Btw, how can you tell this snippet uses 110 Mb. What tool do you use for it? – Christoffer Sep 27 '12 at 15:40
  • Well, that's just an approximation. "Looong string".bytesize gives you 13 ~ 1 byte per character (depends on encoding) which means that the string in the answer which is 28 bytes is added to the array which means the array size is roughly 28*4million ~ 106MB. – Kashyap Sep 27 '12 at 18:02
  • You don't need to set related_feed_categories to nil, controllers are destroyed at the end of each request. – Perello Sep 28 '12 at 08:55
  • Perello, thanks! That was very good to hear. At least I can rule that out now. – Christoffer Sep 28 '12 at 09:44
  • Kashyap. Ruby-prof seemed promising but in order to test memory I needed to patch install ruby and some other file that was 4 years old. I don't really know how to do this and and I am pretty sure I will just move my trouble shooting into figuring out how to install that patch... Any good guides or other tools? Memory counting seems to demand a lot of patching and extra installations :( – Christoffer Sep 28 '12 at 09:46
  • @Christoffer This might be helpful: http://www.shopify.com/technology/4321572-most-memory-leaks-are-good and this: http://www.engineyard.com/blog/2009/thats-not-a-memory-leak-its-bloat/ As a rule or thumb, if you do not know the source of the problem for bloat, best way to go about it start re-thinking about every aspect. These guides have the process neatly explained. – Kashyap Sep 28 '12 at 14:38
  • Thanks, I've read both of those but can't say it helped me much in my particular problem. What exactly do you mean with re-thinking every aspect? – Christoffer Sep 30 '12 at 08:59