2015-05-18 2 views
0

Я работаю над веб-проектом на PHP, где пользователь нажимает кнопку «Обновить», и начинается длительный процесс.Обновление должно быть видно за пределами транзакции (проблема синхронизации)

Что я хочу, так это то, что если один пользователь уже запускает этот процесс, то другие пользователи могут сказать, когда они загружают страницу, процесс уже запущен, время начала и кто ее запустил. Они не смогут запустить процесс снова, когда он уже запущен.

я мог бы сделать что-то вроде этого (SQL ниже упрощенно):

-- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production 

SELECT 1 FROM institutions WHERE institution_code = '15' FOR UPDATE; 

UPDATE institutions SET process_start = CURRENT_TIMESTAMP, 
    process_acct_id = 101, process_finish = null WHERE institution_code = '15'; 

-- Process Runs here, taking a couple of hours. 

UPDATE institutions SET process_finish = CURRENT_TIMESTAMP 
    WHERE institution_code = '15'; 

COMMIT; 

Проблема с вышесказанным, что другие потоки не могут видеть process_acct_id, process_start и process_finish до окончания процесса запуска и сделка совершена. Как мне написать так, чтобы они могли? Я запускаю первое обновление в автономной транзакции? Или есть лучший способ?

+0

Я не уверен, что вы имеете в виду. Не могли бы вы подробнее рассказать о том, как это будет реализовано? –

ответ

0

Я не знаком с Oracle, поэтому синтаксис не может быть правильным, но я пошлю только одну операцию в транзакции. Я использовал столбец «Active» вместо временных меток, чтобы упростить его, но вы можете разработать аналогичную логику, используя ваши временные метки. Кроме того, я предположил, что вы определяете действия по коду взыскания.

BEGIN TRAN 

UPDATE Active = 1 WHERE Active = 0 AND insititution_code = @InstitutionCode 

IF @@ROWCOUNT = 0 
BEGIN 
    RETURN 0 
END 
ELSE 
BEGIN 
    RETURN 1 
END 

COMMIT 

Дело в том, что обновление будет блокировать другие транзакции и блок кода возвращает 1, если удалось обновить строку или 0 в противном случае. Затем вы можете добавить свою логику вокруг этого, зная, что тот, который был успешным, чтобы установить флаг в 1, является единственным, кто работает.

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

0

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

UPDATE institutions 
SET process_acct_id = 101, 
    process_start = CURRENT_TIMESTAMP, 
    process_finish = null 
WHERE institution_code = '15' 
AND process_acct_id IS NULL; -- so no rows will be updated if a process is running 

-- In PHP layer, check how may rows were updated (like SQL%ROWCOUNT) 
-- If zero then another process is already running, so stop 
-- With two simultaneous calls this will block briefly for the second call, 
-- until the first commits when it sees row count of 1 

COMMIT; 

-- Other sessions now see process ID and start time 
-- Process Runs here, taking a couple of hours. 

UPDATE institutions 
SET process_acct_id = null, 
    process_finish = CURRENT_TIMESTAMP 
WHERE institution_code = '15'; 

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