Rails,heroku postgres crane返回空结果集

时间:2013-09-09 09:12:07

标签: ruby-on-rails ruby ruby-on-rails-3 heroku heroku-postgres

我们正在使用heroku作为部署服务。我们的postgres附加计划是具有400 MB内存限制的起重机,我们的数据库中有460MB数据。直到2天前,有时postgres服务器开始响应空结果集。 / p>

这是一个关于这种情况的例子 我们的Entry模型有一个属性,在保存回调之后为条目对象分配条目no,但是这个条目no与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

如您所见,有时postgres服务器返回空结果集。 我们认为如果我们将附加计划升级到800 mb,我们将解决这个问题。 以前是否有人遇到过这个问题,是关于postgres服务器还是其他事情?

我们的系统=> rails 3.0.2,mri ruby​​ 1.9.3

1 个答案:

答案 0 :(得分:1)

Heroku Postgres计划为您提供专用的缓存空间。您可以在Crane计划上超过400mb,但由于数据库必须从磁盘访问数据,因此您的一些查询可能响应较慢。

我的假设是你的assign_entry_no方法没有按照你的想法行事。

我将逐行剖析方法,向您展示一些改进以及您可能遇到的问题。

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

这一行应该按照你的想法行事。但是,您可以通过将条件反转为if self.persisted? && self.project_id.present?来清除它。这会阻止您执行unless,然后撤消unless子句中的!self.project.nil?。此外,使用project_id代替project可以为您节省数据库查询。

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

这是问题所在。首先,您没有对选择进行排序,因此您可能会得到不一致的结果。我建议做Entry.where(project_id: project_id).order(id: :desc).first之类的事情。这样你就可以保证得到最后一个id(不考虑并发问题)。

另一个问题是你正在: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记录。

  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]表格中添加唯一索引,以确保不会出现重复。