SlideShare a Scribd company logo
Batch processing on
RoR
http://slidesha.re/k6zrSL
@SergeyMoiseev
moiseev.sergey@gmail.com
О себе
• http://moiseev--sergey.moikrug.ru/
• пишу на Ruby с 2007 года.
Задача
• Генерация контента (книг) для сайта.
• Входной формат: PDF.
• Выходной формат: SWF.
• Генератор: print2flash.
• Основной приоритет: скорость
обработки (количество публикуемых
книг в месяц).
Нюансы
• Книги поступают разного качества,
часть из них требует многократной
переработки.
• Print2flash может печатать не более
одного задания за раз в пределах
машины.
• Требует windows окружения.
Архитектура
• Множественные инсталляции Rails
приложения.
• Общая база.
• Конечный автомат для контроля
состояния задания.
• Отдельные операции - rake таски
запускаемые по крону или
планировщику задач.
Конфигурация
Batch processing on RoR
Batch processing on RoR
include AASM
aasm_column :state
aasm_initial_state :loaded
aasm_state :loaded, :enter => :reset_retry_count, :exit=>:apply_total_pages
aasm_state :in_abbyy, :enter => :copy_to_abbyy
aasm_state :p2f_queue, :enter=>:apply_pages_count
aasm_state :in_p2fline, :enter => :copy_to_p2fline
aasm_state :p2f_printing, :enter => :p2f_print
aasm_state :print_failed, :enter => :free_p2fline
aasm_state :wait_for_confirm, :enter => :free_p2fline
aasm_state :confirmed
aasm_state :confirmed_non_full
aasm_state :on_server
aasm_state :on_server_non_full
aasm_state :published
aasm_state :published_non_full
aasm_state :hold
aasm_state :mycop
aasm_state :restarting, :after_enter => :check_avail_pdf
aasm_state :restarting_full, :exit=>:restart_full
Состояния конечного автомата
https://github.com/rubyist/aasm
Структура модельного слоя
Почему не очередь
• Так казалось проще.
• Для каждого задания можно
одназначно сказать его статус.
• Единый каталог со всем готовым на
текущий момент контентом.
Гетерогенность
• return unless RUBY_PLATFORM =~ /mswin/
• Dir[File.join(work_catalog,"page*.pdf")]
Аудит
acts_as_audited :except => [:name, :file_name]
https://gist.github.com/949049#file_book.rb
desc "Loads files into Production line"
task :process_batch => :environment do
Audit.as_user("rake:process_batch") do
Book.getFromBatch unless running?("process_batch")
end
end
https://gist.github.com/949049#file_production.rake
https://gist.github.com/949049#file_books_controller.rb
def restart
if @book.restartable?
Audit.as_user current_user do
@book.restart_production!
end
flash[:notice] = 'Обработка перезапущена.'
else
flash[:error] = 'Перезапуск невозможен.'
end
redirect_to request.referer
end
https://github.com/collectiveidea/acts_as_audited
Мониторинг
http://munin-monitoring.org/
namespace :munin do
task :config do
puts <<-CONFIG
graph_title Produced pages in last 5 minutes
graph_args -l 0
graph_vlabel pages amount
graph_category App
graph_info This graph shows amount of pages produced
texts.label Text files
docs.label Doc files
views.label View files
quotes.label Quote files
CONFIG
exit 0
end
task :run => :environment do
res = ActiveRecord::Base.connection.execute("select count(nullif(text_ready_time>(now()-'5 minutes'::interval),false)) as
c0,count(nullif(doc_ready_time>(now()-'5 minutes'::interval),false)) as c1,count(nullif(view_ready_time>(now()-'5 minutes'::interval),false)) as
c2,count(nullif(quote_ready_time>(now()-'5 minutes'::interval),false)) as c3 from pages where updated_at > (now()-'5 minutes'::interval);")
if res
puts "texts.value #{res[0]['c0']}"
puts "docs.value #{res[0]['c1']}"
puts "views.value #{res[0]['c2']}"
puts "quotes.value #{res[0]['c3']}"
end
exit 0
end
end
https://gist.github.com/949049#file_munin.rake
https://gist.github.com/949049#file_nagios.rake
namespace :nagios do
task :abbyy_activity, [:warn, :crit] => :environment do |t, args|
if args.warn and args.crit
states_list = ["loaded", "in_abbyy", "p2f_queue"]
warn_level = args.warn.to_i
crit_level = args.crit.to_i
warn_events = Audit.find(:all, :conditions=>["auditable_type = 'Book' and (username is null or username ilike 'rake:%') and action = 'update' and
created_at > ?", warn_level.minutes.ago])
crit_events = Audit.find(:all, :conditions=>["auditable_type = 'Book' and (username is null or username ilike 'rake:%') and action = 'update' and
created_at > ?", crit_level.minutes.ago])
if (crit_count = crit_events.select{|a| a['changes']['state'] and (a['changes']['state'] & states_list).size > 1 }).empty?
puts "CRITICAL No state changes at abbyy for #{crit_level} minutes"
exit 2
end
if (warn_count = warn_events.select{|a| a['changes']['state'] and (a['changes']['state'] & states_list).size > 1 }).empty?
puts "WARNING No state changes at abbyy for #{warn_level} minutes"
exit 1
end
puts "OK Abbyy: state changes #{warn_count.size} for #{warn_level} minutes; state changes #{crit_count.size} for #{crit_level} minutes."
exit 0
else
exit 3
end
end
http://www.nagios.org/

More Related Content

What's hot (20)

PDF
Hacking PostgreSQL. Обзор исходного кода
Anastasia Lubennikova
 
PPTX
JavaScript: прошлое, настоящее и будущее.
FDConf
 
PDF
Wordpress Cron
versusbassz
 
PDF
Андрей Светлов-«Делаем своё решение для оптимальной загрузки кластера»
Tanya Denisyuk
 
PDF
Семь тысяч Rps, один go
Badoo Development
 
PDF
ECP и API управления процессами. InterSystems Meetup Sankt-Peterburg 2014
InterSystems
 
PDF
Михаил Трошев "AJAX в поиске по вебу"
Yandex
 
PPTX
Быстрое развёртывание шаблонов и статики в Mail.ru, Николай Кондратов
Fuenteovejuna
 
PDF
Практика применения Pinba в Badoo / Денис Карасик (Badoo)
Ontico
 
PDF
"Пиринговый веб на JavaScript"
FDConf
 
PDF
Reform: путь к лучшему ORM
Badoo Development
 
PDF
Расширения для PostgreSQL
Anastasia Lubennikova
 
PDF
Hacking PostgreSQL. Разделяемая память и блокировки.
Anastasia Lubennikova
 
PDF
Практический опыт создания уменьшенной копии боевой инфраструктуры сайта для ...
SQALab
 
ODP
Hacking PostgreSQL. Физическое представление данных
Anastasia Lubennikova
 
PDF
Модули на C для Ruby
racoons
 
PDF
Превышаем скоростные лимиты с Angular 2
Oleksii Okhrymenko
 
PDF
Современная операционная система: что надо знать разработчику / Александр Кри...
Ontico
 
PDF
Компиляция скриптов PHP. Алексей Романенко
Fuenteovejuna
 
KEY
Chef коротко об инфраструктуре
Andrey Subbota
 
Hacking PostgreSQL. Обзор исходного кода
Anastasia Lubennikova
 
JavaScript: прошлое, настоящее и будущее.
FDConf
 
Wordpress Cron
versusbassz
 
Андрей Светлов-«Делаем своё решение для оптимальной загрузки кластера»
Tanya Denisyuk
 
Семь тысяч Rps, один go
Badoo Development
 
ECP и API управления процессами. InterSystems Meetup Sankt-Peterburg 2014
InterSystems
 
Михаил Трошев "AJAX в поиске по вебу"
Yandex
 
Быстрое развёртывание шаблонов и статики в Mail.ru, Николай Кондратов
Fuenteovejuna
 
Практика применения Pinba в Badoo / Денис Карасик (Badoo)
Ontico
 
"Пиринговый веб на JavaScript"
FDConf
 
Reform: путь к лучшему ORM
Badoo Development
 
Расширения для PostgreSQL
Anastasia Lubennikova
 
Hacking PostgreSQL. Разделяемая память и блокировки.
Anastasia Lubennikova
 
Практический опыт создания уменьшенной копии боевой инфраструктуры сайта для ...
SQALab
 
Hacking PostgreSQL. Физическое представление данных
Anastasia Lubennikova
 
Модули на C для Ruby
racoons
 
Превышаем скоростные лимиты с Angular 2
Oleksii Okhrymenko
 
Современная операционная система: что надо знать разработчику / Александр Кри...
Ontico
 
Компиляция скриптов PHP. Алексей Романенко
Fuenteovejuna
 
Chef коротко об инфраструктуре
Andrey Subbota
 

More from Александр Ежов (20)

PDF
Кеширование данных в БД
Александр Ежов
 
PDF
Борьба с багами: RailsClub на DevConf 2015
Александр Ежов
 
PDF
Rails 5: awesome features and breaking changes
Александр Ежов
 
PDF
Big Data и Ruby
Александр Ежов
 
PDF
Abstract machines for great good
Александр Ежов
 
PPT
Rubt on Rails: 1000 запросов в секунду
Александр Ежов
 
PPT
Liquor, Liquid и другие безопасные языки разметки в RoR
Александр Ежов
 
PDF
Capistrano Rails
Александр Ежов
 
PDF
Умное кэширование в Rails
Александр Ежов
 
PPT
Sphinx как база данных
Александр Ежов
 
ODP
Emacs и Rails
Александр Ежов
 
PPT
Антикризисная презентация
Александр Ежов
 
PPT
Eventmachine: структура evented-приложений
Александр Ежов
 
PPT
Дэдлоки
Александр Ежов
 
PDF
Обзорное сравнение серверов приложений для Rails
Александр Ежов
 
PDF
Приложения в плагинах
Александр Ежов
 
PDF
Использования PgQ и Londste в rails-приложении
Александр Ежов
 
PDF
Масштабирование в Rails
Александр Ежов
 
PDF
Интернационализация и локализация Ruby on Rails. gem russian
Александр Ежов
 
Кеширование данных в БД
Александр Ежов
 
Борьба с багами: RailsClub на DevConf 2015
Александр Ежов
 
Rails 5: awesome features and breaking changes
Александр Ежов
 
Big Data и Ruby
Александр Ежов
 
Abstract machines for great good
Александр Ежов
 
Rubt on Rails: 1000 запросов в секунду
Александр Ежов
 
Liquor, Liquid и другие безопасные языки разметки в RoR
Александр Ежов
 
Capistrano Rails
Александр Ежов
 
Умное кэширование в Rails
Александр Ежов
 
Sphinx как база данных
Александр Ежов
 
Антикризисная презентация
Александр Ежов
 
Eventmachine: структура evented-приложений
Александр Ежов
 
Обзорное сравнение серверов приложений для Rails
Александр Ежов
 
Приложения в плагинах
Александр Ежов
 
Использования PgQ и Londste в rails-приложении
Александр Ежов
 
Масштабирование в Rails
Александр Ежов
 
Интернационализация и локализация Ruby on Rails. gem russian
Александр Ежов
 
Ad

Batch processing on RoR

  • 3. Задача • Генерация контента (книг) для сайта. • Входной формат: PDF. • Выходной формат: SWF. • Генератор: print2flash. • Основной приоритет: скорость обработки (количество публикуемых книг в месяц).
  • 4. Нюансы • Книги поступают разного качества, часть из них требует многократной переработки. • Print2flash может печатать не более одного задания за раз в пределах машины. • Требует windows окружения.
  • 5. Архитектура • Множественные инсталляции Rails приложения. • Общая база. • Конечный автомат для контроля состояния задания. • Отдельные операции - rake таски запускаемые по крону или планировщику задач.
  • 9. include AASM aasm_column :state aasm_initial_state :loaded aasm_state :loaded, :enter => :reset_retry_count, :exit=>:apply_total_pages aasm_state :in_abbyy, :enter => :copy_to_abbyy aasm_state :p2f_queue, :enter=>:apply_pages_count aasm_state :in_p2fline, :enter => :copy_to_p2fline aasm_state :p2f_printing, :enter => :p2f_print aasm_state :print_failed, :enter => :free_p2fline aasm_state :wait_for_confirm, :enter => :free_p2fline aasm_state :confirmed aasm_state :confirmed_non_full aasm_state :on_server aasm_state :on_server_non_full aasm_state :published aasm_state :published_non_full aasm_state :hold aasm_state :mycop aasm_state :restarting, :after_enter => :check_avail_pdf aasm_state :restarting_full, :exit=>:restart_full Состояния конечного автомата https://github.com/rubyist/aasm
  • 11. Почему не очередь • Так казалось проще. • Для каждого задания можно одназначно сказать его статус. • Единый каталог со всем готовым на текущий момент контентом.
  • 12. Гетерогенность • return unless RUBY_PLATFORM =~ /mswin/ • Dir[File.join(work_catalog,"page*.pdf")]
  • 13. Аудит acts_as_audited :except => [:name, :file_name] https://gist.github.com/949049#file_book.rb desc "Loads files into Production line" task :process_batch => :environment do Audit.as_user("rake:process_batch") do Book.getFromBatch unless running?("process_batch") end end https://gist.github.com/949049#file_production.rake https://gist.github.com/949049#file_books_controller.rb def restart if @book.restartable? Audit.as_user current_user do @book.restart_production! end flash[:notice] = 'Обработка перезапущена.' else flash[:error] = 'Перезапуск невозможен.' end redirect_to request.referer end https://github.com/collectiveidea/acts_as_audited
  • 15. namespace :munin do task :config do puts <<-CONFIG graph_title Produced pages in last 5 minutes graph_args -l 0 graph_vlabel pages amount graph_category App graph_info This graph shows amount of pages produced texts.label Text files docs.label Doc files views.label View files quotes.label Quote files CONFIG exit 0 end task :run => :environment do res = ActiveRecord::Base.connection.execute("select count(nullif(text_ready_time>(now()-'5 minutes'::interval),false)) as c0,count(nullif(doc_ready_time>(now()-'5 minutes'::interval),false)) as c1,count(nullif(view_ready_time>(now()-'5 minutes'::interval),false)) as c2,count(nullif(quote_ready_time>(now()-'5 minutes'::interval),false)) as c3 from pages where updated_at > (now()-'5 minutes'::interval);") if res puts "texts.value #{res[0]['c0']}" puts "docs.value #{res[0]['c1']}" puts "views.value #{res[0]['c2']}" puts "quotes.value #{res[0]['c3']}" end exit 0 end end https://gist.github.com/949049#file_munin.rake
  • 16. https://gist.github.com/949049#file_nagios.rake namespace :nagios do task :abbyy_activity, [:warn, :crit] => :environment do |t, args| if args.warn and args.crit states_list = ["loaded", "in_abbyy", "p2f_queue"] warn_level = args.warn.to_i crit_level = args.crit.to_i warn_events = Audit.find(:all, :conditions=>["auditable_type = 'Book' and (username is null or username ilike 'rake:%') and action = 'update' and created_at > ?", warn_level.minutes.ago]) crit_events = Audit.find(:all, :conditions=>["auditable_type = 'Book' and (username is null or username ilike 'rake:%') and action = 'update' and created_at > ?", crit_level.minutes.ago]) if (crit_count = crit_events.select{|a| a['changes']['state'] and (a['changes']['state'] & states_list).size > 1 }).empty? puts "CRITICAL No state changes at abbyy for #{crit_level} minutes" exit 2 end if (warn_count = warn_events.select{|a| a['changes']['state'] and (a['changes']['state'] & states_list).size > 1 }).empty? puts "WARNING No state changes at abbyy for #{warn_level} minutes" exit 1 end puts "OK Abbyy: state changes #{warn_count.size} for #{warn_level} minutes; state changes #{crit_count.size} for #{crit_level} minutes." exit 0 else exit 3 end end http://www.nagios.org/