Недавно у меня была задача, согласно которой мне нужно было иметь определенный функционал блокировки. Специфика была обусловлена:
- Транзакция, которая обновляла таблицу, была распределенной, поэтому я не мог ее контролировать,
- В течение дня должны одновременно поддерживаться тысячи неблокирующих транзакций, назовем их «общими» операциями,
- Каждая «общая» операция обновляла строки в конкретной «ветви» («LDN», «NY», «LA» ...),
- Once a day there is a 'master' operation for each branch, which happen spanteniously, on different branches,
- During 'master' operation no 'general' operations on that branch can happen.
- При запуске «мастерской» операции она должна дождаться завершения текущей «общей» операции по предоставленной ветке, которая была в системе до прихода «мастерской» операции.
- Во время «основной» обработки в определенной ветке все остальные ветки могут быть обновлены.
Чтобы заархивировать это, я создал специальную таблицу Oracle DB.
create table BRANCH_LOCK(
BRANCH VARCHAR2(10),
FLAG VARCHAR2(1),
CONSTRAINT "PK_BRANCH_LOCK" PRIMARY KEY ("BRANCH")
)
Поддерживался следующий функционал для различных операций:
Для «общих» операций:
1. In the same XA transaction each operation locks BRANCH_LOCK table in SHARE mode, 2. After locking it checks FLAG, on updated branch, 1. If flag is 'Y', that means that currently 'master' operation is in progess, so Exception is thrown, and no further processing is done; 2. If flag is 'N' than everything is OK, and general processing is done;
Для «ведущей» операции:
- When 'master' operation comes I start separate transaction which:
- Lock BRANCH_LOCK table in EXCLUSIVE mode, which transaction can not acquire while there is SHARE mode LOCK on this table in a different transaction (This way, I guarantee that 'master' operation would start after all current 'general' operation finish, although it waits for transactions on all branches to finish, not only specified one),
- Устанавливает флаг для ветки на «Y» (таким образом я гарантирую, что не будет «общих» транзакций при обработке «основной» операции),
- Во входящей транзакции я меняю флаг в таблице на «N», поэтому после фиксации таблица BRANCH_LOG будет иметь соответствующее значение в столбце FLAG, и система сможет снова обрабатывать «общие» операции.
Это еще не запущено в производство, поэтому мне интересно, есть ли лучшее решение для этого, и есть ли еще недостатки, кроме описанного?
Некоторые обновления, о которых я не упомянул:
- «Главная» операция работает с результатами «общих» операций. Поэтому очень важно, чтобы ни одна «общая» операция не была потеряна во время «основной» обработки, поэтому текущая «общая» операция должна быть завершена до начала обработки основной операции. .
- Несколько «общих» операций в одной и той же ветке происходят каждую секунду, около 3000 операций в секунду,
- Для ветки может выполняться только одна «главная» операция, одновременно могут выполняться несколько «главных» операций в разных ветвях.