Първият пример е за система, в която постъпват данни от много места (асинхронно) и се правят се различни обработки (асинхронно). По всяко време някой процес може да извика дадена процедура и понякога това може да предизвика конфликти, които да доведат до дублиране (по-добрия вариант) или загуба на информация.
Ето примерна процедура, която води до дублиране на информация:
create or replace procedure Move_Some_Data is
-- example code - has a BUG!!!
begin
-- find all non-archived batches
for batches in (select batch_id
from batch_log
where archived = 0) loop
-- archive the current batch
insert /*+append*/
into arch_table
select *
from prod_table
where batch_id = batches.batch_id;
-- delete the rows from prod table
delete from prod_table
where batch_id = batches.batch_id;
... -- other tables containing batch information
-- mark the batch as archived
update batch_log
set archived = 1
where batch_id = batches.batch_id;
-- finish the transaction
commit;
end loop;
end;
В този случай, ако по някаква грешка пуснем процедурата 2 пъти (почти) едновременно, има голяма шанс едни и същи данни да се случат в архива 2 пъти. Ето един интересен вариант:
– Сесия 1 изпълнява select заявката и получава курсор към не-архивираните batches 89, 90, 91 и 92. Започва цикъл за прехвърляне.
– Сесия 1 минава една итерация и прехвърля batch 89. Започва insert – select от втора итерация за batch 90
– Сесия 2 се включва в този момент. Изпълнява бързия select и получава курсор към не-архивираните (към този момент) batches 90, 91 и 92. Започва цикъл за прехвърляне.
– Сесия 2 също започва insert – select за прехвърляне на batch 90
– Сесия 1 завършва прехвърлянето на batch 90 и изтрива данните. Това не пречи по никакъв начин на сесия 2, защото тя получава consistent read копие на данните от преди изтриването
– Нататък може да стават всякакви произволни комбинации. Примерно ако данните в batch 91 са значително по-малко от тези в batch 90, сесия 1 може да ги прехвърли и изтрие преди сесия 2 да е свършила с първата си итерация. В този случай когато Сесия 2 стигне до втората си итерация, ще направи insert на 0 реда (и съответно delete), което би било много забавно за наблюдение ако има организиран log.
Елементарната защита в такъв случай би била вместо select да правим select for update на таблица batch_log. Това, в случая, не върши работа, защото (съвсем коректно), след всяка прехвърлена група записи се прави commit.
Едно по-добро решение ще предложа малко по-късно.
Sorry, the comment form is closed at this time.