<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>ITBlog: Memoize - cichy przyjaciel programisty</title>
    <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>grono.net</description>
    <item>
      <title>Memoize - cichy przyjaciel programisty</title>
      <description>&lt;p align="justify"&gt;Grono generuje wszystkie strony html ca&#322;kowicie dynamicznie. W przeciwie&#324;stwie do innych serwis&#243;w, nie mamy &#380;adnego odwrotnego proxy (reverse proxy). Nie zapami&#281;tujemy w cache wygenerowanych stron, nie zachodzi u nas taka potrzeba. To na czym si&#281; skupiamy, to cache'owanie&lt;sup&gt;1&lt;/sup&gt; surowych informacji pobieranych z bazy danych.&lt;/p&gt;


&lt;img src="http://b62.grono.net/176/184/gallery-46441157-500x500.jpg" alt="Architektura serwisu"&gt;&lt;/img&gt;
  
&lt;p align="justify"&gt;W naszej architekturze zapytanie klienta trafia poprzez &lt;i&gt;Loadbalancer&lt;/i&gt; do serwera, kt&#243;ry generuje stron&#281; html. Te serwery nazywamy &lt;i&gt;Backendami&lt;/i&gt;. Backendy pobieraj&#261; niezb&#281;dne informacje z bazy danych i zwracaj&#261; wynikow&#261; stron&#281; stworzon&#261; dla konkretnego u&#380;ytkownika. &lt;/p&gt;

&lt;p align="justify"&gt;W takiej architekturze baza danych musi obs&#322;u&#380;y&#263; ogromne ilo&#347;ci zapyta&#324;. Aby j&#261; odci&#261;&#380;y&#263; wyniki zapyta&#324; s&#261; zapisywane w cache. Je&#347;li w przysz&#322;o&#347;ci zajdzie potrzeba wykonania takiego samego zapytania, to nie trzeba b&#281;dzie ponownie pyta&#263; bazy. Wystarczy tylko pobra&#263; wynik z cache. Ta technika jest szeroko znana pod nazw&#261; &lt;a href="http://en.wikipedia.org/wiki/Memoization"&gt;Memoization&lt;/a&gt;.&lt;/p&gt;

