Automatisk cache busting i Symfony2

Det projekt jag jobbar med just nu är mitt första projekt med Symfony2 som backend-ramverk. Jag är på det stora hela nöjd med hur det fungerar, men ett återkommande problem har varit att efter en ny deploy till testmiljön så har våra interna testare inte fått uppdaterade javascript och CSS-filer direkt, utan fått lov att göra en hård refresh för att få den senaste versionen.

Detta har berott på att vår testserver kör med `mod_expires` och sätter expire ganska så långt in i framtiden för att statiska resurser ska cachas bra på klienten. Detta kräver dock att man har ett bra sätt för att leverera fräscha resurser så snart de uppdateras. Symfony2 använder Assetic för att hantera resurser såsom javascript och CSS. Den app vi bygger har en tung frontend med mycket javascript, och har man inte den senaste versionen av koden för klientsidan så fungerar inte appen som den ska, vi har alltså ett stort behov av att kunna tömma cache på klienten så snart det behövs.

Därför satte jag mig idag och försökte ta itu med detta problem. I det ramverk vi själva har byggt internt för vår e-handelsplattform döps javascript och CSS filerna baserat på källfilernas innehåll. Det innebär att de källfilerna som ska skickas till klienten komprimeras och slås samman till en fil, som sedan namnges baserat på innehållet i alla filerna och levereras till klienten. Detta har den fördelen att så snart något ändras byter den slutgiltiga filen namn och på så vis får klienten alltid en uppdaterad version.

Det finns en liknande lösning för Assetic där man kör ett extra kommando för att döpa om alla filer i efterhand, men med den volym javascript som vår app innehåller skulle detta förlänga våra deploytider med allt för mycket för att vara realistiskt. Inbyggt i Assetic finns det stöd för att använda en query string för att sköta cache busting, men detta kräver då att man är en duktig utvecklare och kommer ihåg att ändra det värdet varje gång man gör en ny deploy.

Min lösning på detta problem blev att vid varje deploy uppdatera värdet på assets_version automatiskt, och då sätta det till nuvarande revision i vårt git repository. Vi har redan ett enkelt bash script som körs varje gång vi ska göra en deploy för att uppdatera cache och dumpa javascript filer, så i det scriptet har jag lagt till följande:

# update assets version to current git revision
rev=`git rev-parse HEAD | cut -c 1-8`
cat app/config/parameters.yml | grep -v assets_version > /tmp/parameters.yml

echo "    assets_version: $rev" >> /tmp/parameters.yml
mv /tmp/parameters.yml app/config/

Sedan hämtar jag parametern i min config.yml:

framework:
    templating:      { engines: ['twig'], assets_version: %assets_version% }

På så vis ska klienterna förhoppningsvis alltid har färska javascript och CSS filer efter en deploy.