Pamięć podręczna cache

Co to jest pamięć podręczna (cache) i jak z nią pracować w Drupalu?

Słowo cache pochodzi z języka francuskiego, a jego znaczenia to ukryty lub ukryta skrytka. Termin ten był pierwotnie używany w kontekście wojskowym i oznaczał ukrywanie zapasów za linią frontu w celu zabezpieczenia ich przed wrogami. Co natomiast oznacza cache w przypadku programowania i jak sobie z nim radzić w Drupalu? Na te pytania odpowiemy w naszym artykule. 

Cache - co to jest?

W informatyce pojęcie cache jest używane do określenia pamięci podręcznej. Służy ona do tymczasowego przechowywania danych, które są często wykorzystywane. Przez co nie muszą być za każdym razem przeliczane na nowo. W przypadku aplikacji webowych cache pozwala na szybsze wczytywanie często odwiedzanych witryn lub zasobów, co pozytywnie wpływa na czas ładowania strony internetowej

To tyle, jeżeli chodzi o teorie i ciekawostki. W następnej części skupimy się na pracy z cache w Drupalu 9 i 10. Jest to dość skomplikowane narzędzie, które często sprawia dużo problemów niedoświadczonym programistom, przez co idą na skróty, a w efekcie wyłączają cache całkowicie, np. na danej stronie lub w bloku, co zwykle jest całkiem niepotrzebne.

Cache w Drupalu - ogólne informacje

Standardowa instalacja Drupala używa cachowania w bazie danych, np w MySQL. 
Pamięć podręczna w Drupalu jest podzielona na tak zwane biny. Każdy bin posiada osobną tabelę w bazie. Często jeżeli dokładamy nowe contribowe moduły, dodają one swoje biny. 

Widzimy tu kilka tabel z cache z rdzenia Drupala - cache_menu lub cache_entity oraz cache biny, które zostały dodane przez dodatkowe moduły, np. Ulimate Cron.

cache w drupalu


Podczas pracy systemu CMS niektóre biny generują bardzo dużo wpisów, dlatego dzielimy je na mniejsze, aby łatwiej było je czyścić oraz pracować z nimi.

Jak pracować z pamięcią podręczną w Drupalu?

W Drupalu 9 cache jest domyślnie włączone, więc nie musimy go uruchamiać ręcznie. Jednakże w przypadku gdy cache przeszkadza nam podczas developmentu i ręcznie musimy go czyścić co parę chwil, możemy ograniczyć jego działanie. Całkowite wyłączenie cache w Drupalu jest praktycznie niemożliwe, ponieważ jest ono integralną częścią działania systemu. Niektóre elementy, takie jak cache bazy danych, cache renderowania czy cache menu, są wymagane przez Drupala do poprawnego i szybkiego działania, więc nie można ich całkowicie wyłączyć.

Natomiast możemy: 

  • wyłączyć cache’owanie plików Twig, poprzez zmianę lub nadpisanie parametrów w pliku service.yml, 
twig.config:
  cache: false
  • wyłączyć konkretne biny, nadpisując w pliku settings.php, biny services cache.backend.null.

Przykład 

$settings['cache']['bins']['render'] = 'cache.backend.null';
​​​​​​​$settings['cache']['bins']['discovery_migration'] = 'cache.backend.memory';
$settings['cache']['bins']['page'] = 'cache.backend.null';
$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';

Wcześniej jednak musimy do service.php dodać service cache.backend.null.

services:

cache.backend.null:
   class: Drupal\Core\Cache\NullBackendFactory

Jak włączyć debugowanie cache w Drupal 10?

W najnowszej wersji Drupala istnieje możliwość debugowania pamięci podręcznej w Twigu. Włączamy tę opcję, dodając debug w pliku service.yml.

twig.config:

  debug: true

  auto_reload: true

  cache: false

Otrzymujemy wtedy output w HTML-u z komentarzami informującymi o tym, jakie cache tagi zostały użyte, jaki jest cache context oraz cache keys. Jest to bardzo przydatne przy debugowaniu. O cache tagach i context opowiemy w dalszej części tekstu. 

