2013-02-28 7 views
2
1000+

У меня есть EventsController создать действие, которое выглядит следующим образом:ActiveRecord сделки, сохранение записей

class EventsController < ApplicationController 

    def create 
    @event = Event.new(params[:event].slice(*Event.accessible_attributes)) 
    if @event.save 
     DraftBuilder.new(event: @event).build(params[:event][:file].path) 
     redirect_to @event 
    else 
     render :new 
    end 
    end 

end 

Params [: события] [: файл] это файл, который пользователь может отправить через Event # новые действия через file_field_tag.

Метод DraftBuilder # build, между прочим, анализирует данный файл и создает около 1000 записей в базе данных (сохраняет данные в базу данных по нескольким таблицам).

Проблема У меня есть то, что DraftBuilder # build очень медленный. Это медленно, потому что я сохраняю записи в цикле, а Active Record создает новую транзакцию для каждого сохранения.

упрощенный DraftBuilder # сборка может выглядеть следующим образом:

class DraftBuilder 
    def build(file) 
    @data = parse(file) #@data is an array of hashes with 1000+ records 
    @data.each do |pick_arguments| 
     Pick.create(pick_arguments) 
    end 
    end 
end 

Я нашел одно решение этой проблемы. Заверните контроллер создать действие, чтобы ActiveRecord :: Base.transaction:

class EventsController < ApplicationController 
    around_filter :transactions_filter, only: [:create] 

    def transactions_filter 
     ActiveRecord::Base.transaction do 
     yield 
     end 
    end 
end   

Хотя это решение работает, создает только одну транзакцию, и ускоряет весь процесс около 60 раз. Это хороший способ решить эту проблему? Разумеется, транзакции для этого не были разработаны? Каковы другие возможности для создания записей из файлов с более чем тысячей записей?

ответ

2

Лучшим решением для медленных процессов является использование фоновых заданий, таких как delayed_job или resque или sidekiq.

+0

Я думаю, что я видел некоторые Railscasts о delayed_jobs и sidekiq, мне нужно проверить Это. И мне нужно добавить некоторое «уведомление об обработке» в events/show.erb.html, потому что он сразу же запускает ** redirect_to @ event **. Правильно? – Kocur4d

1

У вас есть два пути:

Вместо

@data.each do |pick_arguments| 
    Pick.create(pick_arguments) 
end 

1) Сделки

ActiveRecord::Base.transaction do 
    @data.each do |pick_arguments| 
    Pick.create(pick_arguments) 
    end 
end 

2) Gem ActiveRecord-импорт

data = [] 
@data.each do |pick_arguments| 
    data << Pick.new(pick_arguments) 
end 
Pick.import books 
+0

И, как они сказали, вы можете использовать задачу delayed_job, чтобы сразу ответить пользователю и обработать задачу в фоновом режиме, и даже транзакции не считались способом создания массивных вставок, это общий шаблон в ActiveRecord. – rorra

+0

delayed_job или resque или sidekiq делают то, что мне нужно. Я думаю, что ваше предложение о том, чтобы вывести транзакционный блок из контроллера на объект DraftBuilder, также стоит упомянуть, потому что я не думаю, что любой, кто будет смотреть на контроллер, должен знать, «как кошка обкрашена», так что +1 ... когда плохо быть в состоянии :) – Kocur4d

Смежные вопросы