Tech Life

Ilustrační obrázek

Session aneb “kdo to tu mění”?

23. 12. 2009 11:30    kategorie: Tech Life    autor: RSl    komentářů: 0

Jednou z výhod programovacího jazyka se sdílenou hlavní pamětí je, že zápis programů je přímočarý - s objekty na heapu se dá pracovat stejně, jako s lokálními proměnnými, programy vypadají jednoduše, prostě krása. Ale co nevýhody?
 

Jednou z nevýhod programovacího jazyka se sdílenou hlavní pamětí je, že zápis programů je přímočarý - sdílené objekty na heapu vypadají na první pohled stejně jako lokální nesdílené objekty. Programátor, který si jezdí prstíkem po obrazovce a přeslabikuje si jednotlivé kroky programu, je naplněn štěstím, když program vypadá správně a při zkušebním spuštění i funguje. Předpoklad, že “co funguje v jednom threadu, bude stejně dobře fungovat ve více threadech”, zůstává nevysloven a patrně o něj ani myšlenka nezavadí. Což je trochu potíž, protože tento předpoklad není pravdivý.

Vezměme si třeba HttpSession. Moc užitečný objekt, řeknete si. Tam se toho vejde! Košíky, uživatelé, příznaky, přízraky, cache.. prostě něco jako lednička v kuchyňce - když bych mohl na něco později mít ještě chuť, šup tam s tím.

A je jich dost pro všechny - každý uživatel, co přijde na server, dostane jednu. Kolik? Jednu jedinou.

Když se nad tím zamyslíte, není to až tak moc. Například všechny jednotlivé požadavky jednoho uživatele dostanou tu samou session. V podstatě nic nebrání tomu, aby byly požadavky jednoho uživatele zpracovávány souběžně a pokud nějakým způsobem manipulují se session, aby tyto manipulace prováděly současně ve více threadech.

Například takový lazy-inicilazovaný / cachovaný objekt:

Na první pohled to vypadá dobře, ale při bližším zkoumání se začne vynořovat celá řada potenciálních problémů:

  • může se stát, že dva thready získají dvě různé instance MyData: zatímco první thread nenajde datový objekt v session a začne ho vytvářet/fetchovat/inicializovat, druhý ho stihne nenajít a vytvoří ho taky. Pokud ho navíc modifikují, každý udělá svoje dílčí změny a nakonec se v session objeví verze toho threadu, který náhodou dojde k ukládání do session později
  • i pokud více threadů získá korektně stejnou instanci MyData, je třeba si uvědomit, že případné modifikace objektu ovlivní i ostatní thready. Tj. pokud je objekt mutable, měl být thread-safe a veškerý přístup k němu (i čtení!) by měl být ošetřen. Pouhá synchronizace jednotlivých metod MyData nemusí být dostačující, pokud např. chceme pracovat s vnitřně konzistentním stavem, který se získá více než jedním voláním metody (i zdánlivě jednoduché atributy fromDate, toDate mohou být v rozporu, pokud se změna trefí mezi jejich čtení). Volat sort() na seznam uložený v session také není úplně chytré.
  • naopak spoléhat na to, že ona instance MyData bude sdílena mezi thready, se také nemusí vyplatit. Servletový kontejner se může v mezičase rozhodnout session zpersistovat a jiný request může pracovat s deserializovanou kopií - tj. všechny hodnoty budou stejné, ale nikoli identické instance. To se netýká ani tak souběžných requestů (kde by se to stát nemělo), ale spíše předávání instancí MyData mimo obsluhu requestu a jejich dlouhé pamatování. Mělo by platit, že data vytažená ze session platí jen po dobu requestu.

Další problém, který nevyplývá z uvedeného příkladu, ale je také poměrně závažný:

  • Session není transakční! Co tam jednou uložíte, zůstane tam, i když pak request, který to tam uložil, selže. Proto, pokud je to možné, měla by se modifikace session dělat až po operaci, která by mohla selhat. Jsou-li nejaké podmínky, které se během zpracování requestu kontrolují, měly by se zkontrolovat předtím, než se začne cokoli v session měnit.

Jak z toho ven?

  • identifikovat všechno, co se ukládá do session a mít o tom přehled,
  • zjistit, které hodnoty spolu souvisejí, zvážit, jestli by nebylo lepší je sloučit do jednoho objektu (pod jeden klíč),
  • pokud je něco “jenom” cachovaná hodnota, je většinou bez problému, pokud:
    • se zjišťuje výpočtem, který když se provede opakovaně, vyjde stejně,
    • po vypočtení a uložení do session se daná instance nikdy nemění,
    • není potřeba nějakým způsobem izolovat transakční změny (tj. nevadí pokud je vidět změna z dosud necommitnuté transakce),
  • pokud se hodnota v čase mění (uživatelský profil, košík…), je potřeba jasně definovat její životní cyklus, zejména:
    • jestli je možné předpokládat, že souběžné requesty nenastanou (to lze aplikovat např. u login/logout, ale pozor! pořád by mělo být zřejmé, že race-condition není na úkor bezpečnosti),
    • jestli je dovoleno dělat více změn souběžně - pak je potřeba dobře definovat, co z takových změn vyjde. ideálně by to mělo být totéž, jako kdyby se provedly po sobě,
    • pokud není dovoleno dělat více změn současně,
      • zamykat, aby na sebe souběžné požadavky na změnu počkaly,
      • definovat a kontrolovat počáteční podmínky pro provedení změny - může se stát, že po uvolnění zámku už nebudou původní podmínky platit.
Sdílet odkaz:
tisk

Diskuze k článku

K článku nebyl zatím přidán komentář.

Přidat příspěvek

 

Kontakt pro média


Máte zájem o další informace, odborný článek či přednášku na konferenci? Kontaktujte nás prosím na pr@etnetera.cz.

RSS - Tech life


RSS kanál Tech Life Blogu

Offlineblog

Offlineblog

Ljama


Komix z prostředí imaginární firmy.

ljama

Ještě jste ho nečetli? Tak tudy ...

 
Doporučujeme: Nabídka práce, volná pracovní místa - pracovní portál SPRÁVNÝKROK.CZ