Как добавить стиль скрепки к изображениям размером 13 КБ (на AWS S3) без нехватки памяти?

Я добавил два стиля (smallcard и mediumcard) к своей модели прикрепления скрепки Скриншот:

class Screenshot < ActiveRecord::Base
    has_attached_file :image,
    :styles => { :tiny => "x75", :small => "x245", :medium => "x480", :large => "1280x900>",
                 :smallcard => "280x245#", :mediumcard => "570x480#" },
    :storage => :s3,
    :s3_credentials => "#{Rails.root}/config/amazon_s3.yml",
    :path => "/screenshots/:id_partition/:style/:filename"
end

Я вручную создал файл public/system/paperclip_attachments.yml, чтобы уменьшить обработку уже существующих стилей:

---
:Screenshot:
  :image:
    - :tiny
    - :small
    - :medium
    - :large

Но все же, когда я запускаю rake paperclip:refresh:missing_styles CLASS=Screenshot, я получаю следующее:

Regenerating Screenshot -> image -> [:mediumcard, :smallcard]
rake aborted!
Cannot allocate memory - identify -format %wx%h '/tmp/79a229e96ab52dfa760132958da47bf320120806-31260-1eleoww[0]'
Tasks: TOP => paperclip:refresh:missing_styles
[clip]

Когда я веду журналы, обработка доходит до 500 (идентификаторы).

По общему признанию, сервер представляет собой Linode 512 под управлением Ubuntu, и он годами отлично обслуживает страницы для 3 приложений Rails и 1 приложения PHP. У меня никогда раньше не заканчивалась память.

Отслеживая процесс задачи rake, он постепенно увеличивается с каждым обрабатываемым изображением, пока не съест всю доступную оперативную память.

Может, моему Линоде пора подрасти... но сначала я надеюсь на какие-то другие варианты.

Как я могу обойти эту проблему с памятью и добавить эти два стиля к уже существующим 13-килобайтным изображениям?

Спасибо за вашу помощь!


person Peter Giacomo Lombardo    schedule 06.08.2012    source источник
comment
Поможет ли вам взломать github.com/thoughtbot/paperclip/ blob/master/lib/paperclip/ и изменить размер пакета при вызове find_each на что-то маленькое (например, 50)?   -  person Frederick Cheung    schedule 07.08.2012
comment
Я тоже об этом думал, но, как предлагает Крис ниже, похоже, что процесс, вызывающий reprocess! должен полностью завершиться, чтобы память кучи была освобождена обратно в ОС. Я нашел подсказки, что это связано с утечкой памяти ImageMagick... :(   -  person Peter Giacomo Lombardo    schedule 07.08.2012
comment
Как бы то ни было, скрепка перебирает ваши объекты по 1000 за раз — пока не завершится пакет, эти 1000 объектов (и изображения, которые они загрузили) не могут быть собраны мусором, потому что они все еще находятся в этом массиве, который повторяется. - вам нужно уменьшить набор вещей, которые он должен хранить в памяти, и итерация в меньших кусках будет одним шагом.   -  person Frederick Cheung    schedule 07.08.2012


Ответы (2)


Вы должны дать вашей системе возможность правильно освободить память. Смелый трюк, который мы использовали, когда столкнулись с похожей проблемой, используя ORM для пакетной задачи PHP, заключается в следующем: оберните вашу задачу в другую задачу, которая вызывает первую задачу только для одного элемента за раз. В общем, вы должны реорганизовать первую задачу, чтобы взять аргумент для базового образа. Вторая задача должна собрать все изображения (удобным для памяти способом, например, идентификаторы объектов или что-то в этом роде), а затем пройтись по ним и вызвать первую задачу с каждым из них в качестве аргумента. Когда первая задача будет завершена, она вернет память ОС, которая затем сможет освободить память. С другой стороны, второй задаче или задаче-оболочке никогда не требуется сразу столько памяти. Таким образом, максимальное использование памяти должно быть максимальным для обработки одного изображения, а не всех изображений.

person Chris    schedule 06.08.2012

Надеюсь, это может помочь кому-то еще с такой же проблемой.

Как предложил Крис, я обернул одну задачу rake внутри другой, которая вызывается с помощью %x(). Каждая итерация полностью освобождает память от предыдущего вызова.

namespace :screenshots do
  desc "Incrementally rebuild thumbnails. START=0 & BATCH_SIZE=10 & VERBOSE=false"
  task :reprocess_stepper => :environment do
    batch_size = (ENV['BATCH_SIZE'] || ENV['batch_size'] || 10)
    verbose    = (ENV['VERBOSE'] || ENV['verbose'] || nil)

    total = Screenshot.count
    start = 0

    while start < total
      puts "Spawning: bundle exec rake screenshots:reprocess_some START=#{start} BATCH_SIZE=#{batch_size} VERBOSE=#{verbose} RAILS_ENV=#{Rails.env}"
      puts %x{bundle exec rake screenshots:reprocess_some START=#{start} BATCH_SIZE=#{batch_size} VERBOSE=#{verbose} RAILS_ENV=#{Rails.env} }
      start = start + batch_size.to_i
    end
  end

  desc "Reprocess a batch of screenshots. START=0 & BATCH_SIZE=10 & VERBOSE=false"
  task :reprocess_some => :environment do
    start      = (ENV['START'] || ENV['start'] || 0)
    batch_size = (ENV['BATCH_SIZE'] || ENV['batch_size'] || 10)
    verbose    = (ENV['VERBOSE'] || ENV['verbose'] || nil)

    puts "start = #{start} & batch_size = #{batch_size}" if verbose
    puts "RAILS_ENV=#{Rails.env}" if verbose

    screenshots = Screenshot.order("id ASC").offset(start).limit(batch_size).all
    screenshots.each do |ss|
      puts "Re-processing paperclip image on screenshot ID: #{ss.id}" if verbose
      STDOUT.flush
      ss.image.reprocess!
    end
  end
end

Затем вы можете вызвать эту задачу следующим образом:

RAILS_ENV=production bundle exec rake screenshots:reprocess_stepper VERBOSE=true BATCH_SIZE=50
person Peter Giacomo Lombardo    schedule 02.09.2012