はじめに
環境
Rails: 5.2.3
Ruby: 2.6.3
MySQL: 5.6.40
内容
-
users
テーブルのquestion_count
カラムが更新されていないバグがあり、
バッチを作成し、一括で正しい値に更新できるようにしました。 - 既存のメソッドを使用しているため分かりにくい箇所があるかもしれませんがご容赦ください。
user.rb
has_many :questions, dependent: false
has_many :only_questions_visible_to_user, -> { where(question_id: nil).visible_to_user }, class_name: 'Question', dependent: false
scope :visible_to_user, -> { where(is_invalid: false).where(deleted: false) }
def update_question_count!
update!(question_count: count_questions)
end
private
# question_count を更新する時の集計に利用するメソッド
#
# @return [Integer]
def count_questions
only_questions_visible_to_user.size
end
question.rb
belongs_to :user, optional: true
実装したコード
- ログを出力して途中経過、更新に失敗したもの認識しやすいようにしてみました。
- 一度のみの実行で、以降は必要なくなるため、
one_shot
ディレクトリ配下に置いて実行後に削除します。
lib/tasks/one_shot/update_users_question_count.rake
# frozen_string_literal: true
namespace :user do
desc 'Update users_question_count'
task update_question_count: :environment do |_task|
index = 0
updated_question_counts = 0
user_count = User.count
logger = Logger.new($stdout)
logger.info 'Start'
User.find_each do |user|
begin
if user.question_count != user.only_questions_visible_to_user.size
user.update_question_count!
updated_question_counts += 1
end
rescue ActiveRecord::RecordInvalid => e
logger.info "Failed to update user_id: #{user.id}"
logger.info("#{e.class}: #{e.message}")
next
end
index += 1
case index
when 4000, 8000, 12_000, 16_000
logger.info "Progress: #{index}/#{user_count}"
end
end
logger.info "Updated User Count: #{updated_user_counts}"
logger.info 'Finished'
end
end
バッチ実行
bundle exec rake user:update_question_count