<li><!-- START RENDERER -->

<!-- CACHE-HIT: No -->

<!-- CACHE TAGS:

   * node_view

   * node:1

   * comment_view

   * comment:44

   * config:core.entity_view_display.comment.intranet_content_comment.full

   * comment:29

   * comment:43

   * config:filter.format.filter_html

   * taxonomy_term:69

   * user:20

-->

<!-- CACHE CONTEXTS:

   * route.name.is_layout_builder_ui

   * languages:language_interface

   * theme

   * user.permissions

   * user.roles

   * url.site

   * timezone

-->

<!-- CACHE KEYS:

   * entity_view

   * node

   * 44

   * teaser

   * en

-->

<!-- CACHE MAX-AGE: -1 -->

<!-- PRE-BUBBLING CACHE TAGS:

   * node_view

   * node:1

-->

<!-- PRE-BUBBLING CACHE CONTEXTS:

   * route.name.is_layout_builder_ui

   * languages:language_interface

   * theme

   * user.permissions

-->

<!-- PRE-BUBBLING CACHE KEYS:

   * entity_view

   * node

   * 1

   * teaser

   * en

-->

<!-- PRE-BUBBLING CACHE MAX-AGE: -1 -->

<!-- RENDERING TIME: 0.093894196 -->

Jak wyczyścić cache w Drupalu 10? 

Cache można wyczyścić na kilka sposobów. Najłatwiejszym jest kliknięcie w menu. Polecamy moduł Admin Toolbar Extra Tools, który dostarcza nam przydatny skrót. Możemy wyczyścić wszystko lub poszczególne sekcje.

Czyszczenie cache w Drupalu

Drugi sposób, który również warto znać to skorzystanie z Drusha. Jeżeli podczas developmentu robimy zmiany w serwisach czy plikach Twig, możemy otrzymać błąd 500, przez co strona będzie zablokowana i nie będziemy mieć dostępu do UI. Wtedy możemy użyć Drusha i polecenia Cache rebuild

drush  cache:rebuild  lub w skrócie drush cr.

Ostatecznie możemy po prostu wyczyścić tabele w bazie z prefixem cache_.

Co to jest cache context w Drupalu?

W Drupalu 9 i 10 istnieje wiele kontekstów cache, które pozwalają na przechowywanie w pamięci podręcznej różnych wersji elementów w zależności od określonych warunków. 

Przykład

Mamy dwujęzyczną stronę, do której dodaliśmy niestandardowy blok. Po zalogowaniu na stronę wyświetla on tekst:

  • (PL) Paweł, witamy na naszej stronie. Właśnie oglądasz stronę pod adresem /node/1
  • (EN) Pawel, welcome on our website. You are currently visiting the page on url /node/1.

Jeżeli po włączeniu cache zauważymy, że nasz blok wyświetla tekst źle, na pewno problem leży w nieprawidłowo ustawionym kontekście. W domyślnym pliku service.yml, Drupal posiada standardowo wymuszone konteksty. Znajdziemy je w renderer.config: required_cache_contexts.

Required_cache_contexts to atrybut określający kontekst pamięci podręcznych, które muszą być uwzględnione przy zapisywaniu do cache. Standardowo są to 

languages:language_interface, theme oraz user.permissions. 

Widzimy tutaj trzy zmienne dynamiczne. To jest właśnie nasz kontekst.

  1. Język. W zależności od języka zmienia się tekst na stronie. Standardowy service.yml posiada language interface, więc automatycznie po zmianie języka interfejsu powinniśmy zobaczyć tekst w innym języku.
  2. Imię użytkownika. Aby uniknąć sytuacji, kiedy po zalogowaniu na innego użytkownika zobaczymy inne imię niż swoje, musimy dodać nowy kontekst. Standardowy user.permission jest zbyt szczegółowy. Możemy dodać całego użytkownika. Wtedy każdy użytkownik będzie miał swoje cache. 
  3. Adres aktualnej strony. Tutaj możemy użyć np. context url.path.

