Idempotent – idempotentny, czyli jaki?

Idempotent

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ą?
[SprawnyProgramista_intro]

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.

Idempotent – matematyka

  1. 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.
  2. 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ń.

  1. Ciąża
    Jeżeli kobieta jest już  raz w ciąży, to bardziej już w ciąży nie będzie.
  2. Ś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

  1. 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;
  2. 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;
  3. 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.

  1. 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);
    SQL duplicated key

    SQL duplicated key

  2. 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;
    $$
  3. 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

 

kierunek java


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!

Jak zostać programistą

2 komentarze
Share:

2 Comments

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *