私は次のコードブロックを持っています:
unless User.exist?(...)
begin
user = User.new(...)
# Set more attributes of user
user.save!
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique => e
# Check if that user was created in the meantime
user = User.exists?(...)
raise e if user.nil?
end
end
その理由は、おそらくご想像のとおり、複数のプロセスがこのメソッドを同時に呼び出してユーザーを作成する可能性があるためです (まだ存在しない場合)。そのため、最初のプロセスがブロックに入り、新しいユーザーの初期化を開始する間、属性を設定して最後に save! を呼び出すと、ユーザーはすでに作成されている可能性があります。その場合、ユーザーが存在するかどうかを再度確認し、まだ存在しない場合にのみ例外を発生させます (= その間、他のプロセスが作成していない場合)。
問題は、定期的に ActiveRecord::RecordInvalid 例外が保存から発生することです! レスキューブロックから救出されません。何か案は?
編集:
わかりました、これは奇妙です。私は何かが欠けているに違いない。Simone のヒントに従って、コードを次のようにリファクタリングしました。
unless User.find_by_email(...).present?
# Here we know the user does not exist yet
user = User.new(...)
# Set more attributes of user
unless user.save
# User could not be saved for some reason, maybe created by another request?
raise StandardError, "Could not create user for order #{self.id}." unless User.exists?(:email => ...)
end
end
今、私は次の例外を受け取りました:
ActiveRecord::RecordNotUnique: Mysql::DupEntry: Duplicate entry 'foo@bar.com' for key 'index_users_on_email': INSERT INTO `users` ...
「unless user.save」と書かれている行にスローされます。それはどうしてですか?Rails は、電子メールが一意であるためユーザーを作成できると考えていますが、Mysql の一意のインデックスが挿入を妨げていますか? それはどのくらいの確率ですか?どうすれば回避できますか?