Plugin: timed_fragment_cache
Cache i kwestie związane z jego utrzymaniem i ekspiracją nie są zapewne obce żadnemu programiście Rails mającemu na swoim koncie jakąkolwiek aplikację “produkcyjną”. Jak to wyczytałem gdzieś w sieci: “W programowaniu są tylko dwa poważne problemy: nazywanie zmiennych i ekspiracja cache” (może trochę przekręcam cytat, ale sens był na pewno właśnie taki). Na szczęście istnieje plugin timed_fragment_cache, który znacznie ułatwia to trudne zagadnienie.
Plugina instalujemy wydając polecenie:
script/plugin install http://svn.livsey.org/plugins/timed_fragment_cache
Co nam on daje? Przede wszystkim pozwala na określenie czasu, po jakim dany fragment automatycznie się wyekspiruje. Dzięki temu nie musimy się martwić, czy ręcznie wyekspirowaliśmy fragment we wszystkich miejscach w kodzie, w których jego zawartość może się zmienić. Czasową ekspirację robi się bardzo prosto:
<% cache 'costam', 1.hour.from_now do %>
...
<% end %>Jak łatwo się domyślić, fragment ‘costam’ zostanie wyekspirowany po godzinie.
Wygodne? Wygodne. Proste? Proste. Ale jak uważny czytelnik zapewne zauważył, ten prosty blok kodu nie rozwiązuje bynajmniej całkowicie problemu z cache’owaniem fragmentów. Załóżmy, że uzupełniamy wykropkowany fragment:
<% cache 'costam', 1.hour.from_now do %>
<% @posts.each do |post| %>
<%= @post.title %><br/>
<% end %>
<% end %>a w kontrolerze inicjujemy @posts następująco:
@posts = Post.find( :all )Przykład może niezbyt wydumany, ale za to życiowy :) Problem polega oczywiście na tym, że nawet wtedy, kiedy fragment jest w dalszym ciągu w cache, zmienna @posts jest niepotrzebnie inicjowana (=> jest przeprowadzane potencjalnie kosztowne zapytanie bazodanowe). Na szczęście omawiany plugin w tej sytuacji przychodzi nam z pomocą - inkryminowaną linię kodu w kontrolerze zastępujemy przez:
when_fragment_expired 'costam', 1.hour.from_now do
@posts = Post.find( :all )
endDzięki temu zmienna @posts będzie inicjowana tylko wtedy, kiedy fragment zostanie wyekspirowany i trzeba go ponownie wyrenderować.
Ważne: jeśli używamy when_fragment_expired, to w wywołaniu cache w widoku nie podajemy czasu ekspiracji. Jeśli nie będziemy o tym pamiętać, to narazimy się na trudno wykrywalne błędy związane z minimalnymi różnicami czasowymi między wykonaniem akcji kontrolera a renderowaniem widoku.
Na koniec bezczelnie się pochwalę - autor plugina dodał mojego patcha, który pozwala na bezproblemowe działanie plugina przy wielu serwerach Mongrel (patrz moje wcześniejsze artykuły nt. współbieżności). To chyba wyjaśnia, dlaczego ten plugin omawiam jako pierwszy :)