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 in rails
Batch processing in rails
https://github.com/rubyist/aasm
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


                          Состояния конечного автомата
Структура модельного слоя
Почему не очередь

• Так казалось проще.
• Для каждого задания можно одназначно
  сказать его статус.
• Единый каталог со всем готовым на
  текущий момент контентом.
Гетерогенность




•   return unless RUBY_PLATFORM =~ /mswin/

•   Dir[File.join(work_catalog,"page*.pdf")]
Аудит
   https://gist.github.com/949049#file_book.rb
                                                             https://github.com/collectiveidea/acts_as_audited
acts_as_audited :except => [:name, :file_name]

   https://gist.github.com/949049#file_production.rake

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_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
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
http://www.nagios.org/
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


                                 https://gist.github.com/949049#file_nagios.rake

More Related Content

PDF
Batch processing on RoR
PDF
2015-12-05 Вадим Литвинов - Нагрузочное тестирование с MZBench
PDF
Erlang tasty & useful stuff
PDF
"Пиринговый веб на JavaScript"
PDF
Psgi app
PDF
Превышаем скоростные лимиты с Angular 2
KEY
Chef коротко об инфраструктуре
PDF
«Как перестать отлаживать асинхронные вызовы и начать жить»​
Batch processing on RoR
2015-12-05 Вадим Литвинов - Нагрузочное тестирование с MZBench
Erlang tasty & useful stuff
"Пиринговый веб на JavaScript"
Psgi app
Превышаем скоростные лимиты с Angular 2
Chef коротко об инфраструктуре
«Как перестать отлаживать асинхронные вызовы и начать жить»​

What's hot (20)

PPTX
Highload осень 2012 лекция 10
PDF
CSSO — сжимаем CSS
PDF
Почему Mojolicious?
KEY
Sequel — механизм доступа к БД, написанный на Ruby
PDF
#2 "Распространённые ошибки в JavaScript" Денис Речкунов
PPTX
Особенности совместной работы Ruby и Oracle
PPTX
Cache GitHub Continous Integration
PDF
Wordpress Cron
PPTX
JavaScript: прошлое, настоящее и будущее.
PPT
Загрузка, обработка, хранение и отдача статики
PDF
Building better APIs on rails
PDF
Streaming replication in practice
PDF
Собираем по винтикам 2ГИС Web API — инструкция в картинках
PDF
PDF
Drupal code sprint для новичков
PDF
Mysql replication
PDF
«Изоморфные js приложения с использованием catberry.js», Денис Речкунов
PDF
2014.10.15 Мурат Кабилов, Avito.ru #PostgreSQLRussia
PPTX
Roman Gorel: Building better APIs on Rails.
Highload осень 2012 лекция 10
CSSO — сжимаем CSS
Почему Mojolicious?
Sequel — механизм доступа к БД, написанный на Ruby
#2 "Распространённые ошибки в JavaScript" Денис Речкунов
Особенности совместной работы Ruby и Oracle
Cache GitHub Continous Integration
Wordpress Cron
JavaScript: прошлое, настоящее и будущее.
Загрузка, обработка, хранение и отдача статики
Building better APIs on rails
Streaming replication in practice
Собираем по винтикам 2ГИС Web API — инструкция в картинках
Drupal code sprint для новичков
Mysql replication
«Изоморфные js приложения с использованием catberry.js», Денис Речкунов
2014.10.15 Мурат Кабилов, Avito.ru #PostgreSQLRussia
Roman Gorel: Building better APIs on Rails.
Ad

Viewers also liked (14)