Finalnie po dodaniu tych trzech kontekstów, cache zostanie w innej wersji dla każdego języka, użytkownika i adresu URL.

Jak dodać/zmienić context cache?

Aby nie robić tego globalnie dla strony w service.yml., informacje na temat cache w tym konteksty i tagi przekazujemy w render array.

Należy dodać do render array w #cache klucz 'contexts'. 

'#cache' => ['contexts' => ['url.path', 'user', ‘languages:language_interface]]

Natomiast encje mają często dedykowane metody do tego. Na przykład, aby dodać cache context w bloku, możemy nadpisać metodę getCacheContexts().

public function getCacheContexts() { return Cache::mergeContexts(parent::getCacheContexts(), ['url.path', 'url.query_args']); }

Jakie cache contexty są dostepne w Drupalu?

Konteksty wymagane dla danego projektu mogą być także tworzone i definiowane przez moduły lub przez deweloperów. W tym celu tworzymy service z tagiem     
tags:

​​​​​​​      - { name: 'context_provider' } 

oraz implementujemy ContextProviderInterface.

Jednak liczne konteksty dostarcza już sam rdzeń Drupala.

Cookies
Ten kontekst może być używany do dostosowywania treści strony w zależności od wartości ciasteczek, takich jak personalizacja treści lub wyświetlanie treści tylko dla zalogowanych użytkowników.

Headers
Jest to kontekst, który bierze pod uwagę wartości nagłówków przesyłanych w żądaniu HTTP. Może być używany do dostosowania treści strony w zależności od wartości nagłówków, np wyświetlania treści tylko dla określonej przeglądarki.

IP
Ten kontekst bierze pod uwagę adres IP klienta. Może być używany do dostosowywania treści strony w zależności od pochodzenia żądania, takiego jak wyświetlanie treści tylko dla użytkowników z określonej lokalizacji geograficznej.

Languages
Kontekst, który może być używany do dostosowywania treści strony w zależności od języka.

Protocol_version
Jest to kontekst, który bierze pod uwagę wersję protokołu HTTP używaną w żądaniu HTTP, np. czy jest to wersja HTTP 1, czy może 2. 

Request_format
Ten kontekst bierze pod uwagę format żądania HTTP, taki jak JSON lub XML. Może być używany do dostosowywania treści strony w zależności od formatu, jak obsługa różnych typów żądań API.

Route
Jest to kontekst, który bierze pod uwagę użyty routin. Może być używany do dostosowywania treści strony w zależności od aktualnej ścieżki, np wyświetlania treści tylko na stronie startowej.

Session
Ten cache context bierze pod uwagę stan sesji użytkownika. Możemy go wykorzystywać do dostosowywania treści strony w zależności od stanu sesji, takiego jak wyświetlanie spersonalizowanej treści dla zalogowanego użytkownika.

Theme
Jest to kontekst, który bierze pod uwagę aktualnie wybraną skórkę Drupala.

Timezone 
To kontekst pamięci podręcznej, który odnosi się do strefy czasowej.

URL
Ten cache context jest związany z adresem URL żądania HTTP. Pozwala to na przechowywanie w pamięci podręcznej różnych wariantów strony, w zależności od adresu URL.

User
​​​​​​​Kontekst pamięci podręcznej odnosi się do zalogowanego użytkownika i jego uprawnień. Posiada także podkonteksty - is_super_user, node_grants, permissions, roles.

Co to są cache tagi w Drupalu?

Cache tagi to sposób zarządzania pamięcią podręczną w Drupalu, który pomaga uniknąć wyświetlania przestarzałych danych. Każdy element w cache ma przypisane tagi, które służą jako identyfikator. Na przykład zapisując node z id 1, Drupal poda go pod tagiem. [Node:1].

Kiedy element się zmienia, przykładowo po aktualizacji w bazie danych, jego tagi są nieważne, co powoduje usunięcie elementu z pamięci podręcznej. Dzięki temu mechanizmowi można uniknąć wyświetlania przestarzałych danych z cache.

