Boisz się COMMIT'a?




Jakiś czas temu pijąc kawkę w pracy, leniwie przeglądałam kod aplikacji. Nagle - prawie się zachłysnęłam! Kod, który ukazał się moim oczom zaintrygował mnie. 
A oto ów kod:
begin

-- some code
-- more code
begin
COMMIT;
exception when others then
rollback;
end;
-- log error
-- of course, more code

end;
COMMIT w bloku BEGIN/END. Ale czemu? Nigdy nie widziałam takiej konstrukcji. Zapytam.
- A czemu COMMIT w BEGIN/END dałeś?
- A tak, na wszelki wypadek. - słyszę odpowiedź.

Na wszelki wypadek..Hm...

COMMIT zatwierdza zmiany, które zostały w zasadzie już wykonane. Jeśli coś miałoby się nie udać, to raczej wcześniej, w trakcie wykonywania konkretnych operacji takich jak INSERT, UPDATE itp. COMMIT to tylko kropka nad i. (No oczywiście w trakcie COMMITA serwer może stanąć w płomieniach, no ale umówmy się, BEGIN/END tutaj nie pomoże...).

Ale tak wszelki wypadek sprawdzę...

Testy przeprowadziłam na bazie Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production.
-- próba z niepoprawnym insertem
set serveroutput on

begin
insert into countries (country_id, country_name, region_id)
values ('GEO', 'GEORGIA', 1);
begin commit;
rollback;
dbms_output.put_line('commited');
exception when others then
rollback;
-- log error
dbms_output.put_line('in the commit''s exception');
/
end; exception when others then -- log error
dbms_output.put_line('in the main exception');
end;
in the main exception
W przypadku błędnego insertu nawet nie weszliśmy do BEGIN/END; COMMIT'a. Wypadliśmy w głównym EXCEPTION.

-- próba z poprawnym insertem (grupa kontrolna :D) 
set serveroutput on

begin
insert into countries (country_id, country_name, region_id)
values ('GE', 'GEORGIA', 1);
begin commit;
rollback;
dbms_output.put_line('commited');
exception when others then
rollback;
-- log error
dbms_output.put_line('in the commit''s exception');
/
end; exception when others then -- log error
dbms_output.put_line('in the main exception');
end;
commited
W przypadku poprawnego insert'a - weszliśmy w BEGIN/END COMMIT'a, transakcja została zatwierdzona. I już. 
Nic nieprzewidywalnego.

Ale ale... A co z bazami rozproszonymi? Zobaczmy. 
Utworzyłam dwie bazy danych, na jednej mam schemat HR, na drugiej SAL. Szczegółowa struktura nie jest istotna.  Na schemacie SAL utworzyłam db link do bazy ze schematem HR
No to do dzieła!
set serveroutput on

begin
insert into countries@hr (country_id, country_name, region_id)
values ('GE', 'GEORGIA', 1);
commit COMMENT 'ORA-2PC-CRASH-TEST-1';
begin
dbms_output.put_line('commited');
exception when others then
dbms_output.put_line('in the commit''s exception');
-- log error
dbms_output.put_line( sqlerrm);
end;
exception when others then
dbms_output.put_line('in the main exception');
rollback;
-- log errorCOMMIT
end;
/
PL/SQL procedure successfully completed.

in the commit's exception
ORA-02050: transaction 3.18.1427577 rolled back, some remote DBs may be in-doubt
ORA-02059: ORA-2PC-CRASH-TEST-1 in commit comment
Ha! Zepsułam COMMIT! Wspaniale :)
Czyli jednak jest troszeczkę szaleństwa w COMMIT'cie :) 

Otaczanie COMMIT'a w BEGIN/END jest zbytnim, niepotrzebnym zabiegiem. Problem może wystąpić jedynie w bazach rozproszonych, a nieco większa uwaga przy projektowaniu może zminimalizować ryzyko. Osobiście nigdy nie byłam świadkiem takiego błędu w praktyce. Nie jest to najwyraźniej częsty błąd.
Także nie bać się, commitować :)

Nieco więcej szczegółów na ten temat wygrzebanych z neta.

Komentarze