【问题标题】:Rails: repeated ActiveRecord::RecordNotUnique when creating objects with Postgres?Rails:使用 Postgres 创建对象时重复 ActiveRecord::RecordNotUnique?
【发布时间】:2015-04-22 17:59:07
【问题描述】:

我正在使用一个 Rails 4 应用程序,该应用程序需要创建大量对象以响应来自另一个系统的事件。当我在我的一个模型上调用create! 时,我在主键列上出现了非常频繁的ActiveRecord::RecordNotUnique 错误(由PG::UniqueViolation 引起)。

我在 SO 上找到了其他建议抢救异常并调用 retry 的答案:

begin
  TableName.create!(data: 'here')
rescue ActiveRecord::RecordNotUnique => e
  if e.message.include? '_pkey' # Only retry primary key violations
    log.warn "Retrying creation: #{e}"
    retry
  else
    raise
  end
end

虽然这似乎有帮助,但对于数据库中已经存在的顺序 ID(日志条目缩写),我仍然收到 ActiveRecord::RecordNotUnique 错误:

WARN -- Retrying creation: PG::UniqueViolation: DETAIL: Key (id)=(3067) already exists.
WARN -- Retrying creation: PG::UniqueViolation: DETAIL: Key (id)=(3068) already exists.
WARN -- Retrying creation: PG::UniqueViolation: DETAIL: Key (id)=(3069) already exists.
WARN -- Retrying creation: PG::UniqueViolation: DETAIL: Key (id)=(3070) already exists.

它尝试的 ID 在 3000-4000 范围内,即使有问题的表中有超过 90000 条记录。

为什么 ActiveRecord 或 PostgreSQL 会浪费大量时间依次尝试现有 ID?


原始异常(简化/删除的查询字符串):

{
  "exception": "ActiveRecord::RecordNotUnique",
  "message": "PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint \"table_name_pkey\"\nDETAIL:  Key (id)=(3023) already exists."
}

【问题讨论】:

    标签: sql ruby-on-rails ruby postgresql activerecord


    【解决方案1】:

    您还可以使用 rails 控制台重置表“table_name”的序列

    > ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
    

    (在 rails 3.2、rails 5.0.1 中测试)

    【讨论】:

      【解决方案2】:

      我不确定它是怎么发生的,但事实证明表的主键的 PostgreSQL 序列不知何故被重置或与表不同步:

      SELECT nextval('table_name_id_seq');
      -- 3456
      
      SELECT max(id) FROM table_name;
      -- 95123
      

      我不得不在表的最后一个 ID 处重新启动主键序列:

      ALTER SEQUENCE table_name_id_seq RESTART 95124;
      

      更新:这是一个 Rake 任务,用于重置 Rails 4 和 PostgreSQL 项目中大多数模型的 ID 序列:

      desc 'Resets Postgres auto-increment ID column sequences to fix duplicate ID errors'
      task :reset_sequences => :environment do
        Rails.application.eager_load!
      
        ActiveRecord::Base.descendants.each do |model|
          unless model.attribute_names.include?('id')
            Rails.logger.debug "Not resetting #{model}, which lacks an ID column"
            next
          end
      
          begin
            max_id = model.maximum(:id).to_i + 1
            result = ActiveRecord::Base.connection.execute(
              "ALTER SEQUENCE #{model.table_name}_id_seq RESTART #{max_id};"
            )
            Rails.logger.info "Reset #{model} sequence to #{max_id}"
          rescue => e
            Rails.logger.error "Error resetting #{model} sequence: #{e.class.name}/#{e.message}"
          end
        end
      end
      

      以下参考资料被证明是有用的:

      【讨论】:

      • 对 ID 和序列如何不同步有任何想法吗?这也发生在我身上,修复很容易,但我想知道问题最初是如何发生的......
      • 在我的情况下,由于直接设置 ID 的批量数据导入,序列不同步,但没有更新序列。 也许并发进程同时创建大量记录时也会发生这种情况?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多