4

私は次のコードブロックを持っています:

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 の一意のインデックスが挿入を妨げていますか? それはどのくらいの確率ですか?どうすれば回避できますか?

4

2 に答える 2

3

この場合、移行を使用してユーザー テーブル キーに一意のインデックスを作成し、データベースでエラーが発生するようにすることができます。

validates_uniqueness_ofまた、ユーザー モデルに検証を追加することを忘れないでください。

検証によって常に重複データが防止されるわけではありません (2 つの同時リクエストが同じミリ秒で書き込まれる可能性は最小限に抑えられます)。をインデックスと組み合わせて使用​​する場合、validates_uniqueness_ofそのすべてのコードは必要ありません。

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

になる

user = User.new(...)
# Set more attributes of user
if user.save
  # saved
else
  # user.errors will return
  # the list of errors
end
于 2011-01-06T17:03:49.837 に答える
3

Rails の検証では、データベース内の競合状態を検出できません。私たちが使用する解決策は、データベースの制約も追加することです。

これに関するリンクの簡単なページは次のとおりです: Rails ActiveRecord Validations: validates_uniqueness_of races

于 2011-04-28T03:14:26.267 に答える