Idempotentny, idempotentność – idempotent – czyli właściwie co?
W dzisiejszym odcinku zajmiemy się dobrą praktyką programistyczną, która polega na pisaniu idempotentnego kodu.
Z tego odcinka dowiesz się:
- co to jest idempotentny kod i dlaczego warto go pisać?
- jak pisać skrypty bazodanowe odporne na wielokrotne uruchamianie;
- jakie są dobre praktyki związane z pisaniem usług sieciowych typu REST;
- co ma wspólnego rodzenie dzieci z informatyką?
Idempotentny – w uproszczeniu oznacza to, że niezależnie od tego, ile razy wykonamy daną operację, to wynik zawsze będzie taki sam.
Zobaczmy to na kilku przykładach, żeby było to bardziej zrozumiałe.
Przyjrzymy się idempotentności w kontekście matematyki, codziennego życia oraz oczywiście informatyki.
Jeżeli chodzi o programowanie, przedstawię Ci kilka przykładów z wykorzystaniem baz danych oraz usług sieciowych typu REST.
Spis treści
Idempotent – matematyka
- Mnożenie przez zero
Niezależnie od tego, ile razy pomnożymy coś przez zero, wynik zawsze będzie taki sam – zero.
Przykładowo: 10*0=0, 100*0=0 itp. - Mnożenie przez jeden
Mnożenie przez jeden nie zmienia wyniku, dlatego bez względu na to, ile razy byśmy nie pomnożyli naszej liczby przez jeden, to wynikiem zawsze będzie ta liczba.
Przykładowo: 10*1=10, 100*1=100 itp.
Idempotent – prawdziwe/codzienne życie
W prawdziwym życiu też mamy przykłady idempotentnych zachowań.
- Ciąża
Jeżeli kobieta jest już raz w ciąży, to bardziej już w ciąży nie będzie. - Śmierć
Podobnie jest ze śmiercią. Raz zabitej osoby nie można już bardziej zabić… choćbyś nie wiem, jak bardzo się starał. Pomijamy tu oczywiście filmy z serii: „Zabili go i uciekł”.
Idempotent – a informatyka
No dobrze, ale co to wszystkiego ma wspólnego z programowaniem?
Informatyka, a w szczególności różnego rodzaju systemy rozproszone bardzo chętnie korzystają z idempotentnych rozwiązań. Głównie dlatego, że idempotentne operacje można bezkarnie powtarzać. Jeżeli nie mamy pewności, że pierwsza operacja doszła do skutku, zamiast weryfikować to, zwyczajnie ją powtarzamy. Jest to dużo prostsze i bezpieczniejsze podejście.
Przejdźmy teraz już do przykładów ściśle związanych z informatyką.
Idempotent – a bazy danych
- Usuwanie rekordów – usuwanie rekordów z natury jest idempotentne – raz usunięty rekord, niezależnie od tego, ile razy byśmy nie próbowali go usunąć, będzie już usunięty.
delete from table where id = 1;
- W podobny sposób funkcjonuje aktualizacja danych – można tylko raz nadpisać dane, kolejne próby aktualizacji nie zmienią już naszego wpisu.
update table set title = 'Note 1' where id = 1;
- Odczyt danych z natury też jest idempotentny.
select * from table where id = 1;
Co jednak z bardziej skomplikowanymi problemami, jak np. dodawanie nowych rekordów czy modyfikacja struktury bazy danych?
Tutaj niestety musimy radzić sobie już samodzielnie i będzie to wymagało napisania przez nad odrobiny kodu.
Dodawanie danych bez duplikatów
Standardowy insert, jeżeli będzie wielokrotnie uruchamiany, za każdym razem doda nowy wiersz do tabeli. Jest to jak najbardziej naturalne i zazwyczaj pożądane zachowanie.
W niektórych sytuacjach jednak chcemy uniknąć duplikatów, które mogły powstać przez przypadkowe wielokrotne uruchomienie skryptu lub wcześniejsze ręczne dodanie danych.
- UNIQUE
W takiej sytuacji możemy skorzystać np. z ograniczenia UNIQUE na kluczu biznesowym, którego unikatowość chcemy zachować. Nie jest to złe rozwiązanie, jednak w przypadku próby dodania duplikatu zostanie zwrócony błąd, a co za tym idzie, cały nasz skrypt będzie wycofany.alter table note add constraint unique_title unique(title);
- Odczyt przed dodaniem
Alternatywnie przed dodaniem takiego rekordu możemy najpierw sprawdzić, czy nie został on już wcześniej dodany. Czyli najpierw robimy select, a potem insert, tylko jeżeli nie znaleźliśmy odpowiedniego rekordu.DO $$ DECLARE row record; BEGIN SELECT * INTO STRICT row FROM note WHERE id = 1; EXCEPTION WHEN NO_DATA_FOUND then insert into note (id, title, content) values (1, 'Note 1', 'Note...'); END; $$
- Usunięcie przed dodaniem
Na początku skryptu możemy również podjąć próbę usunięcia takiego rekordu, a potem go dodać. Nie zawsze jest to bezpieczne wyjście, ponieważ możemy stracić dodane w ten sposób dane, jednak czasami to wystarczy.
Należy też uważać, czy podczas takiej operacji nie zmienią nam się klucze główne.delete from note where id = 1; insert into note (id, title, content) values (1, 'Note 1', 'Note...');
Modyfikacja struktury
Jedną z dobrych praktyk wdrażania zmian na bazie danych jest wykonywanie ich w ramach transakcji – dzięki czemu będziemy mieli pewność, że się wykonają całe lub wcale.
Jest to jak najbardziej słuszne podejście, jednak nie za każdym razem sprawdza się idealnie. Wyobraź sobie sytuację, w której chcesz dodać nowe pole do tabeli, a następnie dodać nowy wpis do tej samej tabeli.
alter table note add column title varchar(255); insert into note (id, title, content) values (1, 'Note 1', 'Note...');
Jeżeli to pole istniało już wcześniej, to cały skrypt zostanie wycofany, a co za tym idzie, nasz insert nie zadziała.
DO $$ BEGIN ALTER TABLE note ADD COLUMN title varchar(255); EXCEPTION WHEN duplicate_column THEN RAISE NOTICE 'column title already exists in note.'; END; $$
Jeżeli natomiast przewidzimy w naszym skrypcie taką sytuację i przechwycimy taki wyjątek, to bez wycofywania całej transakcji możemy kontynuować wykonywanie skryptu.
Idempotent – a usługi REST
Z pojęciem idempotent można też dość często spotkać się w kontekście usług typu REST i żądań HTTP.
Nie chcę w tym miejscu skupiać się na samych usługach sieciowych, jednak tym, co dla nas jest ważne, jest to, że REST to styl architektoniczny, określający zbiór reguł i ograniczeń wykorzystywanych do tworzenia usług sieciowych. Jest to między innymi alternatywa do usług typu SOAP.
Poszczególne metody w tym podejściu określone są, między innymi, przez adres URL oraz wykorzystywaną metodę HTTP. Przykładowo, wpisując adres w przeglądarce internatowej, np. Google Chrome, i naciskając enter, wysyłamy do serwera żądanie HTTP typu GET na ten adres. Natomiast wysłanie dowolnego formularza najczęściej realizowane jest metodą POST.
Przyjrzymy się teraz kolejno większości najczęściej wykorzystywanych metod HTTP i krótko je omówimy.
Pamiętaj jednak, że poniższe założenia to tylko dobre praktyki, których my jako programiści powinniśmy przestrzegać. Niestety na te założenia należy uważać, bo może się zdarzyć, że ktoś nie przestrzegał tych reguł i zaimplementował swoją usługę z pominięciem tych dobrych praktyk.
Metoda HTTP | Idempotent | Bezpieczna |
OPTIONS | tak | tak |
GET | tak | tak |
HEAD | tak | tak |
PUT | tak | nie |
POST | nie | nie |
DELETE | tak | nie |
PATCH | nie | nie |
HTTP GET
Metoda GET wykorzystywana jest zazwyczaj tylko do odczytu danych, np. do pobrania strony przez przeglądarkę, przez co jest idempotentna oraz bezpieczna.
HTTP HEAD
Metoda HEAD działa podobnie do GET, zwracając analogiczne nagłówki – jednak bez ciała żądania – przez co jest również idempotentna i bezpieczna.
HTTP OPTIONS
Metoda OPTIONS wyświetla dostępne opcje komunikacji. Ponieważ jest metodą tylko do odczytu, jest również idempotentna i bezpieczna.
HTTP PUT
Metoda PUT wykorzystywana jest do aktualizacji danych, dlatego nie możemy jej uznać za bezpieczną, gdyż modyfikuje nasze dane. Jest natomiast idempotentna, ponieważ możemy ją wywołać wiele razy i za każdym razem wynik powinien być taki sam.
HTTP POST
Metoda POST służy do tworzenia nowych obiektów. Każde jej wywołanie powinno zakończyć się utworzeniem nowego rekordu, dlatego nie jest ani bezpieczna, ani idempotentna.
HTTP DELETE
Metoda DELETE służy do usuwania danych, przez co jej wykorzystanie jest potencjalnie niebezpieczne. Jest natomiast idempotentna – raz usunięty obiekt będzie zawsze usunięty. Tutaj jednak mała uwaga – kolejne wywołania tej metody mogą zwrócić nagłówek http 404 oznaczający brak takiego obiektu.
HTTP PATCH
Metoda PATCH służy do częściowej aktualizacji obiektu, gdy chcemy np. zaktualizować tylko imię danej osoby, ignorując całkowicie pozostałe pola. Takie wywołanie jest potencjalne niebezpieczne i nie jest idempotentne.
📢📣 Podsumowanie. Uwaga!
Pisanie idempotentnego kodu jest jedną z dobrych praktyk programistycznych, jednak i tutaj należy kierować się zdrowym rozsądkiem i korzystać z niej tylko wtedy, jeżeli tego potrzebujemy. Przygotowanie kodu, który będzie odporny na wielokrotne uruchomienie, zazwyczaj zwyczajnie jest bardziej problematyczne i czasochłonne, a może się okazać, że wcale tego nie potrzebujemy.
Koniec/ogłoszenia
Na koniec mam jeszcze kilka ogłoszeń technicznych:
- Po pierwszych dwóch odcinkach dostałem bardzo dużo pozytywnych wiadomości – jeszcze raz bardzo mocno za nie dziękuję.
Jest to niezmiernie miłe uczucie móc przeczytać, że to, co robię, jest dla kogoś przydatne. - Dzisiejszy odcinek zostanie opublikowany pod adresem stormit.pl/003 – zachęcam do odwiedzania bloga, ponieważ znajdziesz tam, między innymi, transkrypt tego odcinka, przykłady omawianych dzisiaj skryptów oraz jeszcze więcej materiałów na temat programowania.
- Podcast został zgłoszony do iTunes i w momencie, gdy tego słuchasz, powinien być dostępny w większości aplikacji do podcastów.
Proszę, daj mi znać, czy rzeczywiście tak jest. Będę również wdzięczny za informację, z jakiej aplikacji korzystasz do słuchania audycji.
Na dzisiaj to już wszystko, bardzo Ci dziękuję za wspólnie spędzony czas.
Pozdrawiam i miłego dnia!
Dodatkowe materiały
- Kurs Java | Darmowy Kurs Programowania w Javie
- Podcast Sprawny Programista
20+ BONUSOWYCH materiałów z programowania
e-book – „8 rzeczy, które musisz wiedzieć, żeby dostać pracę jako programista”,
e-book – „Java Cheat Sheet”,
checklista – „Pytania rekrutacyjne”
i wiele, wiele wiecej!
2 Comments
Proszę podać uzasadnienie idempotentnosci PATCH.
PATCH nie jest idempotentne.