【问题标题】:Rails, heroku postgres crane returns empty result setRails,heroku postgres 起重机返回空结果集
【发布时间】:2013-09-12 17:58:34
【问题描述】:

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

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

【问题讨论】:

  • 当你说last_entry时,你的意思是获取最新的条目吗?如果是这样,以下行不保证为您提供最新行last_entry = Entry.where(:project_id => self.project_id).last。您必须订购这样的结果last_entry = Entry.where(:project_id => self.project_id).order('created_at desc').last
  • 你看清楚我们的回调代码了吗?如果没有记录,则此代码为条目编号分配 1。
  • 是的,我看到你正在处理没有记录的情况。我的观点是,当有一些记录时,查询不一定返回 LATEST 记录,因为您没有按时间排序。

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


【解决方案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,然后撤消!self.project.nil? 子句中的unless。此外,使用 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;,您应该会看到这一点。如果这是您想要的,我建议您在[:project_id, :entry_no] 上的entries 表中添加一个唯一索引,以保证您不会得到重复。

【讨论】:

  • 您的回答非常好,感谢您的关注。但是,我没有找到导致此问题的确切原因。
  • 运行SELECT project_id, entry_no, COUNT(*) FROM entries GROUP BY project_id, entry_no;会得到什么结果?您应该看到每个 project_id, entry_no 对都是唯一的,但不是每个 entry_no 都是唯一的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-16
  • 2021-09-04
  • 2021-02-20
  • 2017-12-19
  • 1970-01-01
相关资源
最近更新 更多