Проблема, которую мы пытаемся решить, выглядит следующим образом.
- У нас есть таблица, полная строк, которые представляют карты.Цель транзакции бронирования - присвоить клиенту карту
- Карта не может принадлежать многим клиентам
- Через некоторое время (если оно не куплено) карту необходимо вернутьпул доступных ресурсов
- Резервирование может выполняться многими клиентами одновременно
- Мы используем базу данных Oracle для хранения данных, поэтому решение должно работать как минимум на Oracle 11
Наше решение - присвоить карточке статус и сохранить дату бронирования.При резервировании карты мы делаем это с помощью оператора «выбрать для обновления».Запрос ищет доступные карты и карты, которые были зарезервированы давно.
Однако наш запрос не работает должным образом.
Я подготовил упрощенную ситуацию, чтобы объяснить проблему.У нас есть таблица card_numbers, полная данных - все строки имеют ненулевые идентификаторы.Теперь давайте попробуем заблокировать некоторые из них.
-- first, in session 1
set autocommit off;
select id from card_numbers
where id is not null
and rownum <= 1
for update skip locked;
Мы не фиксируем транзакцию здесь, строка должна быть заблокирована.
-- later, in session 2
set autocommit off;
select id from card_numbers
where id is not null
and rownum <= 1
for update skip locked;
Ожидаемое поведение состоит в том, что в обоих сеансах мы получаем одну отдельную строку, которая удовлетворяет условиям запроса.
Однако так не получается.В зависимости от того, используем ли мы часть запроса «пропустить заблокирован» или нет - поведение меняется:
- без «пропуска заблокирован» - второй сеанс блокируется - ожидание фиксации транзакции или отката в первом сеансе
- с «пропуском заблокирован» - второй запрос немедленно возвращает пустой набор результатов
Итак, после этого длинного вступления возникает вопрос.
Возможно ли желаемое поведение блокировки в Oracle?Если да, то что мы делаем не так?Какое будет правильное решение?