Štítek: výkon

Když změna jednoho řádku CSS znamená nahrát šest tisíc souborů

Chtěl jsem tu změnit barvu odkazů. Jeden řádek ve stylech. Spustil jsem deployment a s hrůzou v očích koukal, jak se na server nahrává šest tisíc souborů.

Nic se nerozbilo, ale bylo jasné, že něco je velký špatný.

Může za to vychytávka

Každá stránka měla v hlavičce otisk obsahu stylů:

<link rel="stylesheet" href="/assets/css/site.css?v=a3f9c21b04">

Když se styly změní, změní se adresa a prohlížeč si stáhne novou verzi místo té nacachované. Dal jsem to tam schválně a byl na to docela pyšný.

Jenže se tím otisk propsal do každé stránky webu. Změním jeden znak v CSS, změní se všech 6 228 stránek — a deploy skript, který umí posílat jen to, co se skutečně změnilo, to poslušně pošle všechno.

Když jsem se podíval, co vlastně moje úložiště posílá prohlížečům, došlo mi to:

$ curl -sI https://sean.cz/assets/css/site.css
cache-control: public, max-age=0
etag: W/"409e-19f9a201251"

max-age=0 znamená, že se prohlížeč u každého načtení stejně ptá, jestli se soubor nezměnil. Ta vychytávka nebyla nikdy potřeba. Řešila problém, který jsem neměl, a způsobila ten, co jsem měl.

Nový příspěvek přepsal celý archiv

Stránky výpisu se stránkovaly odshora: deset nejnovějších na první stranu, dalších deset na druhou. Nový příspěvek se zařadí na začátek a posune všechno ostatní — hranice všech 328 stran se posunou s ním. Nový post tak znamenal přepsat cca 435 souborů.

Spravilo se to obrácením směru. Desítky se teď počítají od nejstaršího příspěvku, takže nejstarší desítka je navždycky stejná a nový text se přidá jen do té poslední, rozepsané stránky.

Daň je, že /page/1/ dneska znamená nejstarší příspěvky, ale to nevadí, protože se na ně chodí přes „Novější / Starší".

Věc, kterou jsem nečekal

Při ověřování jsem přidal zkušební příspěvek a čekal devět změněných souborů. Vyšlo jich 65 — a byly mezi nimi stránky, které s tím textem nemohly mít nic společného.

Ukázalo se, že když web přegeneruju dvakrát za sebou, pokaždé vyleze trochu jiný výsledek. Příspěvky se řadí podle data a Ruby negarantuje, který ze dvou se stejným datem skončí první. A po stěhování z Tumblru a Twitteru má 110 příspěvků úplně stejné datum jako nějaký jiný.

Nic nezměním, spustím build a přesto mám změněné soubory. Vůbec jsem to netušil. Oprava je jeden řádek: když je datum stejné, rozhodne jméno příspěvku.

Měřit, ne hádat

Byl jsem si jistý, co bude nejdražší operací — kopírování 384 MB fotek. Změřil jsem to a představa se rozsypala. Fotky: jedna sekunda.

Nejdražší bylo mazání složky s hotovým webem, 3,8 sekundy. Build ji vždycky smazal celou a hned do ní nasypal zpátky skoro totožných 7 700 souborů. Teď se nemaže; zapisuje se jen to, co se opravdu liší.

Dvě věci mimo rychlost

Komentáře pod články tahám z Mastodonu a jméno autora jsem vkládal do stránky bez ošetření. Jenže jméno účtu si nastavuje kdokoli ve Fediverse — stačilo se přejmenovat na kus kódu, odpovědět mi na toot a nechat si ho spustit v prohlížeči každému čtenáři. Učebnicová díra. Zavřeno.

A postranní panel si tahal poslední tooty a commity přímo v prohlížeči návštěvníka: čtyři a víc dotazů na cizí služby při každém otevření jakékoli stránky. GitHub navíc nepřihlášené pouští jen šedesátkrát za hodinu, takže ta kartička po chvíli mizela. Teď si data stáhne server.

Jak to dopadlo

  • změna stylů: z 6 228 souborů na jeden
  • nový příspěvek: z ~435 na sedm až devět
  • přegenerování beze změny obsahu: z desítek tiše změněných souborů na nulu
  • dotazy na cizí služby při otevření stránky: ze čtyř a víc na žádný
  • obrázky stažené na úvodní stránce: z desítek na jeden
  • build: ze třinácti sekund na sedm a půl

Dvě poučení. Byl jsem si jistý, že problém jsou fotky, a byla to složka, na kterou bych nesáhl ani po dlouhém přemýšlení. A to prohazování pořadí nikdy nespadlo, nic nenahlásilo, nic nerozbilo — jen potichu dělalo práci navíc.


Jestli jste četli Jak je postavený tenhle blog, tak ho tenhle článek na pár místech přepisuje — tooty ani commity už se v prohlížeči netahají a složka s hotovým webem se nemaže. Původní text nechávám být; popisoval stav, který tehdy platil.

Číst dál