2012-04-21 2 views
5

Из документации для find_or_create:Как избежать условий гонки при использовании метода find_or_create DBIx :: Class :: ResultSet?

Примечания: Поскольку find_or_create() считывает из базы данных, а затем, возможно, вставки на основании результата, этот метод подлежит гоночный состояния. Другой процесс может создать запись в таблице после , которая была найдена и до начала создания. Чтобы избежать ошибки , используйте функцию find_or_create() внутри транзакции.

Достаточно ли использовать только find_or_create() внутри транзакции в PostgreSQL?

ответ

6

Нет, документация неверна. Использование только транзакции не избегайте этой проблемы. Это гарантирует только откат всей транзакции, если произойдет исключение - чтобы в базе данных не сохранялось несогласованное состояние.

К избегать этой проблемы вы должны заблокировать таблицу - внутри транзакции, поскольку все блокировки освобождаются в конце транзакции. Что-то вроде:

BEGIN; 
LOCK TABLE mytbl IN SHARE MODE; 

-- do your find_or_create here 

COMMIT; 

Но это не волшебное лекарство от всего. Это может стать проблемой производительности, и может быть deadlocks (параллельные транзакции взаимно пытаются заблокировать ресурсы, которые другой заблокировал уже). PostgreSQL обнаружит такое условие и отменит все, кроме одной из конкурирующих транзакций. Вы должны быть готовы повторить операцию при сбое.

The PostgreSQL manual about locks.

Если у вас нет много параллелизма вы также можете просто игнорировать эту проблему. Временной интервал очень крошечный, поэтому на самом деле это происходит очень редко. Если вы поймаете ошибку дублирования ключевого нарушения, которая не принесет вреда, вы тоже это рассмотрели.

+2

Другие полезные страницы. Документы: http://www.postgresql.org/docs/current/interactive/mvcc.html PostgreSQL версии 9.1 или более поздней сериализуемой реализации: http://wiki.postgresql.org/wiki/SSI Другие уровни изоляции или версии PostgreSQL: http : //www.postgresql.org/files/developer/concurrency.pdf – kgrittn

+0

Но каков правильный способ поймать «ошибку дублирования ключевого нарушения» в DBIC? –

+0

@eugeney: Я полагаю, вы открываете для этого новый вопрос, а не комментарий. Вы всегда можете ссылаться на этот, чтобы сохранить себе некоторую типизацию. –

0

Эта реализация find_or_create должна предотвратить состояние гонки, описанный в OP:

eval { 
    $row = $self->model->create({ ... }); 
} 
if([email protected] && [email protected] =~ /duplicate/i) { 
    $row = $self->model->find({ ... }); 
} 

Это также уменьшает find_or_create() один запрос в лучшем случае.

+1

Это отменяет логику. Но теперь у вас есть крошечный временной интервал, в котором запись может быть удалена - в этом случае логика завершится неудачно. Попытка написать сначала дороже, чем пытаться читать. Так что это только улучшение, если дубликаты очень необычны. В любом случае, конфликты должны быть очень редкими, потому что временной интервал крошечный. –

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

  • Нет связанных вопросов^_^