Jay
Jay

Reputation: 938

I need to render a giant piece of data via JSON with rails. Having performance issues

I'm having performance issues because I am rendering a json object with over 1000 rails objects. This is only happening for a small amount of users that have access to all of this data. > 5%.

I have three types of objects that get nested into the JSON. Regions, Districts, and Schools. A region has_many districts and schools. A district has_many schools.

There are 7 regions, 85 districts, and 3000+ schools.

These all are loaded into select fields and switched depending on what region or district is chosen.

My first solution was to render the regions and districts into json and populate the selects as necessary. Then use Ajax to get the school objects that belong to the chosen district when they were needed. This caused performance issues as some districts have hundreds of schools. And every time a user switched a district or region, it would have to call ajax to get hundreds of schools, which was causing the page to freeze up.

My most recent solution was to just render everything right on page load. This makes the page operate with no lag, but for the > 5% of users it takes around 7 seconds just to query the JSON!

I'm not sure how to speed this process up, this is my current code to handle putting all the data I need into JSON.

To render my JSON, i call:

User.grantable_regions.to_json

user.rb:

def grantable_regions
  if has_role?(:developer, :staff, :state_user)
    Region.all
  else
    # get any regional-level permissions
    regions = []
    self.permissions.region_permissions.can_grant.uniq.each do |perm|
      regions << Region.find_by(id: perm.region_id)
    end
    regions
  end
end

region.rb:

def as_json(opts)
  super(only: [:id, :name], methods: :active_districts)
end

def active_districts
  districts.active
end

district.rb:

def as_json(opts = {})
  super(only: [:id, :name],
    include: {
      schools: { only: [:id, :name] }
    }
  )
end

school.rb

def as_json(options={})
  { :id => id, :name => name }
end

example JSON:

{\"id\":1,\"name\":\"Region 1\",\"active_districts\":[{\"id\":3,\"name\":\"FOO\",\"schools\":[{\"id\":118,\"name\":\"FOO High School\"}]}]

Does anyone have any ideas on how I can speed this up? Again this only happens for > 5% of users.

Upvotes: 0

Views: 157

Answers (1)

Max Williams
Max Williams

Reputation: 32933

As a strategy, i'd recommend not loading all the data up front. Load the top level options, then when the user makes a top-level choice, ajax-load in the data just below that choice, etc etc.

So, they choose a region, then you ajax load districts in that region, then they choose a district, so you ajax load schools in that district.

This way, you split the process up into a series of small queries rather than one huge monolithic one. This will benefit performance in lots of different ways.

Just noticed you tried this already: i think you gave up to easily! If it's freezing trying to load 100 school's data then i don't see how loading all of the schools on your entire system is going to make things better! Instead, look at what was slowing your ajax load of the schools within a district. It might be that you can improve DB performance by adding indexes, or maybe the code you were using to get the data is unecessarily complicated or slow.

Also, with the ajax-loading, you could look at how the user selects a school. Are they looking for a single school? If so, then a keyword search is probably better than showing all the schools in a select: it's easier for the user and it's more efficient.

Upvotes: 1

Related Questions