Серийный анализатор Active Model - увеличение производительности рендера

Я столкнулся с конкретной ситуацией, когда рендеринг json, созданный ActiveModel:: Serializer, необычайно медленный (около 6-8 секунд). Как я могу улучшить скорость этого рендеринга? Вот код.

Модели:

class Comment < ActiveRecord::Base
  has_many :children_comments,
           class_name: 'Comment',
           foreign_key: 'parent_comment_id'

  belongs_to :user
  belongs_to :parent_comment,
             class_name: 'Comment',
             foreign_key: 'parent_comment_id'
end

сериализаторы:

class CommentSerializer < ActiveModel::Serializer
  include ActionView::Helpers::DateHelper

  attributes :id, :message, :created_at_in_words,
             :created_at, :parent_comment_id
  belongs_to :user
  has_many :children_comments

  def created_at_in_words
    time_ago_in_words(object.created_at) + ' ago'
  end

  def children_comments
    object.children_comments.map do |comment|
      CommentSerializer.new(comment).as_json
    end
  end
end

class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :avatar_url

  def avatar_url
    object.avatar.url
  end
end

В моем контроллере у меня есть

  parent_comments = Comment.where(parent_comment_id: nil)

  render status: :ok,
         json: parent_comments,
         each_serializer: CommentSerializer,
         key_transform: :camel_lower

Вот мой частичный вывод журнала, когда я делаю вызов на сервер. Как вы видите, Active Serializer занимает около 20 мс, чтобы сделать каждый запрос.

Started GET "/comments?lesson_id=420" for ::1 at 2016-09-01 11:09:14 -0400
Processing by Api::CommentsController#index as HTML
  Parameters: {"lesson_id"=>"420"}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 102]]
  Lesson Load (0.5ms)  SELECT  "lessons".* FROM "lessons" WHERE "lessons"."id" = $1  ORDER BY position ASC LIMIT 1  [["id", 420]]
  Comment Load (53.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."commentable_id" = $1 AND "comments"."commentable_type" = $2 AND "comments"."parent_comment_id" IS NULL  [["commentable_id", 420], ["commentable_type", "Lesson"]]
[active_model_serializers]   User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (24.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41401]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41402]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (22.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41403]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (21.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41404]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41405]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41406]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41407]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41408]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41409]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41410]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41411]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41412]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41413]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (23.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41414]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41415]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41416]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (23.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41417]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41418]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41419]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41420]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41421]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.9ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41422]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41423]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41424]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41425]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41426]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41427]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41428]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41429]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41430]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.9ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41431]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41432]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41433]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (21.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41434]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41435]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (21.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41436]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41437]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41438]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (22.9ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41439]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41440]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41441]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.9ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41442]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41443]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41444]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41445]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41446]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41447]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41448]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41449]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41450]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41451]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41452]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41453]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41454]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (22.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41455]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (22.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41456]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41457]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41458]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41459]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41460]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41461]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41462]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41463]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41464]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41465]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41466]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41467]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41468]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41469]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41470]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41471]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41472]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41473]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41474]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41475]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41476]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41477]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41478]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41479]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41480]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41534]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41535]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41536]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41537]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41538]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModelSerializers::Adapter::Json (3895.33ms)
Completed 200 OK in 4007ms (Views: 1222.1ms | ActiveRecord: 2743.8ms)

С ответом Михал, вот небольшой пример из журнала.

Started GET "/comments?lesson_id=370" for ::1 at 2016-09-02 17:13:06 -0400
Processing by Api::CommentsController#index as HTML
  Parameters: {"lesson_id"=>"370"}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 102]]
  Lesson Load (0.4ms)  SELECT  "lessons".* FROM "lessons" WHERE "lessons"."id" = $1  ORDER BY position ASC LIMIT 1  [["id", 370]]
  Comment Load (23.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" IS NULL AND "comments"."commentable_type" = 'Lesson' AND "comments"."commentable_id" = 370
  User Load (0.9ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (102)
  Comment Load (25.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" IN (38641, 38687, 38733)
  CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (102)
[active_model_serializers]   Comment Load (20.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 38642]]
[active_model_serializers]   User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 38643]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (30.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 38644]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]

Моя теория такова. Я убежден, что сериализация children_comments вызывает большинство проблем с производительностью. Поскольку я должен вызывать children_comments для каждого комментария, это приводит к каскадному эффекту. Интересно, могу ли я переписать код таким образом, чтобы повысить производительность.

Ответы

Ответ 1

Измените запрос ActiveRecord на этот

parent_comments = Comment.where(parent_comment_id: nil).includes(:user, children_comments: :user)

Он избавится от N + 1 запросов.

Ответ 2

Вы столкнулись с проблемой типа n + 1 запросов. К сожалению, includes на самом деле не помогает вам здесь, потому что он помогает вам только на одном уровне ассоциации - он позволяет избежать отдельных изъятий детей комментария верхнего уровня, но не внуков или правнуков.

Вероятно, вы можете оптимизировать поиск пользователей, сохраняя свой собственный кеш идентификатора пользователя для пользовательских объектов (кеш рельсов кэширует необработанные данные, но будет снова и снова восстанавливать объекты), но чтобы сделать это значительно быстрее, вам нужно измените способ загрузки комментариев.

Если вы используете базу данных, которая ее поддерживает (например, postgresql), тогда рекурсивные запросы являются опцией (см. https://hashrocket.com/blog/posts/recursive-sql-in-activerecord для обработанный пример). Я не знаю этих масштабов, поскольку дерево становится все глубже и глубже.

Если рекурсивные запросы не являются опцией, есть несколько подходов, которые включают изменение того, что вы храните.

Один из них - материализованный путь. Например, скажем, что корневой комментарий имеет id 1, у ребенка есть идентификатор 101, а один из его дочерних элементов имеет id 426. Этот последний путь комментария - 1/101/426. У всех его братьев и сестер есть пути, начинающиеся с 1/101/. Это означает, что вы можете использовать подобные запросы (с подстановочными знаками в конце), чтобы быстро найти поддеревья. ancestry gem реализует это. Если комментарии перемещаются, вам необходимо переписать пути всех комментариев детей (и внуков и т.д.), Но это может не иметь отношения к вашему варианту использования. Думаю, очень глубокие деревья проблематичны.

Другой - это шаблон вложенного набора. Основная идея заключается в том, что родительский node хранит минимальный и максимальный идентификатор всех своих детей и их детей-детей и т.д. Это позволяет получить всех этих детей за один раз. Оборотная сторона заключается в том, что вставки и обновления требуют перезаписи многих этих данных (более того, чем с материализованным путем). Там были различные драгоценные камни, которые реализуют это на протяжении многих лет (текущий кажется awesome_nested_set).

Также стоит проверить, что у вас есть нужные индексы для поддержки ваших запросов - если только не существует довольно много комментариев с данным родителем, 20-30 мс кажется довольно длинным для одного запроса.