модуль ActiveRecord::Batches
Константы
- ORDER_OR_LIMIT_IGNORED_MESSAGE
Открытые методы экземпляров
# File activerecord/lib/active_record/relation/batches.rb, line 56
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
if block_given?
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
records.each { |record| yield record }
end
else
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
relation = self
apply_limits(relation, start, finish).size
end
end
end Перебор коллекции записей из базы данных (например, используя метод ActiveRecord::Scoping::Named::ClassMethods#all) является очень неэффективным, так как он пытается создать все объекты сразу.
В этом случае методы пакетной обработки позволяют работать с записями по частям, тем самым значительно сокращая потребление памяти.
Метод find_each использует find_in_batches с размером пакета 1000 (или как указано в параметре :batch_size).
Person.find_each do |person|
person.do_awesome_stuff
end
Person.where("age > 21").find_each do |person|
person.party_all_night!
end
Если вы не предоставите блок методу find_each, он вернёт перечислитель для цепочки с другими методами:
Person.find_each.with_index do |person, index| person.award_trophy(index + 1) end
Параметры
-
:batch_size— Устанавливает размер пакета. По умолчанию 1000. -
:start— Устанавливает значение первичного ключа, начиная с которого, включая это значение. -
:finish— Устанавливает значение первичного ключа, по которому завершить обработку, включая это значение. -
:error_on_ignore— Переопределяет конфигурацию приложения, чтобы указать, должно ли генерироваться исключение, когда порядок и ограничение должны быть проигнорированы из-за пакетной обработки.
Это особенно полезно, если вы хотите, чтобы несколько рабочих процессов обрабатывали одну и ту же очередь задач. Вы можете поручить рабочему процессу 1 обработку всех записей между id 0 и 10 000, а рабочему процессу 2 — от 10 000 и далее (установив параметры :start и :finish для каждого рабочего процесса).
# Let's process for a batch of 2000 records, skipping the first 2000 rows Person.find_each(start: 2000, batch_size: 2000) do |person| person.party_all_night! end
ПРИМЕЧАНИЕ: Установить порядок невозможно. Он автоматически устанавливается по возрастанию первичного ключа («id ASC»), чтобы пакетная сортировка работала. Это также означает, что этот метод работает только тогда, когда первичный ключ упорядочивается (например, целое число или строка).
ПРИМЕЧАНИЕ: Также нельзя задать предел, он используется для управления размерами пакетов.
# File activerecord/lib/active_record/relation/batches.rb, line 111
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
relation = self
unless block_given?
return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
total = apply_limits(relation, start, finish).size
(total - 1).div(batch_size) + 1
end
end
in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore) do |batch|
yield batch.to_a
end
end Возвращает каждый пакет записей, найденных в соответствии с параметрами поиска, как массив.
Person.where("age > 21").find_in_batches do |group|
sleep(50) # Make sure it doesn't get too crowded in there!
group.each { |person| person.party_all_night! }
end
Если вы не предоставите блок методу find_in_batches, он вернёт перечислитель для цепочки с другими методами:
Person.find_in_batches.with_index do |group, batch|
puts "Processing group ##{batch}"
group.each(&:recover_from_last_night!)
end
Чтобы получать каждую запись по отдельности, используйте метод find_each.
Параметры
-
:batch_size— Устанавливает размер пакета. По умолчанию 1000. -
:start— Устанавливает значение первичного ключа, начиная с которого, включая это значение. -
:finish— Устанавливает значение первичного ключа, по которому завершить обработку, включая это значение. -
:error_on_ignore— Переопределяет конфигурацию приложения, чтобы указать, должно ли генерироваться исключение, когда порядок и ограничение должны быть проигнорированы из-за пакетной обработки.
Это особенно полезно, если вы хотите, чтобы несколько рабочих процессов обрабатывали одну и ту же очередь задач. Вы можете поручить рабочему процессу 1 обработку всех записей между id 0 и 10 000, а рабочему процессу 2 — от 10 000 и далее (установив параметры :start и :finish для каждого рабочего процесса).
# Let's process the next 2000 records
Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
group.each { |person| person.party_all_night! }
end
ПРИМЕЧАНИЕ: Установить порядок невозможно. Он автоматически устанавливается по возрастанию первичного ключа («id ASC»), чтобы пакетная сортировка работала. Это также означает, что этот метод работает только тогда, когда первичный ключ упорядочивается (например, целое число или строка).
ПРИМЕЧАНИЕ: Также нельзя задать предел, он используется для управления размерами пакетов.
# File activerecord/lib/active_record/relation/batches.rb, line 184
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
relation = self
unless block_given?
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
end
if arel.orders.present? || arel.taken.present?
act_on_order_or_limit_ignored(error_on_ignore)
end
relation = relation.reorder(batch_order).limit(of)
relation = apply_limits(relation, start, finish)
batch_relation = relation
loop do
if load
records = batch_relation.records
ids = records.map(&:id)
yielded_relation = self.where(primary_key => ids)
yielded_relation.load_records(records)
else
ids = batch_relation.pluck(primary_key)
yielded_relation = self.where(primary_key => ids)
end
break if ids.empty?
primary_key_offset = ids.last
raise ArgumentError.new("Primary key not included in the custom select clause") unless primary_key_offset
yield yielded_relation
break if ids.length < of
batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
end
end Возвращает объекты ActiveRecord::Relation для работы с пакетом записей.
Person.where("age > 21").in_batches do |relation|
relation.delete_all
sleep(10) # Throttle the delete queries
end
Если вы не предоставите блок методу in_batches, он вернёт BatchEnumerator, который является перечислимым.
Person.in_batches.each_with_index do |relation, batch_index|
puts "Processing relation ##{batch_index}"
relation.delete_all
end
Примеры вызова методов на возвращённом объекте BatchEnumerator:
Person.in_batches.delete_all Person.in_batches.update_all(awesome: true) Person.in_batches.each_record(&:party_all_night!)
Параметры
-
:of— Устанавливает размер пакета. По умолчанию 1000. -
:load— Указывает, загружать ли отношение. По умолчанию false. -
:start— Устанавливает значение первичного ключа, начиная с которого, включая это значение. -
:finish— Устанавливает значение первичного ключа, по которому завершить обработку, включая это значение. -
:error_on_ignore— Переопределяет конфигурацию приложения, чтобы указать, должно ли генерироваться исключение, когда порядок и ограничение должны быть проигнорированы из-за пакетной обработки.
Это особенно полезно, если вы хотите работать с объектом ActiveRecord::Relation вместо массива записей или если вы хотите, чтобы несколько рабочих процессов обрабатывали одну и ту же очередь задач. Вы можете поручить рабочему процессу 1 обработку всех записей между id 0 и 10 000, а рабочему процессу 2 — от 10 000 и далее (установив параметры :start и :finish для каждого рабочего процесса).
# Let's process the next 2000 records Person.in_batches(of: 2000, start: 2000).update_all(awesome: true)
Пример вызова метода `query` для отношения:
Person.in_batches.each do |relation|
relation.update_all('age = age + 1')
relation.where('age > 21').update_all(should_party: true)
relation.where('age <= 21').delete_all
end
ПРИМЕЧАНИЕ: Если вы собираетесь пройтись по каждой записи, вы должны вызвать `each_record` на возвращённом BatchEnumerator:
Person.in_batches.each_record(&:party_all_night!)
ПРИМЕЧАНИЕ: Установить порядок невозможно. Он автоматически устанавливается по возрастанию первичного ключа («id ASC»), чтобы пакетная сортировка была согласованной. Таким образом, первичный ключ должен быть упорядочиваемым, например, целым числом или строкой.
ПРИМЕЧАНИЕ: Также нельзя задать предел, он используется для управления размерами пакетов.
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.