Я пишу демон для мониторинга создания новых объектов, который добавляет строки в таблицу базы данных, когда обнаруживает новые вещи. Мы будем называть объекты виджетами. Поток кода примерно такой:
1: every so often:
2: find newest N widgets (from external source)
3: foreach widget
4: if( widget not yet in database )
5: add rows for widget
Последние две строки представляют состояние гонки, так как, если два экземпляра этого демона работают одновременно, они могут оба создать строку для виджета X, если синхронизируется время.
Наиболее очевидным решением было бы использование ограничения unique
для столбца идентификатора виджета, но это невозможно из-за разметки базы данных (на самом деле разрешено иметь более одной строки для виджета, но демон никогда не должен делать это автоматически).
Моя следующая мысль будет заключаться в использовании транзакции, поскольку это то, для чего они предназначены. Я полагаю, что в мире ADO.NET я бы хотел уровень изоляции Serializable
, но я не уверен. Может ли кто-нибудь указать мне правильное направление?
Обновление : Я провел некоторые эксперименты, и Сериализованная транзакция, кажется, не решает проблему, или, по крайней мере, не очень хорошо. Интересный случай описан ниже и предполагает, что используется только одна таблица. Обратите внимание, что я не уверен в деталях блокировки, но думаю, что я правильно понял:
Thread A: Executes line 4, acquiring a read lock on the table
Thread B: Executes line 4, acquiring a read lock on the table
Thread A: Tries to execute line 5, which requires upgrading to a write lock
(this requires waiting until Thread B unlocks the table)
Thread B: Tries to execute line 5, again requiring a lock upgrade
(this requires waiting until Thread A unlocks)
Это оставляет нас в классическом тупиковом состоянии. Возможны и другие пути кода, но если потоки A и B не чередуются, проблема синхронизации все равно не возникает. Конечным результатом является то, что SqlException генерируется в одном из потоков после того, как SQL обнаруживает тупик и завершает один из операторов. Я могу поймать это исключение и обнаружить конкретный код ошибки, но он не очень чистый.
Другой путь, по которому я могу пойти, - создать вторую таблицу, которая будет отслеживать виджеты, видимые демоном, где я могу использовать ограничение unique
. Это все еще требует перехвата и обнаружения определенных кодов ошибок (в данном случае, нарушений целостности), поэтому я все еще заинтересован в лучшем решении, если кто-то может подумать об этом.