Read more

How to use Parallel to speed up building the same html partial multiple times (for different data)

Judith Roth
May 05, 2017Software engineer at makandra GmbH

The parallel-gem Show archive.org snapshot is quite easy to use and can speed up rendering time if you want to render the same partial multiple times (e.g. for rendering long lists of things).
If your parallelized code talks to the database, you should ensure not to leak database connections.

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

Consider you want to render a list of groups with their members as json. You can use a partial for the rendering of group members, because they look the same for all groups. And those partials can as well be rendered in parallel.

With parallel the rendering was 2,5 times faster in my case (the actual speedup depends on the hardware that is used to run the code).


Slow, non-parallel example:

class GroupsController < ApplicationController

  def members
    group_ids = params[:group_ids].split(',')

    group_ids_and_html = {}
    group_ids.each do |group_id|
      group = load_group(group_id)
      group_ids_and_html[group_id] = render_group_html(group)
    end

    render json: group_ids_and_html
  end

  private
  
  def load_group(group_id)
    Group.find(group_id)
  end
  
  def render_group_html(user_group)
    render_to_string partial: 'groups/members', locals: { group: group }
  end

end

Faster example with Parallel:

class GroupsController < ApplicationController

  def members
    group_ids = params[:group_ids].split(',')

    group_ids_and_html = Parallel.map(group_ids) do |group_id|   # <- Here is the difference
      group = load_group(group_id)
      [group_id, render_group_html(group)]
    ensure
      ActiveRecord::Base.clear_active_connections   # <- close additional database connections
    end

    render json: Hash[*group_ids_and_html.flatten(1)]
  end

  private
  
  def load_group(group_id)
    Group.find(group_id)
  end
  
  def render_group_html(user_group)
    render_to_string partial: 'groups/members', locals: { group: group }
  end

end

Note

If you are using database-cleaner with DatabaseCleaner.strategy = :transaction, this could lead to problems in tests. You should use DatabaseCleaner.strategy = :truncation for the tests that touch your parallelized code.

Judith Roth
May 05, 2017Software engineer at makandra GmbH
Posted by Judith Roth to makandra dev (2017-05-05 14:34)