2

Кто-нибудь захочет дать мне совет о том, как я могу улучшить производительность следующего метода контроллера?Улучшение производительности контроллера Rails, возвращающего сериализованный хеш

def index 
    @contacts = Hash[current_user.company.contacts.map {|contact| [contact.id, ContactSerializer.new(contact).as_json[:contact]] }] 

    respond_to do |format| 
     format.json { render json: { contacts: @contacts } } 
    end 
end 

Это возвращает следующую структуру данных:

{ 
    contacts: { 
     79: { 
      id: 79, 
      first_name: "Foo", 
      last_name: "Bar", 
      email: "[email protected]", 
      engagement: "0%", 
      company_id: 94, 
      created_at: " 9:41AM Jan 30, 2016", 
      updated_at: "10:57AM Feb 23, 2016", 
      published_response_count: 0, 
      groups: { 
       test: true, 
       test23: false, 
       Test222: false, 
       Last: false 
      }, 
      invites: [ 
       { 
        id: 112, 
        email: "[email protected]", 
        status: "Requested", 
        created_at: "Jan 30, 2016, 8:48 PM", 
        date_submitted: null, 
        response: null 
       } 
      ], 
      responses: [ ], 
      promotions: [ 
       { 
        id: 26, 
        company_id: 94, 
        key: "e5cb3bc80b58c29df8a61231d0", 
        updated_at: "Feb 11, 2016, 2:45 PM", 
        read: null, 
        social_media_posts: [ ] 
       } 
      ] 
     }, 
     81: { 
      id: 81, 
      first_name: "Foo2", 
      last_name: "Bar2", 
      email: "[email protected]", 
      engagement: "0%", 
      company_id: 94, 
      created_at: "12:55PM Feb 04, 2016", 
      updated_at: " 4:25PM Feb 19, 2016", 
      published_response_count: 0, 
      groups: { 
       test: true, 
       test23: true, 
       Test222: false, 
       Last: false 
      }, 
      invites: [ 
       { 
        id: 116, 
        email: "[email protected]", 
        status: "Requested", 
        created_at: "Feb 22, 2016, 9:10 PM", 
        date_submitted: null, 
        response: null 
       } 
      ], 
      responses: [ ], 
      promotions: [ 
       { 
        id: 26, 
        company_id: 94, 
        key: "e5cb3bc80b58c29df8a61231d0", 
        updated_at: "Feb 11, 2016, 2:45 PM", 
        read: null, 
        social_media_posts: [ ] 
       } 
      ] 
     } 
    } 
} 

Мне нужен метод index возвращать хэш где ключи идентификаторов контактов, в отличие от массива, который является то, что обычно возвращаться. Кроме того, я передаю каждый контакт через сериализатор, чтобы получить все связанные данные, которые нужны моему клиенту.

Этот метод работает нормально, когда есть только несколько контактов, однако, когда у меня есть 100 или 1000, он действительно замедляется. Я сравнил его с 100 контактами, и для завершения потребовалось 4 секунды, что было ужасно. Мне интересно, как я могу улучшить свой код, чтобы получить точно такой же результат более результативным образом. Ключевым моментом здесь является то, что выход должен оставаться неизменным. Я не заинтересован в изменении кода на стороне клиента (это зависит от этой структуры данных для множества приложений), поэтому все изменения должны происходить на стороне сервера.

Вот мой ContactSerializer для справки:

class ContactSerializer < ActiveModel::Serializer 
    attributes :id, :first_name, :last_name, :email, :engagement, :company_id, :created_at, :updated_at, :published_response_count, :groups 
    has_many :invites 
    has_many :responses 
    has_many :promotions 

    def groups 
     Hash[object.company.groups.map {|group| [group.name, object.groups.include?(group)] }] 
    end 

    def published_response_count 
     object.responses.where(published: true).count 
    end 

    def created_at 
     object.created_at.in_time_zone("Eastern Time (US & Canada)").strftime("%l:%M%p %b %d, %Y") 
    end 

    def updated_at 
     object.updated_at.in_time_zone("Eastern Time (US & Canada)").strftime("%l:%M%p %b %d, %Y") 
    end 

    def engagement 
     object.engagement 
    end 
end 

Для чего это стоит, я полностью осознаю, что возвращает данные в формате JSON, как это с Rails не является большой практикой и с тех пор отошел от него полностью. К сожалению, этот фрагмент кода был написан довольно давно, и я не могу позволить себе время полностью переписать клиентскую сторону, чтобы потреблять стандартный вывод, такой как массив контактов.

+0

«Рефакторинг» меняет структуру кода ради структуры. Улучшение производительности - это не рефакторинг. Я отредактировал соответственно. Что касается производительности, медленность, вероятно, связана с повторяющимися запросами при поиске групп, приглашений и/или рекламных акций и, вероятно, может быть исправлена ​​путем добавления 'includes' в запрос контактов. Посмотрите в log/development.log и посмотрите, какие запросы повторяются и/или замедляются. –

ответ

0

Я начал изучать запросы, которые мы генерируем ActiveRecord.

Через некоторое время я понял, что на запросы приходится всего несколько мс от общего времени обработки. Я начал тестировать код, начиная с базового запроса company.contacts, а затем постепенно добавляя методы, которые у меня были, например map, а затем передавал каждый контакт в сериализатор и, наконец, вызывал as_json[:contact] на объект, возвращенный ContactSerializer.new(contact).

Я обнаружил, что при вызове as_json[:contact] на сериализованном объекте потреблялось в среднем около 30 мс (в среднем более 100 прогонов) на контакт. Причина, по которой я столкнулся, - удалить корневой узел contact из JSON, который был возвращен.

Когда я сравнивал свой первоначальный код с 198 контактами, он занимал в среднем 10400 мс за 10 прогонов. Когда я удалил as_json[:contact] и установил root false на ContactSerializer, как описано в «Abusing ActiveModel::Serializers for HAL», мне удалось сократить время от 10400 до 87 мс, возвращая ту же самую структуру клиенту, что поражает.

Возможно, можно сбрить несколько мс с некоторыми оптимизациями запросов, но что-то еще на этом этапе - это только обледенение на торте.

 Смежные вопросы

  • Нет связанных вопросов^_^