Я унаследовал приложение rails, которое развернуто с помощью Heroku (я думаю). Я редактирую его в AWS Cloud9 IDE и пока просто делаю все в режиме разработки. Цель приложения - обработать большие объемы данных опроса и выложить их в отчет в формате PDF. Это работает для небольших отчетов с примерно 10 строками данных, но когда я загружаю отчет, который запрашивает загрузку данных более 5000 строк, чтобы создать HTML-страницу, которая преобразуется в PDF, это занимает около 105 секунд, что намного дольше, чем у Heroku. 30 секунд отведено на HTTP-запросы.
Heroku говорит об этом на своем сайте, что дало мне надежду:
"Heroku поддерживает функции HTTP 1.1, такие как длинные опросы и потоковые ответы. Приложение имеет начальное 30-секундное окно для ответа одним байтом обратно клиенту. Однако каждый байт передается после этого (либо получаемый от клиента, либо отправлено вашим приложением) сбрасывает 55-секундное окно. Если данные не отправляются в течение 55-секундного окна, соединение будет разорвано. " (Источник: https://devcenter.heroku.com/articles/request-timeout#long-polling-and-streaming-responses)
Это звучит отлично для меня - я могу отправлять запрос клиенту каждую секунду или около того в цикле, пока мы не закончим создание большого отчета в формате PDF. Однако я не знаю, как отправить или получить байт или около того, чтобы «сбросить скользящее 55-секундное окно», о котором они говорят.
Вот часть моего контроллера, которая отправляет запрос.
return render pdf: pdf_name + " " + pdf_year.to_s,
disposition: 'attachment',
page_height: 1300,
encoding: 'utf8',
page_size: 'A4',
footer: {html: {template: 'recent_grad/footer.html.erb'}, spacing: 0 },
margin: { top: 10, # default 10 (mm)
bottom: 20,
left: 10,
right: 10 },
template: "recent_grad/report.html.erb",
locals: {start: @start, survey: @survey, years: @years, college: @college, department: @department, program: @program, emphasis: @emphasis, questions: @questions}
Я делаю другие запросы, чтобы добраться до этой точки, но я считаю, что та часть, которая вызывает проблему, находится здесь, где визуализируется шаблон. Мой шаблон запрашивает базу данных в конечном цикле, который останавливается, когда у него заканчиваются вопросы опроса для запроса.
У меня такой вопрос: как я могу «отправить или получить байт клиенту», чтобы сказать Heroku «Я все еще пытаюсь создать этот массивный PDF, поэтому, пожалуйста, сбросьте таймер и дайте мне мои 55 секунд!» Это в форме запроса? Потому что, если это так, я снова и снова запрашиваю базу данных MySql в моем файле report.html.erb.
Кроме того, раньше он работал без проблем и работал с небольшими отчетами, но теперь я получаю сообщение об ошибке «504 Gateway Timeout» до того, как запрос будет завершен на текущей странице, но моя консоль puma продолжает запрашивать базу данных, как сумасшедший Я предполагаю, что это проблема Heroku, потому что ошибка 504 возникает ровно каждые 35 секунд (5 секунд для обработки других частей и 30 секунд для завершения цикла в шаблоне, чтобы он мог правильно отображаться).
Если вам нужна дополнительная информация или код, пожалуйста, спрашивайте! Заранее спасибо
EDIT:
Оба комментария ниже предлагают возможные дубликаты, но ни у одного из них нет реального ответа с реальным кодом, они просто ссылаются на документы, которые я цитирую здесь. Я ищу пример кода (или, по крайней мере, способ получить мою ногу в дверь), а не просто ссылку на документы. Спасибо!
РЕДАКТИРОВАТЬ 2:
Я попробовал то, что сказал @Sergio, и установил SideKiq. Я думаю, что я действительно близко, но все еще есть некоторые проблемы с рабочим. Рабочий не имеет доступа к ActionView :: Base, который требуется для метода рендеринга в rails, поэтому он не работает. Я могу получить доступ к рабочему методу, который означает, что мои серверы sidekiq и redis работают правильно, но он попадает в строку ActionView с этой ошибкой:
WARN: NameError: неинициализированная константа HardWorker :: ActionView
Вот рабочий код:
require 'sidekiq'
Sidekiq.configure_client do |config|
# config.redis = { db: 1 }
config.redis = { url: 'redis://172.31.6.51:6379/0' }
end
Sidekiq.configure_server do |config|
# config.redis = { db: 1 }
config.redis = { url: 'redis://172.31.6.51:6379/0' }
end
class HardWorker
include Sidekiq::Worker
def perform(pdf_name, pdf_year)
av = ActionView::Base.new()
av.view_paths = ActionController::Base.view_paths
av.class_eval do
include Rails.application.routes.url_helpers
include ApplicationHelper
end
puts "inside hardworker"
puts pdf_name, pdf_year
av.render pdf: pdf_name + " " + pdf_year.to_s,
disposition: 'attachment',
page_height: 1300,
encoding: 'utf8',
page_size: 'A4',
footer: {html: {template: 'recent_grad/footer.html.erb'}, spacing: 0 },
margin: { top: 10, # default 10 (mm)
bottom: 20,
left: 10,
right: 10 },
template: "recent_grad/report.html.erb",
locals: {start: @start, survey: @survey, years: @years, college: @college, department: @department, program: @program, emphasis: @emphasis, questions: @questions}
end
end
Есть предложения?
РЕДАКТИРОВАТЬ 3:
Я сделал то, что сказал @Sergio, и попытался сделать PDF из файла html.erb напрямую и сохранить его в файл. Вот мой код:
# /app/controllers/recentgrad_controller.rb
pdf = WickedPdf.new.pdf_from_html_file('home/ec2-user/environment/gradSurvey/gradSurvey/app/views/recent_grad/report.html.erb')
save_path = Rails.root.join('pdfs', pdf_name + pdf_year.to_s + '.pdf')
File.open(save_path, 'wb') do |file|
file << pdf
end
И вывод ошибки:
RuntimeError (Failed to execute:
["/usr/local/rvm/gems/ruby-2.4.1@gradSurvey/bin/wkhtmltopdf", "file:///home/ec2-user/environment/gradSurvey/gradSurvey/app/views/recent_grad/report.html.erb", "/tmp/wicked_pdf_generated_file20190523-15416-hvb3zg.pdf"]
Error: PDF could not be generated!
Command Error: Loading pages (1/6)
Error: Failed loading page file:///home/ec2-user/environment/gradSurvey/gradSurvey/app/views/recent_grad/report.html.erb (sometimes it will work just to ignore this error with --load-error-handling ignore)
Exit with code 1 due to network error: ContentNotFoundError
):
Я понятия не имею, что это значит, когда он говорит "иногда это будет работать просто, чтобы игнорировать эту ошибку с помощью --load-error-processing ignore". Файл определенно существует, и я пробовал 5 вариантов пути к файлу.