WordPress i kłopoty z cache – rozwiązanie

Od pewnego czasu na blogu występuje problem. Objawia się on tym, że niektóre wpisy nie wyświetlają się w całości, są jakby obcięte. Zauważyłem to parę dni temu, ale wtedy uznałem za jednorazowy wybryk i machnąłem ręką. Po części zwaliłem sprawę na cache, bo jego wyczyszczenie pomogło. Po głowie jako przyczyna chodziło mi też coś w stylu DDoS, który po publikacji artykułu na blogu uprawiają serwery Mastodon. I zupełnie nie miałem czasu na analizę.

Jednak dotarły do mnie sygnały (dzięki!) o tym, że sprawa się powtarza, postanowiłem przyjrzeć się bliżej. Dziś wszedłem z telefonu na ten sam wpis i… problem wystąpił ponownie. Z racji pory dnia ruch powinien być niewielki, więc warunki do diagnostyki powinny sprzyjać.

Trzy słowa o setupie

Blog jest dumnie wspierany przez WordPress, wykorzystywany jest nginx oraz php-fpm 8.2. Do tego dość intensywnie korzystam z wtyczek do WordPress. W szczególności do różnych optymalizacji i cache, co raczej nie ułatwi diagnostyki. Dużo elementów ruchomych, ingerencja w treść serwowanej występuje w wielu miejscach.

Wspomniałem o pluginie do cache jako jednym z podejrzanych. Konfiguracja Cache Enabler wygląda następująco:

Konfiguracja Cache Enabler
Screenshot konfiguracii Cache Enabler

Objawy i logi

Obcięty wpis wygląda tak:

Ucięty wpis na blogu
Screenshot uciętego wpisu

Cache był wygenerowany o 07:48 i pokrywa się to z czasem wejścia z telefonu widocznym w access.log.

drwxr-xr-x 2 www-data www-data 4096 Sep 24 07:48 pentagram-cerberus-p6361-rzut-okiem-na-bezpieczenstwo

W logu php-fpm nic specjalnego. Chwilę wcześniej widać raczej nie mogące mieć wpływu

[24-Sep-2023 07:44:23] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 1 idle, and 10 total children
[24-Sep-2023 07:44:27] WARNING: [pool www] child 20688 exited on signal 9 (SIGKILL) after 33847.200506 seconds from start
[24-Sep-2023 07:44:27] NOTICE: [pool www] child 25769 started

Diagnostyka

Problem występował na różnych przeglądarkach, także w trybie prywatnym. Zarówno na desktopie, jak i mobile. Sprawdzenie wersji zapisanej w cache pokazało, że jest ona błędna. Dobry znak, bo raczej można wykluczyć wpływ JSów. Były one podejrzane, bo wprawne oko dostrzeże, że polecenie curl przechodzi w pewnym momencie w kod źródłowy strony związany ze skryptem od statystyk Matomo.

Udało mi się ustalić, że wyczyszczenie cache pomaga, ale… tylko na pierwsze wyświetlenie. Kolejne wyświetlenia są już błędne. Sam problem występował niezależnie od obsługi JS. Czy to Firefox, czy links2, czy lynx – pierwsze wyświetlenie było poprawne, kolejne błędne.

Na pierwszy ogień poszły więc ustawienia wtyczki robiącej cache. W Minify HTML in cached pages including inline CSS and JavaScript wyłączyłem miniaturyzację CSS i JS. Nie pomogło.

Natomiast zupełne wyłączenie tej opcji jak najbardziej pomogło. Winnym okazała się zatem jedna z opcji wtyczki Cache Enabler w wersji 1.8.13. Takie tam ryzyka optymalizacji. Zgłosiłem problem na GitHub i zobaczymy co będzie dalej.

Rzuciłem jeszcze okiem na przyczynę. Prawdopodobnie chodzi o niepoprawne, zachłanne parsowanie komentarzy. Obcięcie następuje po pierwszym znaku *.
Gecko/20100101 Firefox/60.0' -H $'Accept: */*' -H $'Accept-Language: en-US,en;q=0.5'
natomiast linia bezpośrednio przed tym, co się zaczyna pojawiać, wygląda tak:
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */

