Повторный конфликт first_or_initialize - PullRequest
0 голосов
/ 11 сентября 2018

Я смотрю на сбой повторной попытки 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...