PPT
A HUGE Community Health Game
KEY
Worried code
PDF
PHOTOBOOK BODA CLAROS BERNAL MUESTRA
PPTX
Aztec 2010 images short version
PPTX
Aztec 2010 images short version
PDF
Proof mi álbum
PPTX
Negative contribution of automatic postural control
PDF
Documentation de Doctrine ORM
PPTX
VASA CONCEPT - To Expand The Boundaries of Centre of Mass [COM]
PDF
Fire your front end
PPT
WIU Math Conference
PPTX
How to restore balance following stroke?
PPTX
правовая основа и организация цессии
A HUGE Community Health Game
Worried code
PHOTOBOOK BODA CLAROS BERNAL MUESTRA
Aztec 2010 images short version
Aztec 2010 images short version
Proof mi álbum
Negative contribution of automatic postural control
Documentation de Doctrine ORM
VASA CONCEPT - To Expand The Boundaries of Centre of Mass [COM]
Fire your front end
WIU Math Conference
How to restore balance following stroke?
правовая основа и организация цессии
Ad

Similar to Batch processing in rails (20)

PDF
Истинный DevOps. Секрет 42.
PPTX
CodeFest 2012. Родионов А. — Тестирование Ruby (on Rails) приложений: стек, п...
PPT
Движение по хрупкому дну / Сергей Караткевич (servers.ru)
PPTX
PowerShell
PPT
Easy authcache 2 кеширование для pro родионов игорь
PPT
Easy authcache 2 кэширование для pro. Родионов Игорь
PDF
Behat в PHP с использованием Behat и Mink
PPTX
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
PDF
От Make к Ansible
PPTX
Антон Довгоброд: Highload и очереди задач на примере PHP + Gearman + Yii2
PPTX
Node.js введение в технологию, КПИ #ITmeetingKPI
PDF
Why Ruby?
PDF
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
PDF
Romanova techforum bash
PDF
GitLab, Prometheus и Grafana с Kubernetes
PPT
бегун
PDF
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
PDF
Not the Rails Way
PDF
Суперсилы Chrome developer tools
ODP
Adymo Barcamp Presentation Faster Higher Sql
Истинный DevOps. Секрет 42.
CodeFest 2012. Родионов А. — Тестирование Ruby (on Rails) приложений: стек, п...
Движение по хрупкому дну / Сергей Караткевич (servers.ru)
PowerShell
Easy authcache 2 кеширование для pro родионов игорь
Easy authcache 2 кэширование для pro. Родионов Игорь
Behat в PHP с использованием Behat и Mink
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
От Make к Ansible
Антон Довгоброд: Highload и очереди задач на примере PHP + Gearman + Yii2
Node.js введение в технологию, КПИ #ITmeetingKPI
Why Ruby?
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
Romanova techforum bash
GitLab, Prometheus и Grafana с Kubernetes
бегун
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
Not the Rails Way
Суперсилы Chrome developer tools
Adymo Barcamp Presentation Faster Higher Sql

Batch processing in rails

  • 1. Batch processing on RoR http://slidesha.re/k6zrSL @SergeyMoiseev moiseev.sergey@gmail.com
  • 3. Задача • Генерация контента (книг) для сайта. • Входной формат: PDF. • Выходной формат: SWF. • Генератор: print2flash. • Основной приоритет: скорость обработки (количество публикуемых книг в месяц).
  • 4. Нюансы • Книги поступают разного качества, часть из них требует многократной переработки. • Print2flash может печатать не более одного задания за раз в пределах машины. • Требует windows окружения.
  • 5. Архитектура • Множественные инсталляции Rails приложения. • Общая база. • Конечный автомат для контроля состояния задания. • Отдельные операции - rake таски запускаемые по крону или планировщику задач.
  • 9. https://github.com/rubyist/aasm 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 Состояния конечного автомата
  • 11. Почему не очередь • Так казалось проще. • Для каждого задания можно одназначно сказать его статус. • Единый каталог со всем готовым на текущий момент контентом.
  • 12. Гетерогенность • return unless RUBY_PLATFORM =~ /mswin/ • Dir[File.join(work_catalog,"page*.pdf")]
  • 13. Аудит https://gist.github.com/949049#file_book.rb https://github.com/collectiveidea/acts_as_audited acts_as_audited :except => [:name, :file_name] https://gist.github.com/949049#file_production.rake 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_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
  • 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. http://www.nagios.org/ 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 https://gist.github.com/949049#file_nagios.rake

Editor's Notes