Rozwa&#380;my najprostszy przyk&#322;ad:
&lt;pre style='color:#000000;background:#ffffff;'&gt;mem_dict &lt;span style='color:#808030; '&gt;=&lt;/span&gt; &lt;span style='color:#800080; '&gt;{&lt;/span&gt;&lt;span style='color:#800080; '&gt;}&lt;/span&gt;
&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; add&lt;span style='color:#808030; '&gt;(&lt;/span&gt;a&lt;span style='color:#808030; '&gt;,&lt;/span&gt; b&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    key &lt;span style='color:#808030; '&gt;=&lt;/span&gt; &lt;span style='color:#0000e6; '&gt;"%r %r"&lt;/span&gt; &lt;span style='color:#808030; '&gt;%&lt;/span&gt; &lt;span style='color:#808030; '&gt;(&lt;/span&gt;a&lt;span style='color:#808030; '&gt;,&lt;/span&gt;b&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;if&lt;/span&gt; key &lt;span style='color:#800000; font-weight:bold; '&gt;in&lt;/span&gt; mem_dict&lt;span style='color:#808030; '&gt;:&lt;/span&gt; &lt;span style='color:#696969; '&gt;# mamy zapami&#281;tane?&lt;/span&gt;
        &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; mem_dict&lt;span style='color:#808030; '&gt;[&lt;/span&gt;key&lt;span style='color:#808030; '&gt;]&lt;/span&gt;
 
    val &lt;span style='color:#808030; '&gt;=&lt;/span&gt; a &lt;span style='color:#808030; '&gt;+&lt;/span&gt; b &lt;span style='color:#696969; '&gt;# wylicz warto&#347;&#263;&lt;/span&gt;
    mem_dict&lt;span style='color:#808030; '&gt;[&lt;/span&gt;key&lt;span style='color:#808030; '&gt;]&lt;/span&gt; &lt;span style='color:#808030; '&gt;=&lt;/span&gt; val &lt;span style='color:#696969; '&gt;# zapami&#281;taj&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; val
&lt;/pre&gt;


&lt;p align="justify"&gt;W s&#322;owniku &lt;i&gt;mem_dict&lt;/i&gt; zapami&#281;tywane s&#261; wyniki funkcji. Je&#347;li funkcja b&#281;dzie wywo&#322;ana drugi raz z tymi samymi parametrami, to nie b&#281;dzie konieczne powt&#243;rne wykonywanie oblicze&#324;. Wynik zostanie pobrany ze s&#322;ownika.&lt;/p&gt;

&lt;p align="justify"&gt;W Gronie do przechowywania cache u&#380;ywamy wielu serwer&#243;w &lt;a href="http://www.danga.com/memcached/"&gt;Memcached&lt;/a&gt;. Nasze serwery Memcached s&#261; traktowane przez programist&#281; jako gigantyczny rozproszony s&#322;ownik, w kt&#243;rym dane s&#261; trzymane w pami&#281;ci wielu maszyn.&lt;/p&gt;

&lt;p align="justify"&gt;Stosowanie Memcached to do&#347;&#263; standardowa praktyka. U&#380;ywa go wiele serwis&#243;w, na przyk&#322;ad Facebook.&lt;/p&gt;

&lt;p align="justify"&gt;Jednak standardowy serwer Memcached udost&#281;pnia jedynie podstawowe funkcje, kt&#243;rych trudno jest u&#380;ywa&#263; w naszym &#347;rodowisku. Dlatego stworzyli&#347;my programistyczny interfejs do cache'owania o nazwie &lt;i&gt;Memoize&lt;/i&gt;, kt&#243;ry znacz&#261;co upraszcza u&#380;ywanie cache (jego g&#322;&#243;wnym autorem jest &lt;i&gt;Marek Pu&#322;czy&#324;ski&lt;/i&gt;).&lt;/p&gt;

&lt;p align="justify"&gt;Kolejnym przyk&#322;adem b&#281;dzie funkcja, kt&#243;ra pobiera pewne informacje z bazy danych, na przyk&#322;ad dane u&#380;ytkownika. Standardowo w Django wygl&#261;da&#322;oby to mniej wi&#281;cej tak:&lt;/p&gt;
&lt;pre style='color:#000000;background:#ffffff;'&gt;&lt;span style='color:#800000; font-weight:bold; '&gt;from&lt;/span&gt; models &lt;span style='color:#800000; font-weight:bold; '&gt;import&lt;/span&gt; User

&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; get_user&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    user &lt;span style='color:#808030; '&gt;=&lt;/span&gt; User&lt;span style='color:#808030; '&gt;.&lt;/span&gt;objects&lt;span style='color:#808030; '&gt;.&lt;/span&gt;get&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#e34adc; '&gt;id&lt;/span&gt;&lt;span style='color:#808030; '&gt;=&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; user
&lt;/pre&gt;


&lt;p align="justify"&gt;Spr&#243;bujmy u&#380;y&#263; dekoratora Memoize, aby wynik tej funkcji by&#322; pobierany z cache:&lt;/p&gt;
&lt;pre style='color:#000000;background:#ffffff;'&gt;&lt;span style='color:#800000; font-weight:bold; '&gt;from&lt;/span&gt; models &lt;span style='color:#800000; font-weight:bold; '&gt;import&lt;/span&gt; User
&lt;span style='color:#800000; font-weight:bold; '&gt;from&lt;/span&gt; grono2&lt;span style='color:#808030; '&gt;.&lt;/span&gt;contrib&lt;span style='color:#808030; '&gt;.&lt;/span&gt;memoize &lt;span style='color:#800000; font-weight:bold; '&gt;import&lt;/span&gt; memoize

&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; user_key&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; &lt;span style='color:#0000e6; '&gt;'model.user.%i'&lt;/span&gt; &lt;span style='color:#808030; '&gt;%&lt;/span&gt; &lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;

@memoize&lt;span style='color:#808030; '&gt;(&lt;/span&gt;user_key&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; get_user&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    user &lt;span style='color:#808030; '&gt;=&lt;/span&gt; User&lt;span style='color:#808030; '&gt;.&lt;/span&gt;objects&lt;span style='color:#808030; '&gt;.&lt;/span&gt;get&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#e34adc; '&gt;id&lt;/span&gt;&lt;span style='color:#808030; '&gt;=&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; user
&lt;/pre&gt;

&lt;p align="justify"&gt;Funkcja &lt;i&gt;user_key&lt;/i&gt; generuje klucz, pod kt&#243;rym zostanie umieszczony element w cache. Mo&#380;a by przypuszcz&#263;, &#380;e klucz do obiektu mo&#380;na wygenerowa&#263; automatycznie z parametr&#243;w funkcji. Jednak funkcja generuj&#261;ca klucz zosta&#322;a wyodr&#281;bniona, gdy&#380; mo&#380;e zaistnie&#263; przypadek, &#380;e niekt&#243;re parametry cache&#8217;owanej funkcji nie s&#261; jej kluczami g&#322;&#243;wnymi. &lt;/p&gt;

&lt;p align="justify"&gt;Na przyk&#322;ad, poni&#380;szy parametr &lt;i&gt;print_debug&lt;/i&gt; nie wp&#322;ywa w &#380;aden spos&#243;b na stan cache'owanego obiektu, dlatego nie powinien by&#263; brany pod uwag&#281; podczas generowania klucza:&lt;p&gt;
&lt;pre style='color:#000000;background:#ffffff;'&gt;@memoize&lt;span style='color:#808030; '&gt;(&lt;/span&gt;user_key2&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; get_user_with_warning&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;,&lt;/span&gt; print_debug&lt;span style='color:#808030; '&gt;=&lt;/span&gt;&lt;span style='color:#e34adc; '&gt;True&lt;/span&gt;&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    user &lt;span style='color:#808030; '&gt;=&lt;/span&gt; User&lt;span style='color:#808030; '&gt;.&lt;/span&gt;objects&lt;span style='color:#808030; '&gt;.&lt;/span&gt;get&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#e34adc; '&gt;id&lt;/span&gt;&lt;span style='color:#808030; '&gt;=&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;if&lt;/span&gt; user&lt;span style='color:#808030; '&gt;.&lt;/span&gt;name &lt;span style='color:#808030; '&gt;=&lt;/span&gt;&lt;span style='color:#808030; '&gt;=&lt;/span&gt; &lt;span style='color:#0000e6; '&gt;"Jozin"&lt;/span&gt; &lt;span style='color:#800000; font-weight:bold; '&gt;and&lt;/span&gt; print_debug&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
        &lt;span style='color:#800000; font-weight:bold; '&gt;print&lt;/span&gt; &lt;span style='color:#0000e6; '&gt;"Jozin pobrany z bazy."&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; user
&lt;/pre&gt;

&lt;p align="justify"&gt;Jednak co si&#281; stanie, gdy zmienimy co&#347; na obiekcie &lt;i&gt;User&lt;/i&gt;? W cache b&#281;dzie przecie&#380; stara warto&#347;&#263;, wi&#281;c kolejne wywo&#322;ania funkcji b&#281;d&#261; zwraca&#263; niew&#322;a&#347;ciwe stare dane. Dlatego programista musi explicite usun&#261;&#263; (uniewa&#380;ni&#263;, zinwalidowa&#263;) star&#261; warto&#347;&#263; z cache. &lt;/p&gt;

Realizowane jest to nast&#281;puj&#261;co:
&lt;pre style='color:#000000;background:#ffffff;'&gt;&lt;span style='color:#800000; font-weight:bold; '&gt;from&lt;/span&gt; grono2&lt;span style='color:#808030; '&gt;.&lt;/span&gt;contrib&lt;span style='color:#808030; '&gt;.&lt;/span&gt;memoize &lt;span style='color:#800000; font-weight:bold; '&gt;import&lt;/span&gt; invalidate

&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; change_user_name&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;,&lt;/span&gt; new_name&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    user &lt;span style='color:#808030; '&gt;=&lt;/span&gt; get_user&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    user&lt;span style='color:#808030; '&gt;.&lt;/span&gt;name &lt;span style='color:#808030; '&gt;=&lt;/span&gt; new_name
    user&lt;span style='color:#808030; '&gt;.&lt;/span&gt;save&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    invalidate&lt;span style='color:#808030; '&gt;(&lt;/span&gt;user_key&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;)&lt;/span&gt; &lt;span style='color:#696969; '&gt;# wyczy&#347;&#263; stary cache&lt;/span&gt;
&lt;/pre&gt;

&lt;p align="justify"&gt;Zajmijmy si&#281; trudniejszym przyk&#322;adem, gdzie dwa obiekty po&#322;&#261;czone s&#261; relacj&#261;. Przy zmianie jednego mo&#380;e zaistnie&#263; potrzeba zinwalidowania drugiego. Nasza implementacja &lt;i&gt;Memoize&lt;/i&gt; pozwala na to. &lt;/p&gt;

W tym przyk&#322;adzie niech model &lt;i&gt;Profile&lt;/i&gt; b&#281;dzie powi&#261;zany relacj&#261; z modelem &lt;i&gt;User&lt;/i&gt;:
&lt;pre style='color:#000000;background:#ffffff;'&gt;&lt;span style='color:#800000; font-weight:bold; '&gt;from&lt;/span&gt; models &lt;span style='color:#800000; font-weight:bold; '&gt;import&lt;/span&gt; User&lt;span style='color:#808030; '&gt;,&lt;/span&gt; Profile 
&lt;span style='color:#800000; font-weight:bold; '&gt;from&lt;/span&gt; grono2&lt;span style='color:#808030; '&gt;.&lt;/span&gt;contrib&lt;span style='color:#808030; '&gt;.&lt;/span&gt;memoize &lt;span style='color:#800000; font-weight:bold; '&gt;import&lt;/span&gt; memoize

&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; user_key&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; &lt;span style='color:#0000e6; '&gt;'model.user.%i'&lt;/span&gt; &lt;span style='color:#808030; '&gt;%&lt;/span&gt; &lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;

@memoize&lt;span style='color:#808030; '&gt;(&lt;/span&gt;user_key&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; get_user&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    user &lt;span style='color:#808030; '&gt;=&lt;/span&gt; User&lt;span style='color:#808030; '&gt;.&lt;/span&gt;objects&lt;span style='color:#808030; '&gt;.&lt;/span&gt;get&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#e34adc; '&gt;id&lt;/span&gt;&lt;span style='color:#808030; '&gt;=&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; user


&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; profile_key&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; &lt;span style='color:#0000e6; '&gt;'model.profile.%i'&lt;/span&gt; &lt;span style='color:#808030; '&gt;%&lt;/span&gt; &lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;

@memoize&lt;span style='color:#808030; '&gt;(&lt;/span&gt;profile_key&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; get_profile&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    p &lt;span style='color:#808030; '&gt;=&lt;/span&gt; Profile&lt;span style='color:#808030; '&gt;.&lt;/span&gt;objects&lt;span style='color:#808030; '&gt;.&lt;/span&gt;get&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#e34adc; '&gt;id&lt;/span&gt;&lt;span style='color:#808030; '&gt;=&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    p&lt;span style='color:#808030; '&gt;.&lt;/span&gt;user &lt;span style='color:#808030; '&gt;=&lt;/span&gt; get_user&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#e34adc; '&gt;id&lt;/span&gt;&lt;span style='color:#808030; '&gt;=&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; p
&lt;/pre&gt;

&lt;p align="justify"&gt;Jak wida&#263; zmiana w &lt;i&gt;User&lt;/i&gt; musi spowodowa&#263; uniewa&#380;nienie cache obiektu &lt;i&gt;Profile&lt;/i&gt;. Aby to osi&#261;gn&#261;&#263; definiujemy klucz wi&#261;&#380;&#261;cy, u nas nazywany kluczem wersji. Nast&#281;pnie, aby usun&#261;&#263; wszystkie powi&#261;zane rekordy wystarczy jedynie zinwalidowa&#263; ten klucz. &lt;/p&gt;
&lt;pre style='color:#000000;background:#ffffff;'&gt;&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; super_key&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
  &lt;span style='color:#800000; font-weight:bold; '&gt;return&lt;/span&gt; &lt;span style='color:#0000e6; '&gt;"super_key.%d"&lt;/span&gt; &lt;span style='color:#808030; '&gt;%&lt;/span&gt; uid

@memoize&lt;span style='color:#808030; '&gt;(&lt;/span&gt;user_key&lt;span style='color:#808030; '&gt;,&lt;/span&gt; super_key&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; get_user&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
&lt;span style='color:#808030; '&gt;.&lt;/span&gt;&lt;span style='color:#808030; '&gt;.&lt;/span&gt;&lt;span style='color:#808030; '&gt;.&lt;/span&gt;

@memoize&lt;span style='color:#808030; '&gt;(&lt;/span&gt;profile_key&lt;span style='color:#808030; '&gt;,&lt;/span&gt; super_key&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; get_profile&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
&lt;span style='color:#808030; '&gt;.&lt;/span&gt;&lt;span style='color:#808030; '&gt;.&lt;/span&gt;&lt;span style='color:#808030; '&gt;.&lt;/span&gt;


&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; change_user_name&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;,&lt;/span&gt; new_name&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;:&lt;/span&gt;
    user &lt;span style='color:#808030; '&gt;=&lt;/span&gt; get_user&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    user&lt;span style='color:#808030; '&gt;.&lt;/span&gt;name &lt;span style='color:#808030; '&gt;=&lt;/span&gt; new_name
    user&lt;span style='color:#808030; '&gt;.&lt;/span&gt;save&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
    invalidatev&lt;span style='color:#808030; '&gt;(&lt;/span&gt;super_key&lt;span style='color:#808030; '&gt;(&lt;/span&gt;uid&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;)&lt;/span&gt; &lt;span style='color:#696969; '&gt;# usu&#324; powi&#261;zane rekordy z cache&lt;/span&gt;
&lt;/pre&gt;


&lt;p align="justify"&gt;Wygl&#261;da to prosto i intuicyjnie. Jednak g&#322;&#243;wna trudno&#347;&#263; polega na tym, &#380;e dzia&#322;amy w &#347;rodowisku rozproszonym. Jest mo&#380;liwa sytuacja, gdy jeden Backend zmodyfikuje rekord i go usunie z cache, a inny Backend pobierze chwil&#281; wcze&#347;niej star&#261; warto&#347;&#263; i doda j&#261; do cache. Efekt tego b&#281;dzie taki, &#380;e w cache b&#281;dzie przechowywana niepoprawna warto&#347;&#263;. Na szcz&#281;&#347;cie, dzi&#281;ki przemy&#347;lanej implementacji Memoize mamy gwarancj&#281;, &#380;e taka sytuacja nie wyst&#261;pi.&lt;/p&gt;

&lt;p align="justify"&gt;Opisana architektura bardzo dobrze sprawdza si&#281; w praktyce. Serwery Memcached bardzo &#322;atwo si&#281; skaluj&#261;, gdy brakuje nam pami&#281;ci na cache, to po prostu dostawiamy kolejn&#261; maszyn&#281;.&lt;/p&gt;

&lt;p align="justify"&gt;Ciekawskich mo&#380;e zainteresowa&#263; to, &#380;e raz na jaki&#347; czas musimy zrestartowa&#263; serwery Memcached. Zanim cache ponownie zostanie wype&#322;niony danymi mija co najmniej doba.&lt;/p&gt;

&lt;p align="justify"&gt;Z punktu widzenia programisty dekorator Memoize mo&#380;na bardzo prosto stosowa&#263; w praktyce. Nie jest to &#380;adna prze&#322;omowa technologia, lecz &#322;atwo&#347;&#263; w nauce i zastosowaniu powoduje &#380;e nawet m&#322;odsi informatycy mog&#261; pisa&#263; kod, kt&#243;ry b&#281;dzie dzia&#322;a&#322; dla dziesi&#261;tk&#243;w tysi&#281;cy u&#380;ytkownik&#243;w.&lt;/p&gt;

&lt;p align="justify"&gt;Dzi&#281;ki cache'owaniu prawie 99% wszystkich zapyta&#324; nie musi trafi&#263; do bazy. Jednak ca&#322;y czas staramy si&#281; znale&#378;&#263; miejsca, kt&#243;re nie s&#261; poprawnie cache&#8217;owane, aby m&#243;c jeszcze bardziej przyspieszy&#263; serwis i odci&#261;&#380;y&#263; baz&#281; danych.&lt;/p&gt;



&lt;br/&gt;&lt;br/&gt;
&lt;small&gt;
&lt;p align="justify"&gt;&lt;sup&gt;1&lt;/sup&gt;Przepraszam puryst&#243;w j&#281;zykowych za polonizowanie wyrazu cache. Jak to cz&#281;sto w informatyce bywa, nie istnieje jeszcze dobry odpowiednik tego wyrazu w j&#281;zyku polskim. &lt;a href="http://pl.wikipedia.org/wiki/Cache"&gt;Artyku&#322; Wikipedii&lt;/a&gt; sugeruje odmian&#281; z apostrofem i postaram si&#281; tego trzyma&#263;.&lt;/p&gt;

&lt;/small&gt;
</description>
      <pubDate>Wed, 05 Mar 2008 13:00:00 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:d4c494cd-0f82-46c2-99a4-d0b3ca57f3fa</guid>
      <author>Marek Majkowski</author>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty</link>
      <category>python</category>
      <category>cache</category>
      <trackback:ping>http://itblog.grono.net/articles/trackback/56</trackback:ping>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by Marek Majkowski</title>
      <description>&lt;p&gt;Dla wi&#281;kszo&#347;ci danych tak &amp;#8211; jedna baza. Jednak naprawd&#281; du&#380;e tabele s&#261; przechowywane na dedykowanych maszynach, cz&#281;sto tabele s&#261; r&#281;cznie partycjonowane pomi&#281;dzy maszynami. Depesz opisywa&#322; jedn&#261; z migracji takich tabel na Postgresa: &lt;a href="http://itblog.grono.net/articles/2007/12/10/baza1" rel="nofollow"&gt;http://itblog.grono.net/articles/2007/12/10/baza1&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sat, 03 May 2008 20:37:58 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:e172a8d5-5c6c-492b-aa09-ffd3d1c103a4</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-2653</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by grassoalvaro@yahoo.com</title>
      <description>&lt;p&gt;Korzystacie z jednej bazy danych? Czy te&#380; macie jakie&#347; swoje rozwi&#261;zanie tej kwestii (gdy&#380; Django ORM nie wspiera wielu baz, niestety)?&lt;/p&gt;</description>
      <pubDate>Fri, 02 May 2008 18:12:14 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:be4bd87c-0ca5-40f7-9094-216c9d364dae</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-2609</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by maxim</title>
      <description>&lt;p&gt;a skad wiesz, ze mam morde obrosnieta syfami? sprawdzales?&lt;/p&gt;</description>
      <pubDate>Tue, 15 Apr 2008 08:13:46 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:6461defa-50eb-4159-9f30-7126f5d3d35b</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-2195</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by tn</title>
      <description>&lt;p&gt;no to jest nas dwoch, tyle ze ja nie jestem pryszczaty:P&lt;/p&gt;</description>
      <pubDate>Mon, 14 Apr 2008 20:45:39 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:f7356848-7a2e-4ec8-81d1-8f1a7b8496e0</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-2182</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by maxim</title>
      <description>&lt;p&gt;kolejny pseudoinformatyk do kolekcji&lt;/p&gt;</description>
      <pubDate>Mon, 14 Apr 2008 10:57:26 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:1552922d-2da6-4552-bf12-78e048d78c22</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-2166</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by tn</title>
      <description>&lt;p&gt;maxim:
no nie musi, ale tematem postu jest memcached, a nie obrona przed (D)DOS.&lt;/p&gt;


	&lt;p&gt;co do reszy to z tego co pamietam to PIX i ASA to chyba nie na ten kaliber jesli chodzi o przepustowosc&amp;#8230;:) jesli juz to modul FWSM do jakiegos 6500/7200.&lt;/p&gt;


	&lt;p&gt;co to tego ataku via hping3 to mozesz odpalic against OpenBSD :) efekt mizerny, wiec doswiadczenie mam troche inne, a warunki wejsciowe byly podobne:)&lt;/p&gt;</description>
      <pubDate>Mon, 14 Apr 2008 01:43:05 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:1c59af5a-3369-4f24-a4f1-2ba975572e18</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-2153</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by Filip Butkiewicz Software</title>
      <description>&lt;p&gt;Interesuj&#261;ce, spr&#243;buj&#281; wykorzysta&#263; w swoich serwisach. Pozdrawiam.&lt;/p&gt;</description>
      <pubDate>Tue, 01 Apr 2008 15:59:01 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:b0ad35f5-cdfc-4e46-a157-a0ca93ab12cb</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1761</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by maxim</title>
      <description>&lt;p&gt;coby byc pelnoslownym:&lt;/p&gt;


	&lt;p&gt;hping3&amp;#8212;flood&amp;#8212;rand-source&amp;#8212;syn atakowany_host&lt;/p&gt;


	&lt;p&gt;przetestujta sobie w siakims lan&amp;#8217;ie&lt;/p&gt;


	&lt;p&gt;publikuje tylko w celach edukacyjnych&lt;/p&gt;


	&lt;p&gt;jak chcesz uzyc tego do robienia dymowicha, to palnij sie lepiej w czache tudziez odwiedz klinike psychiatryczna&lt;/p&gt;


	&lt;p&gt;max&lt;/p&gt;</description>
      <pubDate>Tue, 25 Mar 2008 16:33:41 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:06907abc-1084-497a-8c23-b5f476fbdc76</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1681</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by maxim</title>
      <description>&lt;p&gt;tn &amp;#8211; hping3 nie musi obslugiwac protokolu http aby pozamiatac maly serwer http czy http load balancera a wlasciwie dowolna aplikacje serwerowa oparta na tcp&lt;/p&gt;


	&lt;p&gt;pod warunkiem oczywiscie, ze stojacy na wejsciu sprzet jest niewlasciwie skonfigurowany&lt;/p&gt;


	&lt;p&gt;sami borykalismy sie z problemem w sumie dosc skromnego ddos&amp;#8217;a (ok. 300 Mb/s) ktory skutecznie utrudnial zycie paru naszym klientom.&lt;/p&gt;


	&lt;p&gt;nalozenie dosc sensownych limitow jest zawsze problemem tak aby nie utrudnialy zycia klientom a jednoczesnie dawaly sensowny efekt.&lt;/p&gt;


	&lt;p&gt;z pewnoscia grono stosuje ciscowe gsr&amp;#8217;y &amp;#8211; soft z opcja security czy anomaly detectory chociaz watpie aby siegali po te ostatnie z racji dosc podrasowanej ceny (cirka &amp;gt; 40 k$). ewentualnie starsze PIX&amp;#8217;y czy nowe ASA.&lt;/p&gt;


	&lt;p&gt;najprostsza opcja &amp;#8211; postawic linucha 2.6 jako most na sensownym sprzecie i do tego iptables + ew. pare skrypcikow. skutecznosc zalezy od ustawien i rodzaju ataku.&lt;/p&gt;


	&lt;p&gt;przy okolo 300 regolach iptables na sprzecie z prockiem 3GHz + 1 GB ram&amp;#8217;u + ruch na poziomie 400-500 Mbit/s obciazenie sprzetu w granicach 15-18%.&lt;/p&gt;</description>
      <pubDate>Mon, 24 Mar 2008 20:06:56 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:f9e289e2-cff3-4c57-acfa-f03b37f821ea</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1674</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by Marek Majkowski</title>
      <description>&lt;p&gt;&lt;b&gt;tn:&lt;/b&gt; &lt;i&gt;ile % trafien jest w memcached?&lt;/i&gt;&lt;/p&gt;


	&lt;p&gt;Tak jak napisa&#322;em, prawie 99%. Chyba nie ma wi&#281;kszego znaczenia czy to b&#281;dzie 89.8% czy 99.6%. Jak wida&#263; praktycznie wszystkie zapytania trafiaj&#261; w memcached.&lt;/p&gt;</description>
      <pubDate>Sat, 22 Mar 2008 12:42:39 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:08ec7665-9b59-412f-91c6-35aafe2230b9</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1625</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by tn</title>
      <description>&lt;p&gt;maxim: hping3??
a czy on potrafi generowac zapytania HTTP? chyba nie :):P
wiec to chyba nie ta warstwa,ktora jest opisana w tym artykule:)&lt;/p&gt;


	&lt;p&gt;moim zdaniem takie cos latwo mozna zablokowac na samych balancerach (nakladajacs limity na polaczenie z danego adresu ip)
lub wczesniej na  routerach &amp;#8230;itd.&lt;/p&gt;


	&lt;p&gt;wiec pewnie to co wg Ciebie robi &amp;#8220;farma reverse proxy&amp;#8221;,
jest zalatwiane moim zdaniem wlasnie przez przez balancer.&lt;/p&gt;


	&lt;p&gt;mnie tylko ciekawi, jesli to nie tajemnica, ile % trafien jest w memcached?&lt;/p&gt;


	&lt;p&gt;pozdrowienia,
Tomek&lt;/p&gt;</description>
      <pubDate>Fri, 21 Mar 2008 17:55:38 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:1d6b7f01-a2a5-4bc5-953b-da0149ce03ea</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1621</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by maxim</title>
      <description>&lt;p&gt;fajnie, ze dzielicie sie pewnymi informacjami z userami. w przeciwienstwie do allegro ekipa grona jest stosunkowo tworcza &amp;#8211; oby tak dalej.&lt;/p&gt;


	&lt;p&gt;z wlasnego prywatnego doswiadczenia wiem, ze odpowiednio zaprojektowana farma reverse proxy moze uchronic przed skutkami przykrych atakow ddos.&lt;/p&gt;


	&lt;p&gt;co ekipa grona ma do powiedzenia w tej kwestii?&lt;/p&gt;


	&lt;p&gt;nie mowie tu o fakcie, ze takowy atak mial miejsce czy nie namawiam do przyznania sie bo nie taki cel tego.&lt;/p&gt;


	&lt;p&gt;probowaliscie zapuszczac odpowiednie narzedzia (hping3)  np. po LAN&amp;#8217;ie i sprawdzic kiedy nastapi ugiecie systemu?&lt;/p&gt;</description>
      <pubDate>Sun, 09 Mar 2008 00:31:49 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:79e25663-e653-4a10-a490-2707fb74ee21</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1528</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by Marek Majkowski</title>
      <description>&lt;p&gt;&lt;i&gt;Artur Kulig: Czy ma to co&#347; wsp&#243;lnego z cache&#8217;owaniem w gronie?&lt;/i&gt;&lt;/p&gt;


	&lt;p&gt;Nie. Chodzi o obci&#261;&#380;enie bazy. Opisana architektura jest bardzo dobrze skalowalna je&#347;li chodzi o odczyty z bazy danych, jednak w &#380;aden spos&#243;b nie wp&#322;ywa na zapisy do bazy. Edycja postu to w&#322;a&#347;nie zapis. Czasem baza jest zbyt mocno obci&#261;&#380;ona i wtedy naj&#322;atwiejsz&#261; rzecz&#261; jak&#261; mo&#380;na zrobi&#263; &#380;eby zmniejszy&#263; zapisy, to w&#322;a&#347;nie wy&#322;&#261;czenie edycji i usuwania post&#243;w.&lt;/p&gt;</description>
      <pubDate>Fri, 07 Mar 2008 14:34:55 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:6ca90371-9646-41d2-9a56-4021857d2ed3</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1517</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by bizia</title>
      <description>&lt;p&gt;a co ma piernik do wiatraka?&lt;/p&gt;</description>
      <pubDate>Thu, 06 Mar 2008 18:06:49 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:35adb3c0-aca4-47bc-8c78-e38af77c483f</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1491</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by Artur Kulig</title>
      <description>&lt;p&gt;Tak nieco obok tematu zawsze mnie zastanawia&#322;o czemu na gronie nie ma opcji edytowania posta. Ostatnimi czasy zacz&#261;&#322; si&#281; pojawia&#263;. Z tre&#347;ci posta mo&#380;e to wynika, ale ja wol&#281; zapyta&#263; wprost:
Czy ma to co&#347; wsp&#243;lnego z cache&amp;#8217;owaniem w gronie?&lt;/p&gt;</description>
      <pubDate>Thu, 06 Mar 2008 13:57:11 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:ace3d7b4-ce3e-4820-998e-d1c379f2880c</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1488</link>
    </item>
    <item>
      <title>"Memoize - cichy przyjaciel programisty" by matrut</title>
      <description>&lt;p&gt;Na podobnej zasadzie dziala rozwiazanie systemu transakcyjnego w ubs ag i swissquote&lt;/p&gt;


	&lt;p&gt;Przez lata wypracowalismy wlasne middleware  podobne w duzej mierze do JBoss&amp;#8217;a zwane JMaster&amp;#8217;em zoptymalizowane w duzej mierze do uzycia w srodowisku mainframe.&lt;/p&gt;</description>
      <pubDate>Thu, 06 Mar 2008 09:39:49 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:c1c8041a-b15d-4a06-84e5-1c6d2c85a6c9</guid>
      <link>http://itblog.grono.net/articles/2008/03/05/memoize-cichy-przyjaciel-programisty#comment-1484</link>
    </item>
  </channel>
</rss>
