3
votes

I have a Rails 5 app with a model called Sensor Registry.

It currently has about 160,000 records, but I am experiencing extremely low loading times when trying to display this data.

The application is running on a dual core Intel(R) Xeon(R) CPU E5-2670 v3 @ 2.30GHz and 2GB of RAM.

The server logs show the following:

Started GET "/sensor_registries" for 187.220.30.180 at 2017-01-10 23:43:41 +0000

Cannot render console from 187.220.30.180! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255

ActiveRecord::SchemaMigration Load (1.2ms) SELECT "schema_migrations".* FROM "schema_migrations"

Processing by SensorRegistriesController#index as HTML Rendering sensor_registries/index.html.erb within layouts/application

SensorRegistry Load (604.0ms) SELECT "sensor_registries".* FROM "sensor_registries"

Sensor Load (0.6ms) SELECT "sensors".* FROM "sensors" WHERE "sensors"."id" IN (49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 65, 61, 63, 64, 62)

Rendered sensor_registries/index.html.erb within layouts/application (54663.9ms) Completed 200 OK in 55468ms (Views: 54827.7ms | ActiveRecord: 611.5ms)

I got rid of the N+1 problem, but I'm wondering if there is something more I can do about the database queries.

Anyway the problem seems to be at the moment of rendering the page, it takes about 54 seconds to process it.

Is there a way to optimize CPU usage?

What would be the best solution for speeding up the process and showing the data to the user fast enough?

3
This is a difficult one to answer without specific knowledge of the application. However, you'll want to profile your action and see where you can improve the slow parts. Tools like NewRelic and RequestProfiler/KCachegrind are useful for this sort of thing.Chris Heald
i was just wondering why there is a need to show 160k records and how user can view all those records on a page with a huge scroll.Milind
Can you post controller and view source code?Zepplock
Usually, this is a code related problem, you should check for n+1 querys being called on view, or repetition of code that could be avoidedErvalhouS

3 Answers

5
votes

This certainly doesn't look like a hardware problem, but a problem with implementation. This is impossible to answer perfectly without knowing more about your data structure and architecture, but a few thoughts that may help you track down the problem:

1) How big is the rendered page? Is it possible that the sheer size of the data is what's causing slow rendering times? If this is the problem and the rendered page is just too big, think about paginating the results.

2) How much memory is the ruby process using at any time? When you say, it "has about 160,000 records", I assume you're talking about the sensor_registries table, and I assume the subsequent sensors query with the sensors.id in (...) bit is constructed with some data from the sensor_registries table. If this entire table is loaded up in memory before any further work is done, is it possible that the ruby process is just out of memory?

3) Also, does the entire table really need to be loaded up all at once? You might want to take a look at http://apidock.com/rails/ActiveRecord/Batches/find_in_batches. That method is great for breaking up work that needs to be done on a large table.

4) Even better–and there's really not a better way to put this–rethink your architecture. Loading up a whole table in memory as part of a synchronous request (even a small one) is almost always a no-no. Can you come up with a sql query to get all your needed records without loading the whole table?

5) If you absolutely need the whole table, what about caching the result after a single load? If you expect the result of a query to be the same across requests with identical parameters, could you construct a cache key out of the params, and use that to store the result? At least that way, all requests after the first one will be fast(er).

3
votes

User following methods:

  • User pagination to display limit number of records on page and load more records on click button.
  • User page caching to put the data into cache and load the heavy page in less time.
  • Load js files after the page load and use compressed js files.
1
votes

Reduce the amount of records you show at the same time using pagination or something (5-10 at most). That way your queries and rendering will be reduces by a lot. I prefer the will_paginate gem for this but there are many more.

In your views, reduce the amount of logic you are using to render each sensor and create separate views for single sensors (show). In the single sensor views you can add more logic.

Use the new Relic gem to monitor your app and see which request take the most amount of time. Maybe you have slow external resources like api calls, etc which you can perform via Ajax and not on the server: https://docs.newrelic.com/docs/agents/ruby-agent/installation-configuration/ruby-agent-installation

Those are the basics, once you are done with that, move on to eager loading and caching.

For eager loading read this: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

For caching read DHH's blog post: https://signalvnoise.com/posts/3113-how-key-based-cache-expiration-works