0

デプロイ サービスとして heroku を使用しています。postgres アドオン プランは 400 MB のメモリ制限を持つクレーンであり、データベースには 460 MB のデータがあります。2 日前まで、postgres サーバーが空の結果セットを応答し始めることがありました。

この状況の例を次に示します。エントリ モデルの属性があり、エントリ番号をエントリ オブジェクトに割り当てる保存コールバックの後ですが、このエントリ番号は object_id とは異なります。これがコールバック メソッドです。

def assign_entry_no
  return true unless self.new_record? && !self.project.nil?
  last_entry = Entry.where(:project_id => self.project_id).last
  self.entry_no = last_entry.nil? ? 1 : (last_entry.entry_no + 1)
end

これが出力です。

object_id -> id
---------|----
677467 -> 3
677466 -> 2
677465 -> 1 // empty result set
677462 -> 3
677461 -> 2
677460 -> 1 // empty result set
677459 -> 31
677458 -> 30
677457 -> 29

ご覧のとおり、postgre サーバーが空の結果セットを返すことがあります。アドオン プランを 800 mb にアップグレードすれば、問題は解決すると考えました。以前にこの問題に遭遇した人はいますか? それは postgres サーバーに関するものですか?

私たちのシステム => rails 3.0.2、mri ruby​​ 1.9.3

4

1 に答える 1

1

Heroku Postgres プランでは、専用のキャッシュスペースが提供されます。Crane プランでは 400 MB を超えることができますが、データベースがディスクからデータにアクセスする必要があるため、一部のクエリの応答が遅くなる場合があります。

私の仮定は、あなたのassign_entry_no方法はあなたが思っていることをしていないということです。

メソッドを 1 行ずつ分析して、いくつかの改善点と、発生する可能性のある問題を示します。

  return true unless self.new_record? && !self.project.nil?

この行は、あなたが思っていることをするはずです。ただし、条件を のように逆にすることで、少しきれいにすることができますif self.persisted? && self.project_id.present?。これにより、句を実行してから元にunless戻すことができなくなります。さらに、代わりにを使用すると、データベース クエリを節約できます。unless!self.project.nil?project_idproject

  last_entry = Entry.where(:project_id => self.project_id).last

これが問題の行です。まず、select に順序がないため、一貫性のない結果になる可能性があります。のようなことをお勧めしますEntry.where(project_id: project_id).order(id: :desc).first。このようにして、最後のIDを取得することがある程度保証されます(同時実行の問題は別として)。

もう 1 つの問題は、スコープを設定していることです:project_id。新しいプロジェクト (たとえば、) を作成するときid = 44、クエリはSELECT id FROM entries WHERE project_id = 44 ORDER BY id DESC LIMIT 1;. プロジェクト #44 は新しいため、エントリがないため、空のセットが取得されます。これにより、次の行が に割り当てentry_noられ1ます。一意の[project_id, entry_no]セットを取得する可能性がありますが、[entry_no]属性は取得できません。last_entryおそらく、並行環境では、新しいエントリを保存する前に両方が同じレコードを取得する 2 つのエントリが同時に作成される可能性があるためです。

  self.entry_no = last_entry.nil? ? 1 : (last_entry.entry_no + 1)

この行は、意図したとおりであれば十分に問題ないようです。構文を好む人もいますself.entry_no = if last_entry.nil? then 1 else last_entry.entry_no + 1 endが、ロジックは同じです。

結論

entry_noすべてのエントリ レコードに一意の値が本当に必要な場合は、id列を使用してみませんか? 自動インクリメントされ、データベースによって一意であることが保証されます。[project_id, entry_no] ペアの一意性が必要な場合は、通常、コードで既にこれが行われています。SQL クエリSELECT id, project_id, entry_no FROM entries;を実行すると、これが表示されます。これが必要な場合は、entries上のテーブルに一意のインデックスを追加し[:project_id, :entry_no]て、重複しないことが保証されるようにすることをお勧めします。

于 2013-09-10T00:55:01.237 に答える