Hej naprzód marsz – wolontariat

Od zeszłego roku w firmie mamy dodatkowy dzień wolny. Znaczy niezupełnie wolny. I pod warunkiem. Warunkiem jest wykorzystanie tego dnia na wolontariat. Jeśli nie zostanie wykorzystany w danym roku – przepada.

Pomysł na oko fajny, o ile ktoś udziela się już w tej formie. Dla kogoś takiego jak ja, który w dni powszednie nie udziela się – pomysł teoretycznie fajny. Z wolontariatu przychodził mi do głowy jedynie finał WOŚP, który odbywa się w dzień wolny. Do tego pewnie musiałbym się zorientować jakie papierki są wymagane i czy fundacja je wystawia. Nic więc dziwnego, że w zeszłym roku dzień mi przepadł.

Szczęśliwie głosy pracowników o tym, że przydała by się inspiracja, albo i podsunięcie stosownego zajęcia pod nos, zostały wysłuchane. Ekipa, która znalazła i wspierała potrzebującą fundację już w zeszłym roku, poszła w tym roku szerzej i zorganizowała wolontariat dla szerszego grona chętnych.

Organizacja na piątkę z plusem – wystarczyło określić w którym terminie chcemy pracować i się zapisać. Oprócz narzędzi i materiałów do pracy, papierkologii, załatwiony został także transport w postaci arkusza do wspólnego przejazdu autami. Czyli wszystko, co potrzeba.

Wolontariat miał miejsce w stadninie koni. Do wykonania było malowanie stajni oraz grodzenie nowych wybiegów. Pogoda dopisała, poza tym wolałem zrobić coś, gdzie się zmęczę a nie znowu malowanie. Bo w życiu trochę się namalowałem i mogę powiedzieć, że umiem – ach te remonty i remonciki w domu…

Ostatecznie, po początkowych zawirowaniach, wyklarowały się ekipy kopiące, koszące, mierzące. Po przerwie na posiłek regeneracyjny i montażu pierwszej części ogrodzenia pojawiło się nowe zadanie – okablowanie elektrycznego pastucha. Bardzo mi się ono spodobało, bo z elektrycznymi pastuchami nie miałem w życiu do czynienia. Zresztą, jak to zamontować, to chyba nikt nie wiedział. Na szczęście były istniejące ogrodzenia na wzór.

Dowiedziałem się, że elektryczny pastuch składa się z generatora, podającego napięcie na płot. Nie ma zamkniętego obwodu, nie ma plus i minus – wyładowanie następuje do ziemi, tam zresztą podłącza się „drugi kabel” od generatora. A całe ogrodzenie to jeden wielki, wspólny element pod napięciem. Inne ciekawe rzeczy – napięcie w naszym przypadku to jakieś 6000V. Istnieją dedykowane narzędzia do pomiaru, ale działanie można sprawdzić i ręką. Nie jest groźne, jest nieco nieprzyjemne. I jeszcze ważna rzecz – pastuch wysyła impulsy, więc nie „kopie” cały czas, krótkie dotknięcie nie powie prawdy, czy jest załączony.

Po pracy mieliśmy relaks przy grillu. Ogólnie bardzo fajna impreza, w doskonałej atmosferze. Fajnie, że pogoda dopisała, choć mogło być nieco chłodniej. Niemal cały dzień na łące, bez cienia może być męczący sam w sobie. Na szczęście było mnóstwo wody w butelkach i były podpowiedzi wcześniej, by wziąć czapki i krem ochronny.

Opisuję całość, bo w tego typu grupowym wydaniu wolontariat może z powodzeniem służyć jednocześnie za integrację. Zorganizowanie grilla na istniejącej infrastrukturze jest tanie, ludzie spędzają ze sobą dużo czasu robiąc zupełnie inne rzeczy, niż w pracy. Cel jest fajny bo i daje satysfakcję – widoczna, wykonana praca, i pomaga się potrzebującym. W porównaniu z dedykowanymi wyjazdami integracyjnymi to pewnie jakieś śrubki, tym bardziej, że firma pewnie też może to jakoś rozliczyć. Choć nie wiem, nie jestem księgowym. Oczywiście nie zastąpi to typowych integracji w każdym przypadku, tym bardziej, że nie każdy może/chce pracować fizycznie, ale warto rozważyć naśladowanie.

