03-comment-je-travaille/vps/06-performance.md

Performance & optimisations

Ce document répond à : "Y a-t-il des optimisations sur le serveur Telaria ?" Chaque optimisation est documentée avec son impact mesurable et ses piÚges.


1. OPcache PHP — bytecode en mĂ©moire

Sans OPcache, PHP relit et recompile chaque fichier .php Ă  chaque requĂȘte. L'OPcache mĂ©morise le bytecode compilĂ© en RAM.

Configuration de production

; /etc/php/8.4/fpm/conf.d/10-opcache.ini
opcache.enable                 = 1
opcache.memory_consumption     = 256    ; Mo de RAM alloués au cache
opcache.interned_strings_buffer = 16   ; Mo pour les chaßnes internalisées
opcache.max_accelerated_files  = 10000 ; nb max de fichiers en cache
opcache.revalidate_freq        = 0     ; ne jamais revérifier les fichiers
opcache.validate_timestamps    = 0     ; ⚠ voir piĂšge ci-dessous
opcache.save_comments          = 1     ; requis par Doctrine (annotations)
opcache.enable_cli             = 0     ; pas d'OPcache en CLI (confus avec prod)

⚠ Le piĂšge OPcache en production

validate_timestamps=0 signifie que PHP-FPM ne vĂ©rifiera jamais si les fichiers ont changĂ© sur disque. C'est la bonne configuration pour la performance — mais elle exige un protocole strict lors des dĂ©ploiements.

SymptĂŽme classique : aprĂšs un git pull + migration Doctrine, le CLI (php bin/console) voit le nouveau schĂ©ma, mais les requĂȘtes web retournent SQLSTATE[42S22] Unknown column 't0.username' — PHP-FPM sert l'ancien code compilĂ©.

Solution unique : sudo systemctl reload php8.5-fpm aprĂšs chaque dĂ©ploiement. Le reload est graceful (les requĂȘtes en cours se terminent, les workers suivants chargent le nouveau code).

Vérifier l'état de l'OPcache

php -r "print_r(opcache_get_status());" | head -30
# ou depuis une URL admin si configuré

2. Brotli + Gzip — compression des rĂ©ponses

La compression rĂ©duit le volume transfĂ©rĂ© (HTML, JSON, CSS, JS) — gain typique de 60–80%.

Brotli est l'algorithme moderne (meilleure compression que Gzip, supporté par tous les navigateurs modernes). Gzip reste le fallback pour les clients anciens.

; /etc/apache2/conf-available/compression.conf
<IfModule mod_brotli.c>
    AddOutputFilterByType BROTLI_COMPRESS \
        text/html text/plain text/css text/xml text/javascript \
        application/javascript application/json application/xml \
        image/svg+xml font/woff2
    BrotliCompressionQuality 5
    ; Niveau 5 : meilleur ratio compression/CPU. Niveau 11 = max mais CPU élevé.
</IfModule>

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE \
        text/html text/plain text/css text/xml text/javascript \
        application/javascript application/json application/xml \
        image/svg+xml
</IfModule>
sudo a2enconf compression
sudo systemctl reload apache2

# Vérifier
curl -sI -H "Accept-Encoding: br" https://telaria.dev | grep -i content-encoding
# → content-encoding: br
curl -sI -H "Accept-Encoding: gzip" https://telaria.dev | grep -i content-encoding
# → content-encoding: gzip

3. HTTP/2 — multiplexage des requĂȘtes

HTTP/2 permet d'envoyer plusieurs requĂȘtes en parallĂšle sur une seule connexion TCP. Sur une page avec beaucoup d'assets (CSS, JS, images), l'impact est significatif.

; Dans le vhost HTTPS
Protocols h2 http/1.1
; h2 = HTTP/2 chiffré (requis avec HTTPS)
; http/1.1 = fallback si le client ne supporte pas HTTP/2
curl -I --http2 https://telaria.dev | head -5
# HTTP/2 200

