Я смотрю на сбой повторной попытки first_or_initialize
, который "очевидно невозможен".Каким-то образом это произошло даже при разрешении нескольких повторных попыток.
Таблица содержит столбцы id
(целочисленный первичный ключ), uuid
(двоичный код) и item_id
(целое число).И uuid
, и item_id
имеют уникальные индексы.
Метод должен получить uuid элемента или создать его, если он не существует:
def self.get_uuid_for(item)
Retryable.retryable(tries: MAX_RETRIES, on: [ActiveRecord::RecordNotUnique], sleep: 0) do
ActiveRecord::Base.transaction(requires_new: true) do
item_uuid = where(item_id: item).first_or_initialize do |item_uuid|
item_uuid.uuid = SecureRandom.uuid
item_uuid.save!
end
item_uuid.uuid
end
end
end
При локальном запуске онработает:
- без существующей записи (SELECT, INSERT)
- с существующей записью (SELECT)
- с конкурирующей транзакцией (SELECT, INSERT, ROLLBACK, SELECT)
К сожалению, в какой-то момент сборщик ошибок получил:
ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry '22573725' for key 'index_item_uuids_on_item_id'
Что я пытаюсь объяснить.MAX_RETRIES равняется 100, поэтому, даже если каким-либо образом ассоциация создавалась и удалялась все время, существует вероятность того, что все эти попытки действительно потерпят неудачу.Это не тот случай, когда параллельная транзакция занимает много времени, потому что в этом случае INSERT для item_uuid.save!
зависает в ожидании блокировки.
Какой сценарий я пропускаю?
База данныхтакое MySQL / AWS Aurora 5.6.Уровень изоляции REPEATABLE-READ
.