středa 30. března 2005

Souběžná modifikace dat ve webových aplikacích

Rád bych vám pospal jeden ignorovaný problém, kterému jsou vystaveny všechny webové aplikace, které umožňují editaci dat a může s nimi pracovat více než jeden uživatel. Česky by se dal problém přeložit jako Souběžná modifikace dat, v anglicky psaných meteriálech jej najdete pod názvem concurrency data modification. Pokud tento problém opomeneme při návrhu systému, může nám to přinést mnoho starostí.

Popis

Problém si popíšeme na reálném příkladu elektronického obchodu. Pokud si prohlížíte zboží v elektronickém obchodu, bývá zvykem uvadět počet aktuálně dostupného zboží skladem. Řekněme, že je údaj o dostupném zboží ve společné databázové tabulce.

id Popis Skladem
1 Automatická pračka Fil01 1

Jak je vidět, máme jeden kousek k pračce Fil01. Představme si, že máte o takový výrobek zájem a právě si prohlížíte v prohlížeči jeho detail a vidíte, že je na skladě jeden kousek. Během toho, co se rozhodujete, jestli si ten výrobek pořídíte, přijde další uživatel, který má zálusk na to samé zboží jako vy. Oba dva máte v prohlížeči načtenou stránku s detailem výrobku a počtem jeden kus.

Protože váháte o trochu déle než druhý uživatel, přijde jeho objednávka první na řadu. V domnění, že si objednáváte ten poslední kousek, odesíláte svoji objednávku. Bohužel sklad je prázdný, ale vaše objednávka je vyřízena a jste nuceni čekat na expedici zboží. Prosím, oprostěme se od faktu, že můžeme objednávku stornovat, případně že existuje mezikrok v podobě přidání do košíku. Uvažujme čistě nad problémem souběžné modifikace, kdy klient není přímo v interakci se serverem. Jinými slovy, stránka je načtená v prohlížeči a komunikace se serverem proběhne pouze v případě odeslání požadavku.

Pessimistic vs. Optimistic lock

K řešení tohoto problému se používá technika zamykání a existují dva přístupy, přístup pesimistický a optimistický.

Pessimistic lock

Pesimistic lock je takový přístup, kdy data implicitně zamykáme. Na příkladu elektronického obchodu to znamená, že pokud uživatel A načte detail automatické pračky, nikdo jiný kromě něj nemůže na daném detailu pracovat (číst/měnit). Pessimistic lock se používá v těch případech, kdy předpokládáme, že bude docházet k souběžným modifikacím velice často. Vlastní zamknutí záznamu se řeší přímo nad databází pomocí izolace na úrovni databázové transakce a nebo pomocí klausule SELECT ... FOR UPDATES, pokud jí databáze podporuje.

Optimistic lock

Optimistic lock je naopak přístup, kdy spíše předpokládáme, že k souběžným modifikacím nebude docházet příliš často. Data se proto nezamykají na úrovni databáze, v podstatě by se dalo říci, že je to zamknutí na úrovni aplikační transakce. Záleží na daném middleware, jaké možnosti optimistic lock nám poskytne, případně jak si optimistic lock vyřešíme.

Pessimistic lock tedy předpokládá, že dojde k souběžné modifikaci dat, a proto data implicitně zamyká. Optimistic lock předpokládá, že všechno dopadne dobře, a zamkne vlastní konec aplikační transakce, v našem elektronickém obchodě je to odeslání objednávky. Pokud při optimistickém přístupu dojde k souběžné modifikaci, je o tom aplikace náležitě informována. V našem příkladu, tedy uživatel A a B načte detail. Uživatel A odešle objednávku, B odešle objednávku, ale místo úspěšného odeslání je informován, že stav zboží se během jeho požadavku změnil.

Obě dvě řešení mají své pro a proti a každé z nich se hodí pro jiný typ aplikace. Pessimistic lock je systémově náročný, a proto by k vlastnímu zamčení mělo docházet na dobu co možná nejkratší. Tím pádem nám skoro odpadá použití pro webové aplikace, protože tam není interakce klienta se serverem přímá. Optimistic lock je vhodnější kandidát pro použití ve webových aplikacích, ale nevýhoda je v tom, že se s tím musí počítat při návrhu aplikace.

Optimistic lock inspirován Hibernatem

Na závěr bych chtěl nastínit podporu Optimistic lock, jakou má ORM (Object Relational Mapping) Hibernate. Ten mechanismus je tak obecný, že si jej můžete propritárně implementovat pro jakékoliv prostředí. To si ukážeme na příkladu elektronického obchodu.

Jediná úprava našeho příkladu spočívá v rozšíření tabulky o sloupec verze.

id Popis Skladem Verze
1 Automatická pračka Fil01 1 1

Podpora Hibernate je v tom, že nám při každé modifikaci inkrementuje verzi a zároveň kontroluje, jestli verze námi modifikovaného řádku odpovídá verzi v databázi. Pokud bychom tento přístup chtěli aplikovat bez použití Hibernate udělali bychom to takto.

  • Při přechodu na detail zboží si posíláme včetně business informací i hodnotu verze
  • Při odeslání objednávky si navíc pošleme hodnotu verze
  • Před uložením objednávky si zkontrolujeme, jestli se hodnota poslané verze rovná verzi v DB
  • Pokud jsou verze, stejné provedeme uložení objednávky a zvýšíme hodnotu verze
  • Pokud jsou verze, rozdílné vrátíme uživatele na detail zboží a informujeme ho o změně stavu

Jak je vidět, mechanismus verzování je natolik obecný, že jej můžeme použít pro jakoukoliv tabulku. Nevýhodou je neustále přeposílání verze ze serveru na klienta a zpět.