2015-02-20 2 views
4

Я работаю с приложением Rails 4, которому необходимо создать большое количество объектов в ответ на события из другой системы. Я часто встречаю ошибки ActiveRecord::RecordNotUnique (вызванные PG::UniqueViolation) в столбце первичного ключа, когда я вызываю create! на одной из моих моделей.Rails: повторяется ActiveRecord :: RecordNotUnique при создании объектов с помощью Postgres?

я нашел другие ответы на 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 

Хотя это, кажется, помогает, я все еще получаю тонн из 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. 

идентификаторы он пытается находятся в 3000-4000 диапазоне, несмотря на наличии более 90000 записей в таблице.

Почему ActiveRecord или PostgreSQL тратят столько времени на последовательное тестирование существующих идентификаторов?


Оригинальное исключение (упрощенная строка/удаленный запрос):

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

ответ

16

Я не знаю, как это случилось, но оказалось, что последовательность PostgreSQL для первичного ключа таблицы было как-то сбросить или вышел из синхронизации с таблицей:

SELECT nextval('table_name_id_seq'); 
-- 3456 

SELECT max(id) FROM table_name; 
-- 95123 

мне пришлось перезапустить первичный ключ последовательности наконец ID стола:

ALTER SEQUENCE table_name_id_seq RESTART 95124; 

Update: вот задача Rake сбросить последовательность ID для большинства моделей на Rails 4 с проектом PostgreSQL:

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 

Следующие ссылки доказанного полезны:

+0

Любые мысли о том, как идентификаторы и последовательность могут выйти из синхронизации? Это случилось со мной тоже, и исправление прост, но я хотел бы знать, как могла возникнуть проблема изначально ... –

+1

В моем случае последовательности вышли из синхронизации из-за импорта объемных данных, которые устанавливают идентификаторы напрямую, но не обновлял последовательности.* Возможно, это также может произойти, если одновременные процессы создают множество записей одновременно? – nitrogen

4

Вы также можете сбросить последовательность из таблицы 'table_name' с помощью рельсов консоли

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

(испытано в рельсах 3.2)

Смежные вопросы