HTTP/2 n'est disponible qu'avec HTTPS (les navigateurs ne l'implémentent qu'en mode chiffré).


4. Cache navigateur — assets versionnĂ©s

Les assets statiques (CSS, JS, images) changent rarement mais sont téléchargés à chaque visite si non cachés.

Stratégie : max-age long + versioning via AssetMapper

Symfony AssetMapper ajoute automatiquement un hash de contenu dans l'URL des assets (app.abc123.css). Un changement de fichier = nouvelle URL = cache invalidé automatiquement.

; Cache 1 an pour les assets versionnés (URL contient hash)
<IfModule mod_headers.c>
    <FilesMatch "\.(css|js|svg|woff2|ico|png|jpg|webp)$">
        Header set Cache-Control "public, max-age=31536000, immutable"
    </FilesMatch>
</IfModule>

immutable indique au navigateur de ne jamais revalider l'asset (mĂȘme si l'utilisateur force un refresh) — l'URL a changĂ© si le contenu a changĂ©.

Vérification

# Vérifier le header Cache-Control sur un asset
curl -sI https://telaria.dev/assets/app.abc123.css | grep -i cache-control
# → Cache-Control: public, max-age=31536000, immutable

# Vérifier le 304 (pas de re-téléchargement)
ETAG=$(curl -sI https://telaria.dev/assets/app.abc123.css | awk '/^etag:/{print $2}')
curl -I -H "If-None-Match: $ETAG" https://telaria.dev/assets/app.abc123.css
# → HTTP/2 304

5. PHP-FPM pool tuning

Le pool PHP-FPM détermine combien de processus PHP tournent en parallÚle.

; /etc/php/8.4/fpm/pool.d/www.conf
pm = dynamic

pm.max_children      = 20  ; max processus simultanés (RAM disponible / RAM par worker)
pm.start_servers     = 4   ; processus au démarrage
pm.min_spare_servers = 2   ; processus idle minimum
pm.max_spare_servers = 6   ; processus idle maximum
pm.max_requests      = 500 ; recycler un worker aprĂšs N requĂȘtes (prĂ©vient les fuites mĂ©moire)

; Timeout (protĂšge contre les scripts trop longs)
request_terminate_timeout = 60

Calculer pm.max_children

# Mémoire par worker PHP en production
ps -eo pid,rss,comm | grep php-fpm | awk '{sum+=$2} END {print sum/NR/1024 " Mo par worker"}'
# Exemple : 40 Mo par worker
# RAM disponible pour PHP : 1200 Mo - 400 Mo (OS+MySQL+Apache) = 800 Mo
# pm.max_children = 800 / 40 = 20

Surveiller les processus FPM

# Statut du pool (activer pm.status_path dans www.conf)
sudo curl --unix-socket /run/php/php8.5-fpm.sock http://localhost/fpm-status

6. MySQL — optimisations InnoDB

; /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
; Buffer pool InnoDB (principal levier de performance MySQL)
innodb_buffer_pool_size = 256M  ; 256 Mo sur VPS 2 Go (adapter selon la RAM)

; Log I/O
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2  ; Performance (perte max 1s de transactions en cas de crash)

; Connexions
max_connections = 50            ; sur VPS léger, éviter les connexions fantÎmes
connect_timeout = 10
wait_timeout    = 300

7. Vue d'ensemble — gains attendus

Optimisation Gain typique
OPcache ×5–10 en temps d'exĂ©cution PHP
Brotli niveau 5 −60–70% sur HTML/JSON vs non compressĂ©
HTTP/2 multiplexage −20–40% temps de chargement page avec nombreux assets
Cache navigateur (max-age=1an) 0 requĂȘte rĂ©seau pour les assets lors des visites suivantes
PHP-FPM tuning Adapté à la RAM, pas de saturation ni de swap

Étape suivante : 07-email-delivrabilite.md

Assistant documentaire

Posez une question sur la documentation. Les rĂ©ponses citent leurs sources — un clic ouvre le document Ă  gauche.

Loading…
Loading the web debug toolbar…
Attempt #