2016-08-02 10 views
0

いくつかの行を選択して以前の値に基づいて更新する必要があるテーブルがあるか、存在しない場合は作成し、複数のHTTP要求が行われた場合は安全にする必要があります。一度に同じ行/レコードに関する。Rails SQlite3 ActiveRecord選択してから更新する

トランザクションとActiveRecord.lockを使ってみましたが、複数のリクエストがあるたびに「ActiveRecord :: StatementInvalid(SQLite3 :: BusyException:データベースがロックされています)」という結果になりました。 select文で、アクティブなレコードが(利用可能な場合例えば、行など、ロック)最も効率的な方法でそうするDBで利用可能な機能を使用している。現在のRuby 2.3とRailsの5を使用して

MyModel.transaction 
    record = MyModel.lock.find_by(name: update_name) #name is a unique index in the SQL table 
    if record 
    # The actual manipulation is more complex than something I want to do directly in a SQL statement 
    record.update!(value: record.value + add_value) 
    else 
    record.create(name: update_name, value: add_value) 
    end 
end 

。 1

答えて

0

これは2つのことを行う必要があるデータベースの問題です。

nameをデータベースにユニークに設定してください。新しいレコードを同じnameで保存しようとするとデータベースが失敗するような場合は、それに応じてエラーを処理する必要があります。その名前は既に選択されていて、それ以外のものはありません。作成または使用することができます更新するための

MyModel.find_or_create_by(name: update_name) do |record| 
    record.value += add_value 
    record.save 
end 

しかし、再び、上記のそのメソッドを使用すると、rescue周りのすべてのものをラップし、それに応じてエラーを処理する必要が理由です、アトミックではありません。

+0

私はカラムがDB内の一意のインデックスだと言っていますので、私ができる制約の例外を手動で処理する必要があるのであれば、それを破ることはできません。しかし、セレクトとアップデートの間の競争は効果的に私のデータを破壊するでしょう(最後の更新は勝ちですが、それが保存された値は間違っています)。そしてユニークなインデックス違反はありませんので、レスキューするための例外/警告はありません。 –

関連する問題