Как установить уровни изоляции, чтобы после завершения одной транзакции вставленные данные были доступны для другой транзакции?

У меня есть следующие таблицы (сильно упрощенные):

Jobs: JobId, JobState
Data: DataId
JobsData: JobId, DataId

идея JobsData состоит в том, что любой элемент в Data может быть связан с одним или несколькими элементами в Jobs, и каждый элемент в Jobs может иметь один или несколько элементов из Data, связанных с ним.

Теперь у меня две транзакции:

-- TRAN1
BEGIN TRAN
INSERT INTO Data VALUES (NewDataId)
INSERT INTO Jobs VALUES (NewJobId, StateInitial)
INSERT INTO JobsData VALUES (NewDataId, NewJobId)
UPDATE Jobs SET JobState=StateReady WHERE JobId=NewJobId
COMMIT TRAN

-- TRAN2
DECLARE @selectedId;
SELECT TOP (1) @selectedId=JobId FROM Jobs WITH (UPDLOCK, READPAST) WHERE JobState=StateReady
IF @selectedId IS NOT NULL
    SELECT DataId FROM JobsData WHERE JobId = @selectedId

Код с подсказками по блокировке взят из этого ответа. Его цель состоит в том, чтобы несколько TRAN2 экземпляров работали параллельно и никогда не получали одинаковых JobId.

Этот код отлично работает с SQL Server (уровень изоляции по умолчанию READ_COMMITTED), но в SQL Azure TRAN2 иногда работает некорректно - первый SELECT дает ненулевое значение JobId, а второй SELECT дает пустой набор результатов. Я предполагаю, что это связано с тем, что уровень изоляции по умолчанию в SQL Azure равен READ_COMMITTED_SNAPSHOT.

Я хочу внести минимум изменений, чтобы решить мою проблему - чтобы TRAN2 либо извлекал ноль в первом SELECT, либо извлекал правильный набор результатов во втором SELECT.

Какое из совпадений таблицы я могу применить к какому из имеющихся у меня операторов SQL?


person sharptooth    schedule 10.01.2013    source источник


Ответы (1)


Для начала, если вам нужны очереди в Azure, используйте очереди Azure или служебную шину Очереди.

Если вы настаиваете на реализации очередей вместо отношений, используйте шаблон из Использование таблиц как Очереди. Конкретно:

  • не используйте поля состояния вместо очередей событий («Готово» - это запись в вашей очереди заданий, событие, не состояние задания).
  • удалить из очереди с помощью DELETE ... WITH OUTPUT ...

Поверьте мне в этом.

person Remus Rusanu    schedule 10.01.2013
comment
Все это хорошо, но как этот шаблон обращается к случаям, когда элемент извлекается из очереди, а затем объект, который его извлек, завершает работу? - person sharptooth; 10.01.2013
comment
Семантика транзакции по-прежнему применяется. BEGIN TRANSACTION/Dequeue/Process/COMMIT. В случае сбоя удаление из очереди отменяется как часть отката. Конечно, многие «обработки» слишком продолжительны / непредсказуемы (например, HTTP-вызов), чтобы позволить им происходить в транзакции. Типичным шаблоном для этого является использование «ожидающей очереди» и предварительная публикация события «повторная попытка», ожидающего, скажем, через 10 минут: BEGIN TRAN/dequeue/post retry Time.Now+10.minutes/COMMIT/process/BEGIN TRAN/cancel retry/COMMIT. Это гарантирует, что в случае сбоя событие будет повторено через разумное время. - person Remus Rusanu; 10.01.2013
comment
Я понимаю, но это сильно усложняет мой код. Могу ли я просто исправить существующий код с помощью подсказок блокировки? - person sharptooth; 10.01.2013
comment
Нет. Вы можете попытаться исправить «удаление из очереди» с помощью чего-то вроде with u as (SELECT TOP (1) DataId, JobState FROM Jobs WITH (READPAST) WHERE JobState='StateReady') update u set JobState='Working' output deleted.DataId, но вы все равно не решили проблему сбоя / повторной попытки. - person Remus Rusanu; 10.01.2013
comment
Как это решит мою проблему? Моя проблема в том, что SELECT возвращает элемент в правильном состоянии, но данные в другой таблице, соответствующей этому элементу, еще не стали видимыми для транзакции. - person sharptooth; 10.01.2013
comment
Нет, это не твоя проблема. Ваш код неверен в условиях параллелизма. В Azure вы получаете другое время, и вы видите это чаще. - person Remus Rusanu; 10.01.2013
comment
Ну ладно, как мне изменить код, чтобы второй SELECT увидел нужные данные? - person sharptooth; 10.01.2013
comment
Как предотвратить удаление строк из JobsData с @selectedId между двумя операциями SELECT? Можете ли вы гарантировать, что удаление произойдет только, если соответствующая строка @selectedId в Jobs заблокирована X первой? - person Remus Rusanu; 10.01.2013
comment
Предположим, что строки из JobsData никогда не удаляются, а только вставляются. - person sharptooth; 10.01.2013