Улучшение неэффективного запроса - PullRequest
0 голосов
/ 07 марта 2019

У меня проблема с тайм-аутом моего приложения, потому что запрос занимает слишком много времени. Я использую бесплатный уровень Heroku, поэтому у меня нет точного приоритета в скорости, и поэтому я получаю тайм-ауты, которые я не вижу локально. Я надеялся, что кто-то увидит проблему с моим запросом, которая позволит мне ускорить процесс.

Мой запрос выглядит так:

  def index
    # file = File.read('app\controllers\recipe.csv')
    clear_database
    file = get_file
    recipe_array = file.split("\n")
    dbUser = User.find_by(id: 999999999)
    recipe_array.each do |recipe|
      # I'm saving here becuase I need the ID later
      dbRecipe = dbUser.recipes.create

      recipe = recipe.split(",")
      url_index = recipe.length - 1
      img_url_index = recipe.length - 2
      recipe.each_with_index do |item, index|
        if index == 0
          dbRecipe.name = item.strip.downcase
          dbRecipe.save
        elsif index == url_index
          dbRecipe.url = item
          dbRecipe.save
        elsif index == img_url_index
          dbRecipe.img_url = item
          dbRecipe.save
        elsif index.odd?
          count = item
          food = recipe[index + 1]
          dbIngredient = Ingredient.new
          dbFood = Food.find_by_name(food)
          if dbFood.nil?
            dbFood = Food.new
            dbFood.name = food.strip.downcase
            dbFood.save
          end

          # populate ingredient
          dbIngredient.unit_type = item.split(" ").last
          dbIngredient.quantity = item.split(" ").first
          # I'm saving so much above because I need the id's
          dbIngredient.recipe_id = dbRecipe.id
          dbIngredient.food_id = dbFood.id
          dbIngredient.save
        end
      end
    end
  end

Мои данные состоят из рецептов, которые хранятся в файле CSV. Примерно 300 строк выглядят так:

"Sirloin Steak with Blue Cheese Compound Butter,0.6 oz, Butter,2 count, Garlic Cloves,2 count, Green Onions,12 oz, Fingerling Potatoes,8 oz, Green Beans,12 oz, Sirloin Steaks,1 oz, Blue Cheese,https://homechef.imgix.net/https%3A%2F%2Fasset.homechef.com%2Fuploads%2Fmeal%2Fplated%2F2543%2F2543SirloinSteakwithBlueCheeseCompoundButterReshoot2__1_of_1_-b04048840f58000cef80b38fc3f77856-b04048840f58000cef80b38fc3f77856.jpg?ixlib=rails-1.1.0&w=425&auto=format&s=eeba60ce35bcee4938a11286cbea0203,https://www.homechef.com/meals/sirloin-steak-with-blue-cheese-compound-butter
Teriyaki Ginger-Glazed Salmon,1 Tbsp, Chopped Ginger,2 count, Garlic Cloves,2 count, Green Onions,8 oz, Carrot,2 count, Heads of Baby Bok Choy,1 count, Red Fresno Chile,2 oz, Teriyaki Glaze,12 oz, Salmon Fillets,https://homechef.imgix.net/https%3A%2F%2Fasset.homechef.com%2Fuploads%2Fmeal%2Fplated%2F3429%2F3429TeriyakiGinger-GlazedSalmonReshoot3__1_of_1_-73adcd6ad23cc72b28fdba85387fa18a-73adcd6ad23cc72b28fdba85387fa18a.jpg?ixlib=rails-1.1.0&w=425&auto=format&s=9e6b37380203ec5a58a5ddb906b5ae8b,https://www.homechef.com/meals/teriyaki-ginger-glazed-salmon
Al Pastor Pork Flautas,1 count, Shallot,1 count, Lime,3 oz, Pineapple Chunks,1 oz, Queso Fresco,12 oz, Ground Pork,1 tsp, Chipotle Seasoning,6 count, Small Flour Tortillas,0.5 oz, Baby Arugula,1 oz, Sour Cream,10 oz, Ground Beef,https://homechef.imgix.net/https%3A%2F%2Fasset.homechef.com%2Fuploads%2Fmeal%2Fplated%2F4290%2F4290AlPastorPorkFlautasFinal2__1_of_1_-4e7fe04ac157a463b4d93eb57e9b93f9-4e7fe04ac157a463b4d93eb57e9b93f9.jpg?ixlib=rails-1.1.0&w=425&auto=format&s=de2e2403d7261f2697567faf5f477359,https://www.homechef.com/meals/al-pastor-pork-flautas

Ответы [ 3 ]

2 голосов
/ 07 марта 2019

Вы пытаетесь сделать слишком много в течение одного http запроса / ответа. Для каждой строки в вашем CSV вы делаете sql INSERTdbUser.recipes.create), UPDATEdbRecipe.save), а также SELECTFood.find_by_name(food)).

Даже если вы сделаете несколько оптимизаций, вы уверены, что CSV будет иметь только ~ 300 строк в течение жизни вашего приложения? И даже если ответ «да», в общем случае хорошей практикой является реагирование на действия пользователя как можно быстрее, вместо того, чтобы заставлять их наблюдать, как их браузер ожидает ответа.

Итак, я рекомендую вам пересмотреть свой подход. Если одно действие должно выполнить много команд sql, подумайте о способах выполнения задачи асинхронно. Для этого и были разработаны такие инструменты, как ActiveJob (https://edgeguides.rubyonrails.org/active_job_basics.html) и sidekiq (https://github.com/mperham/sidekiq/)).

Например, разработайте свое приложение таким образом, чтобы пользователь нажимал какую-то кнопку для загрузки CSV и отвечал: «Спасибо за ваше представление, мы работаем над этим!». Пользователь всегда может вернуться и проверить состояние обрабатываемого файла или обновить экран. Или вы можете получить более сложные и автоматизированные проверки состояния с помощью AJAX-опроса или двусторонней связи через веб-сокеты. Railsy способ сделать это будет с ActionCable (https://guides.rubyonrails.org/action_cable_overview.html).

1 голос
/ 07 марта 2019

Вы можете использовать activerecord-import gem - основная идея которого заключается в загрузке множества записей одним запросом.

Вы должны указать имена столбцов в виде массива:

column_names = [:name, :quantity, :unit_type, :food_id]

Далее вы должны загрузить данные из CSV в том же порядке:

row_values = [
  ["Sirloin Steak with Blue Cheese Compound Butter", 1, "Butter", 2],
  ["Something else", 2, "else", 32],
  ...
]

и импорт:

DbIngredient.import(column_names, row_values)
1 голос
/ 07 марта 2019

На самом деле это не запрос, а множество других вещей. Вы анализируете файл и записываете в базу данных на основе содержимого этого файла. Кроме того, если все, что вы делаете, это заполнение БД, то нет никаких причин использовать приложение или браузер вообще. Эту задачу лучше выполнить через грабли или консоль rails. Вы можете избежать тайм-аута базы данных, разбив действия на отдельные части, например

  • загрузить файл в память
  • для каждой строки файла, запись в базу данных и сохранение записи

В любом случае вы должны полностью удалить это заполнение БД из приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...