nit*_*gen 5 ruby sql postgresql activerecord ruby-on-rails
我正在使用Rails 4应用程序,该应用程序需要创建大量对象以响应来自其他系统的事件.当我调用我的某个模型时,我在主键列上遇到非常频繁的ActiveRecord::RecordNotUnique错误(由此引起PG::UniqueViolation)create!.
我在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
Run Code Online (Sandbox Code Playgroud)
虽然这似乎帮助,我仍然得到吨的ActiveRecord::RecordNotUnique误差,对于已经存在于数据库(日志条目略)顺序ID:
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.
Run Code Online (Sandbox Code Playgroud)
它正在尝试的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."
}
Run Code Online (Sandbox Code Playgroud)
nit*_*gen 17
我不确定它是如何发生的,但事实证明,表的主键的PostgreSQL序列以某种方式重置或与表不同步:
SELECT nextval('table_name_id_seq');
-- 3456
SELECT max(id) FROM table_name;
-- 95123
Run Code Online (Sandbox Code Playgroud)
我必须在表的最后一个ID重新启动主键序列:
ALTER SEQUENCE table_name_id_seq RESTART 95124;
Run Code Online (Sandbox Code Playgroud)
更新:这是一个Rake任务,用于在PostgreSQL项目的Rails 4上重置大多数模型的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
Run Code Online (Sandbox Code Playgroud)
以下参考证明是有用的:
您还可以使用rails控制台重置表'table_name'的序列
> ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
Run Code Online (Sandbox Code Playgroud)
(在导轨3.2,导轨5.0.1中测试)
| 归档时间: |
|
| 查看次数: |
3507 次 |
| 最近记录: |