lib/lockbox/migrator.rb in lockbox-0.3.1 vs lib/lockbox/migrator.rb in lockbox-0.3.2
- old
+ new
@@ -1,58 +1,126 @@
module Lockbox
class Migrator
- def initialize(model)
- @model = model
+ def initialize(relation, batch_size:)
+ @relation = relation
+ @transaction = @relation.respond_to?(:transaction)
+ @batch_size = batch_size
end
- def migrate(restart:)
- model = @model
+ def model
+ @model ||= @relation
+ end
- # get fields
+ def rotate(attributes:)
+ fields = {}
+ attributes.each do |a|
+ # use key instad of v[:attribute] to make it more intuitive when migrating: true
+ field = model.lockbox_attributes[a]
+ raise ArgumentError, "Bad attribute: #{a}" unless field
+ fields[a] = field
+ end
+
+ perform(fields: fields)
+ end
+
+ # TODO add attributes option
+ def migrate(restart:)
fields = model.lockbox_attributes.select { |k, v| v[:migrating] }
- # get blind indexes
blind_indexes = model.respond_to?(:blind_indexes) ? model.blind_indexes.select { |k, v| v[:migrating] } : {}
- # build relation
- relation = model.unscoped
+ perform(fields: fields, blind_indexes: blind_indexes, restart: restart)
+ end
+ private
+
+ def perform(fields:, blind_indexes: [], restart: true)
+ relation = @relation
+
+ # remove true condition in 0.4.0
+ if true || (defined?(ActiveRecord::Base) && base_relation.is_a?(ActiveRecord::Base))
+ relation = relation.unscoped
+ end
+
+ # convert from possible class to ActiveRecord::Relation or Mongoid::Criteria
+ relation = relation.all
+
unless restart
attributes = fields.map { |_, v| v[:encrypted_attribute] }
attributes += blind_indexes.map { |_, v| v[:bidx_attribute] }
- if defined?(ActiveRecord::Base) && model.is_a?(ActiveRecord::Base)
+ if defined?(ActiveRecord::Relation) && relation.is_a?(ActiveRecord::Relation)
+ base_relation = relation.unscoped
+ or_relation = relation.unscoped
+
attributes.each_with_index do |attribute, i|
- relation =
+ or_relation =
if i == 0
- relation.where(attribute => nil)
+ base_relation.where(attribute => nil)
else
- relation.or(model.unscoped.where(attribute => nil))
+ or_relation.or(base_relation.where(attribute => nil))
end
end
+
+ relation = relation.merge(or_relation)
+ else
+ relation = relation.or(attributes.map { |a| {a => nil} })
end
end
- if relation.respond_to?(:find_each)
- relation.find_each do |record|
- migrate_record(record, fields: fields, blind_indexes: blind_indexes, restart: restart)
+ each_batch(relation) do |records|
+ migrate_records(records, fields: fields, blind_indexes: blind_indexes, restart: restart)
+ end
+ end
+
+ def each_batch(relation)
+ if relation.respond_to?(:find_in_batches)
+ relation.find_in_batches(batch_size: @batch_size) do |records|
+ yield records
end
else
+ # https://github.com/karmi/tire/blob/master/lib/tire/model/import.rb
+ # use cursor for Mongoid
+ records = []
relation.all.each do |record|
- migrate_record(record, fields: fields, blind_indexes: blind_indexes, restart: restart)
+ records << record
+ if records.length == @batch_size
+ yield records
+ records = []
+ end
end
+ yield records if records.any?
end
end
- private
+ def migrate_records(records, fields:, blind_indexes:, restart:)
+ # do computation outside of transaction
+ # especially expensive blind index computation
+ records.each do |record|
+ fields.each do |k, v|
+ record.send("#{v[:attribute]}=", record.send(k)) if restart || !record.send(v[:encrypted_attribute])
+ end
+ blind_indexes.each do |k, v|
+ record.send("compute_#{k}_bidx") if restart || !record.send(v[:bidx_attribute])
+ end
+ end
- def migrate_record(record, fields:, blind_indexes:, restart:)
- fields.each do |k, v|
- record.send("#{v[:attribute]}=", record.send(k)) if restart || !record.send(v[:encrypted_attribute])
+ records.select! { |r| r.changed? }
+
+ with_transaction do
+ records.each do |record|
+ record.save!(validate: false)
+ end
end
- blind_indexes.each do |k, v|
- record.send("compute_#{k}_bidx") if restart || !record.send(v[:bidx_attribute])
+ end
+
+ def with_transaction
+ if @transaction
+ @relation.transaction do
+ yield
+ end
+ else
+ yield
end
- record.save(validate: false) if record.changed?
end
end
end