System tagów to nie tylko tagi składające się z samego ID. Drupal generuje także tagi z listami nodów. np tag node_list oraz tagi dla konkretnego bundla np node_list:article.

Jak dodać cache o danym tagu?

Jeżeli używamy drupalowych encji, mają one wbudowane zapisywanie oraz czyszczenie tagów. Natomiast jeżeli chcemy zapisać customowe dane w cache z tagami, robimy to w poniższy sposób.

Przykład

Załóżmy, że $data to dane, które ściśle powiązane są z nodem id 1 oraz użytkownikiem o uid 1, ponieważ zawierają one dane użytkownika oraz tytuł node’a. Dodanie tagów node oraz user powoduje, że jeżeli zaktualizujemy encje użytkownika lub node’a, tag przestanie być ważny, a próba pobrania z pamięci podręcznej się nie uda i system pozyska świeże dane i wygeneruje nowe cache.

Przykładowe dane, które chcemy zapamiętać w pamięci podręcznej.

$data = [‘custom_data_123’ => Witaj Paweł, Lorem ipsum ];

Tagi, którymi chcemy oznaczyć dane.

$tags = [custom_data:123', ‘node:1’,’user:1’];

Zapisujemy dane w pamięci podręcznej z użyciem tagów.

Cache::set('cache_key', $data, Cache::PERMANENT, $tags);

lub

$this->cache->set('cache_key', $data, Cache::PERMANENT, $tags);


Jeżeli robimy to przez serwis, który obsługuje dany cache bin, np. @cache.data, wtedy w konstruktorze poprzez DI przekazujemy CacheBackendInterface $cache.

Jak wyczyścić cache o danym tagu? 

Innym przykładem niestandardowego użycia tagów jest czyszczenie selektywne per tag.

Przykład

Załóżmy, że mamy taki system, który raz dziennie robi aktualizacje w tabeli użytkowników i zmienia im dane na te pobrane z innego ERP. Jeżeli używamy do tego encji, przy zapisie Drupal automatycznie wyczyści tagi. Natomiast jeśli przyjmiemy, że specyfika systemu robi to bezpośrednio na bazie SQL, efekt będzie taki, że w bazie będziemy mieć nowe dane, ale Drupal dalej w wielu miejscach, w tym np. w widokach, będzie pokazywał stare dane użytkowników. Dzieje się tak, ponieważ bezpośrednia aktualizacja na bazie nie powoduje wyczyszczenia tagów. 

Aby wyczyścić tagi, możemy użyć: 

Cache::invalidateTags(['user:' . $id]);

gdzie podajemy pojedyncze tagi. 

Przydatne klasy interfejsów i serwisów do pracy z cache w Drupalu 9 i 10

W Drupalu dostępne są różne serwisy, które można wykorzystać do pracy z pamięcią podręczną. Poniżej omawiamy niektóre z nich.

CacheBackendInterface

Interfejs dla backendów pamięci podręcznej w Drupalu, który definiuje metody do dodawania, pobierania i usuwania danych z cache'a. Tego interfejsu używają istniejące biny. Jeżeli potrzebujemy swojego bina, musimy użyć właśnie tego.

  custom.bin_cache:
    class: Drupal\Core\Cache\CacheBackendInterface
    tags:
      - { name: cache.bin }
    factory: cache_factory:get
    arguments: [ custom_bin ]

CacheFactory

Serwis, który pozwala na tworzenie instancji pamięci podręcznej w Drupalu.

 $cache = $this->cacheFactory->get(‘custom_bin’); 
​​​​​​​$cache->set('cache_key', $data, Cache::PERMANENT, $tags);

CacheTagsInvalidatorInterface

Interfejs dla usług, które usuwają dane z pamięci podręcznej na podstawie tagów cache.

cache_tags.invalidator 
​​​​​​​     \Drupal::service('cache_tags.invalidator')->invalidateTags([‘node:1’]);

PageCache

Serwis, który zapewnia mechanizmy cache'owania stron internetowych, w tym opcjonalną integrację z Reverse Proxy Cache, takim jak Varnish.

DynamicPageCache

Serwis, który zapewnia cache'owanie dynamicznych elementów stron internetowych, takich jak bloki i widżety.

RenderCache

Serwis, który odpowiada za cache'owanie renderowanych elementów stron internetowych, takich jak bloki, formularze i strony.

CacheableJsonResponse

Klasa, która dziedziczy po klasie JsonResponse i pozwala na zwrócenie odpowiedzi JSON z dodatkowymi informacjami o cache'owaniu odpowiedzi.

Przykład: 

    $cache = new CacheableMetadata();
    $cache->setCacheMaxAge(7200);
​​​​​​​    $cache->setCacheContexts(['url.query_args', 'user']);
    $response = new CacheableJsonResponse($response, 200);
    $response->addCacheableDependency($cache);
​​​​​​​    return $response;

Zewnętrzne systemy cache w Drupalu

W Drupalu dostępne są różne moduły, które umożliwiają wykorzystanie zewnętrznych systemów cache, takich jak Memcache, Redis czy Varnish. Moduły te pozwalają na konfigurację połączenia z zewnętrznym systemem cache oraz ustawienia reguł, według których będą przechowywane informacje w pamięci podręcznej.

Zastosowanie zewnętrznego systemu cache może przyspieszyć czas ładowania stron internetowych i zmniejszyć obciążenie serwera, co przekłada się na lepszą wydajność i większą skalowalność strony. Dlatego, jeżeli standardowe cache nie jest dla nas wystarczające, warto rozważyć wykorzystanie takiego rozwiązania w swoim projekcie.

Varnish

Varnish Cache jest oprogramowaniem serwerowym, które może być używane do cache'owania treści stron internetowych. Varnish Cache przyśpiesza ładowanie stron internetowych poprzez zapisywanie kopii witryn w pamięci podręcznej, a następnie serwowanie tych kopii bezpośrednio z pamięci podręcznej, zamiast pobierania ich z serwera. Dzięki temu użytkownicy mogą szybciej przeglądać strony internetowe.

Memcache

Memcache to rozproszona pamięć cache'ująca, która umożliwia przechowywanie dużych ilości danych w pamięci RAM. W Drupalu można używać Memcache jako backendu dla pamięci podręcznej, co pozwala na szybszy dostęp do danych, niż w przypadku pobierania ich z bazy danych. Memcache jest również skalowalne, co oznacza, że można dodawać kolejne serwery, aby zwiększyć ilość dostępnej pamięci podręcznej.

Redis

Redis to cache typu klucz-wartość, które również przechowuje dane w pamięci RAM. W Drupalu można używać Redis jako backendu dla pamięci podręcznej, a także do przechowywania innych danych, takich jak kolejki zadań (Redis Queue).

Pamięć podręczna (cache) w Drupalu - podsumowanie

W tym artykule staraliśmy się przedstawić podstawowe założenia oraz narzędzia związane z cache'owaniem w Drupalu. Zachęcamy do zapoznania się z dokumentacją na stronie Drupal.org, gdzie można znaleźć szczegółowe informacje oraz przykłady.

Warto podkreślić, że cache w Drupalu 9 odgrywa kluczową rolę w optymalizacji witryny internetowej. Dzięki niemu strony ładowane są szybciej, a obciążenie serwera jest mniejsze. Dzięki dostępności różnych opcji konfiguracyjnych i narzędzi do zarządzania pamięcią podręczną, można ją dostosować do indywidualnych potrzeb każdej witryny i zapewnić użytkownikom jak najlepsze doświadczenia. 

Ważne jest, aby regularnie testować i optymalizować cache, aby upewniać się, że działa ono zgodnie z oczekiwaniami i przynosi pozytywne efekty dla witryny. Pamiętajmy, że właściwe zarządzanie pamięcią podręczną jest kluczowe dla osiągnięcia wysokiej wydajności i zadowolenia użytkowników naszej strony internetowej.

Interesują Cię tematy techniczne? Na naszym blogu chętnie dzielimy się wiedzą, doświadczeniem i najnowszymi informacjami z branży IT.