Połam to lepiej

Niedawno brałem udział w konkursie organizowanym przez Sekurak.pl. Konkurs polegał – jak to się potocznie mówi – na „łamaniu haseł”. Poprawniej byłoby rzec, że polegał na odzyskiwaniu haseł na postawie hashy, ale mniejsza o to. Udało mi się wygrać, tzn. złamałem największą ilość haseł jako pierwszy. Teoretycznie więc mógłbym na tym poprzestać, ale – z perspektywy patrząc – pewne rzeczy można było zrobić albo opisać lepiej. Oryginalny writeup mój i innych zwycięzców można znaleźć w tym artykule, natomiast ten wpis jest jedynie jego uzupełnieniem.

Dla przypomnienia, jeśli chodzi o gotowce, skorzystałem ze reguł wbudowanych w hashcata, reguły OneRuleToRuleThemAll[1]. Zaś jeśli chodzi o gotowe słowniki to użyłem RockYou, Ignis (10 mln) oraz słownika języka polskiego dostępnego w Debianie w pakiecie wpolish.

Co można zrobić lepiej? Przede wszystkim ten wpis na blogu daje namiar na „growy” słownik języka polskiego. Jest on mniejszy od tego z wpolish. Zawiera 3,2 mln linii, zamiast 4,3 mln. Mogłoby wydawać się to wadą, ale… przy skomplikowanych operacjach większy słownik nie musi być lepszy.

Słownik growy zawiera znaki końca linii w formacie windowsowym. Można je przekształcić na format uniksowy np. w taki sposób:

cat slowa.txt | perl -ne 'chomp; /(\S+)/ && print $1,$/' > slowa_unix.txt

Taka wersja przyda się w kolejnych operacjach.

Słownik wpolish można z kolei znormalizować przez usunięcie wielkich liter. Nie mają one znaczenia, bo występują na początku i któraś reguła hashcata z pewnością uwzględnia takie przekształcenie. Polecenie

cat /usr/share/dict/polish | tr "[:upper:]" "[:lower:]" | sort -u > wpolish_lower.txt

redukuje wielkość słownika o 46k wierszy, czyli nieco ponad 1%.

Z kolei jeśli chodzi o najbardziej kompletny słownik języka polskiego, to będzie nim złączenie znormalizowanego wpolish oraz growego z poprawionymi końcami nowej linii:

cat slowa_unix.txt wpolish_lower.txt | sort -u > slownik_all.txt

Wynik jest o jedynie 5k linii większy od oryginalnego wpolish.

Pozbycie się pl-znaków ze słownika:

cat slownik_all.txt | tr "ąćźżńłóęś" "aczznloes" | sort -u > slownik_all_nopl.txt

Łatwo zauważyć, że jeśli ktoś będzie miał hasło, które jest wyrazem w języku polskim, ale zapisanym w sposób mieszany, częściowo z polskimi znakami, częściowo wersją polskawą, to nasze słowniki taki wyraz ominą. Przykładem takiego słowa może być wyraz żółty zapisany jako zólty. Co wtedy?

Pomóc może zrobienie własnej reguły hashcata, wykorzystującą zastąpienie, czyli s:

Replace sXY Replace all instances of X with Y

Można od razu w podobny sposób potraktować „jednoliterowe” błędy ortograficzne i regułą zastąpić u przez ó i na odwrót. Niestety, z tego co widzę przy pomocy samego hashcata nie da się zrobić ani przekształcenia ch -> h, ani odwrotnego. W dodatku powyższe zastąpienie dotyczy wszystkich wystąpień, a niezupełnie o to chodzi. Jednak lepszy rydz, niż nic.

Tyle gwoli uzupełnienia. Regułę hashcata pozostawiam do samodzielnego montażu, a o zewnętrznym skrypcie do przekształceń jeszcze pomyślę.

[1] Celowo linkuję do genezy powstania, której lekturę gorąco polecam, nie samej reguły.