Performances PHP LiteSpeed : cache opcode, LSAPI et suivi WaitQ

Guide pratique pour optimiser PrestaShop sur LiteSpeed : configurer OPcache, dimensionner LSAPI par RAM, surveiller WaitQ et réduire TTFB/503.

Poste de travail avec double moniteur affichant des données techniques sur le cache opcode et la sécurité.

Table des matières :

  1. LiteSpeed + PrestaShop : où se perd le temps côté PHP (et pourquoi LSAPI change la donne)
  2. Cache opcode (OPcache) sous LiteSpeed : dimensionnement, invalidation et “warming” réaliste
  3. LSAPI : modèle de processus, variables clés et dimensionnement par la RAM (pas “au pif”)
  4. WaitQ LiteSpeed : définition, lecture “queueing theory”, et corrélation directe avec 503/TTFB
  5. Runbook “Performances PHP LiteSpeed” : mesurer, corriger, vérifier (sans casser la prod)

LiteSpeed + PrestaShop : où se perd le temps côté PHP (et pourquoi LSAPI change la donne)

Sur un front PrestaShop 8.1.x ou 9.x (PHP 8.2/8.3), le goulot est rarement “le serveur web” au sens strict : c’est la somme du bootstrap PHP (autoload Composer + classes legacy), du rendu Twig/Smarty, des hooks de modules, puis des I/O (MySQL, Redis si présent). LiteSpeed, lui, intervient surtout sur la capacité à encaisser de la concurrence HTTP sans exploser le TTFB. Si vous avez déjà un reverse proxy (Varnish/CDN), vous le voyez tout de suite : les pages non cachées (panier, compte, checkout, back-office) sont celles qui souffrent dès que la concurrence augmente.

Deux rappels utiles (souvent confondus en prod) :

  • Latence réseau : si votre audience est majoritairement en France/Belgique/Suisse, héberger le serveur près de votre base d’utilisateurs (ex. région parisienne / nord de l’Europe) peut gratter quelques millisecondes… mais ne résout pas une file d’attente PHP. Une WaitQ élevée est presque toujours un problème de capacité/temps de service, pas un problème de distance.
  • Cache HTTP vs PHP : un CDN/Varnish “sauve” la home et les pages catégories/produits cacheables, mais dès qu’un cookie, une session, une variation de prix, un panier ou une page compte entre en jeu, vous revenez au coût réel du traitement PHP + DB.

LiteSpeed Web Server (Enterprise ou OpenLiteSpeed) exécute PHP via LSAPI (LiteSpeed SAPI), un modèle proche de PHP-FPM (process pool) mais piloté par le serveur LiteSpeed, avec ses propres métriques temps réel (dont WaitQ). L’intérêt pratique : vous pouvez relier saturation PHP ↔ queue côté serveur web sans deviner. En PHP-FPM classique derrière Nginx/Apache, on finit souvent à corréler trois sources (status FPM, logs Nginx, sysstat) pour comprendre si la file d’attente se forme côté webserver ou côté PHP.

Mini-scénario typique e-commerce (très fréquent pendant soldes / campagnes Ads) :

  • 200 utilisateurs “actifs” en même temps.
  • 80% des hits sont servis en cache HTTP (rapides).
  • 20% touchent des pages non cachées (panier/checkout/compte) et déclenchent des hooks modules (paiement, fidélité, anti-fraude, widgets).

Résultat : même si “le site semble rapide” sur les pages cacheées, la capacité réelle du serveur se joue sur la concurrence côté PHP. C’est exactement là où LSAPI + WaitQ deviennent intéressants : ils rendent visible la saturation sur les endpoints dynamiques, souvent cachée par le cache HTTP sur le reste.

Point à ne pas maquiller : PrestaShop n’a pas de page cache full HTML natif en cœur (hors mises en cache applicatives et CCC), donc en trafic élevé vous êtes mécaniquement dépendants du cache HTTP (Varnish/CDN) et de la latence PHP pour tout ce qui ne se cache pas. Si votre contexte est “soldes / pics de trafic”, recoupez avec les approches multi-niveaux et reverse proxy (voir Cache PrestaShop : Varnish, Redis, Memcached et OPcache côté serveur et Optimisation PrestaShop pour soldes : caches, CDN et Varnish en trafic élevé).

Cache opcode (OPcache) sous LiteSpeed : dimensionnement, invalidation et “warming” réaliste

Le “cache opcode” en PHP, c’est OPcache : le moteur conserve en mémoire partagée le bytecode des scripts PHP déjà compilés, pour éviter de re-parser/re-compiler à chaque requête. La définition la plus directe reste celle du manuel PHP : « OPcache improves PHP performance by storing precompiled script bytecode in shared memory » (PHP Manual, OPcache). Sur PrestaShop, l’impact est généralement massif parce que l’empreinte en fichiers PHP chargés est élevée (core + vendor + modules) et que vous avez beaucoup de hits sur les mêmes points d’entrée.

Sous LiteSpeed + LSAPI, OPcache fonctionne comme avec FPM, mais la réalité opérationnelle change : une saturation de l’OPcache (mémoire trop faible, trop peu d’entrées, fragmentation) se traduit très vite par une hausse de CPU (compilations) et donc par des temps de réponse plus longs… qui se transforment ensuite en file d’attente WaitQ (section dédiée plus bas). Le piège courant est de “l’activer” sans le dimensionner.

Indicateurs simples à viser en prod (à lire via opcache_get_status() ou via une page admin sécurisée) :

  • Hit rate > 99% sur une charge stable.
  • 0 “out of memory” / “hash overflow” dans les stats OPcache.
  • Utilisation mémoire < 90% en pic (sinon fragmentation/évictions).
  • Max accelerated files : pas d’évictions “silencieuses” (scripts non mis en cache alors qu’ils devraient l’être).

Pour un PrestaShop avec un parc de modules non trivial, les valeurs “mutualisé” (128 Mo / 10k fichiers) sont souvent trop faibles.

Base de travail pragmatique (à ajuster avec les stats) :

; PHP 8.2/8.3 - production
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256
opcache.interned_strings_buffer=32
opcache.max_accelerated_files=100000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.jit=0

Points d’attention concrets :

  • memory_consumption : 256–512 Mo n’a rien d’extravagant sur un dédié/VPS e-commerce, surtout si vous avez beaucoup de modules (et donc de classes chargées).
  • max_accelerated_files : sous-dimensionné, vous perdez des scripts (évictions) et vous recompilez “en boucle”. C’est souvent le vrai facteur limitant avant la mémoire.
  • interned_strings_buffer : utile sur des applis avec beaucoup de chaînes répétées (framework + templates + vendor). Si vous observez une pression mémoire OPcache malgré un memory_consumption élevé, ce buffer peut aider.
  • jit : en web e-commerce “classique”, le JIT reste rarement le meilleur levier ; il peut même compliquer le debug/profiling. Faites-le uniquement après benchmark reproductible.

Un moyen simple d’éviter le “tuning à l’aveugle” est de capturer, après quelques minutes de charge représentative, un petit tableau de lecture (exemple de ce que vous cherchez à obtenir) :

Indicateur OPcache Ce que ça signifie Action typique
Mémoire > 90% + hit rate qui baisse risque d’évictions / fragmentation augmenter memory_consumption
num_cached_scripts proche de la limite trop peu d’entrées disponibles augmenter max_accelerated_files
“out of memory” / “hash overflow” OPcache saturé augmenter mémoire et/ou entrées
Hit rate < 99% sans déploiement recompilations inutiles vérifier invalidation, trop d’évictions, fichiers non cacheables

Côté invalidation, il faut choisir : sécurité de déploiement vs perf brute. En prod, certains fixent opcache.validate_timestamps=0 (pas de stat() sur les fichiers, donc plus rapide) et déclenchent un reload propre au déploiement (graceful restart LSWS/LSAPI). Ça fonctionne très bien… tant que votre chaîne de déploiement est propre (release immuable, symlinks atomiques, purge cache applicatif, rollback maîtrisé). Si vous n’avez pas ce niveau de discipline, gardez validate_timestamps=1 et un revalidate_freq raisonnable.

Deux cas où validate_timestamps=1 est souvent le “moins risqué” :

  • stockage applicatif sur un FS réseau (certains clusters/stockages distribués) où les incohérences de timestamps peuvent arriver ;
  • environnements où plusieurs équipes “patchent” encore à la main (ce qui arrive en e-commerce…).

Pour le “warming” : gardez-le réaliste. OPcache se réchauffe naturellement au trafic, mais après un redémarrage/reload, le premier passage sur les routes clés peut redevenir coûteux. Un warm-up minimal (souvent suffisant) consiste à faire rejouer 10–20 URLs représentatives : home, 2–3 catégories, 2–3 produits, recherche, panier, login client, et 1–2 pages back-office (auth + dashboard), sans lancer 500 pages artificiellement (ça ferait travailler MySQL inutilement).

Pour vérifier OPcache dans des environnements cPanel/LSWS, voyez aussi OPcache PHP : activer et vérifier l’extension dans cPanel. Et si vous cherchez une méthode de benchmark plus globale (PHP + DB + cache), recoupez avec Benchmarks performance PrestaShop : optimiser PHP-FPM/OPcache/MySQL.

LSAPI : modèle de processus, variables clés et dimensionnement par la RAM (pas “au pif”)

LSAPI est la passerelle entre LiteSpeed et PHP, avec un pool de processus PHP persistants. Le paramètre critique, c’est le nombre de workers PHP réellement capables de traiter en parallèle, sans swapper et sans faire exploser le CPU. Le dimensionnement “CPU cores × 2” est une approximation qui se casse la figure sur PrestaShop, parce que l’empreinte mémoire d’un process PHP chargé (core + vendor + modules + templates compilés) peut facilement dépasser 150–250 Mo RSS.

La méthode robuste : raisonner RAM d’abord, puis plafonner par le CPU.

  1. Mesurez l’empreinte réelle d’un worker en charge (RSS) via ps -o rss,cmd -C lsphp (ou le binaire PHP LSAPI de votre distribution).
  2. Définissez la RAM réellement disponible pour PHP (en retirant OS + MySQL/MariaDB + Redis + marge).
  3. Calculez : children_max = RAM_PHP / RSS_moyen.

Exemple réaliste : serveur 16 Go, vous réservez 6 Go (OS + DB + buffers + marge), il reste 10 Go pour PHP. Si un worker est à ~200 Mo RSS en charge : 10 000 / 200 = 50 workers maximum théorique. Ensuite vous limitez par le CPU (sinon vous déplacez le problème sur la run queue Linux). Dans la pratique, sur 8 vCPU, démarrer à 20–30 workers est souvent plus sain que 50.

Ce calcul devient encore plus important dès que :

  • vous activez un cache applicatif (Redis) + vous gardez MySQL local (donc concurrence CPU/RAM),
  • vous avez des modules qui font des appels externes (API, anti-fraude, transporteurs) : même si le CPU ne monte pas, les workers restent “occupés” en attente réseau, ce qui réduit la capacité utile.

Côté paramètres LSAPI (selon votre packaging / panneau), vous retrouverez généralement des équivalents à :

  • nombre de children (pool size)
  • max requests par process (recyclage, type pm.max_requests en FPM)

Conceptuellement : recyclez les workers pour limiter la dérive mémoire (fragmentation, fuites dans des extensions/modules). Un max_requests de quelques milliers est un point de départ, à ajuster selon votre stabilité. Le but n’est pas de “tuer PHP tout le temps”, mais d’éviter des workers qui gonflent pendant des jours.

Checklist rapide “dimensionnement sain” (à valider sur 24–72h de prod) :

  • swap à 0 (ou quasi) en période normale ;
  • RSS des workers stable (pas de dérive infinie) ;
  • WaitQ retombe à ~0 après les pics ;
  • temps de réponse p95 des pages non cachées stable (pas de “yo-yo”).

Troisième point souvent sous-estimé : le back-office. PrestaShop admin déclenche des parcours très différents (imports, génération d’images, indexation, modules lourds). Si vous mélangez front + admin dans le même pool sans garde-fou, un import CSV peut siphonner vos workers et créer une WaitQ qui pénalise le front. La séparation la plus propre reste : vhost/admin isolé (ou au minimum des limites de ressources/process dédiés), et des tâches lourdes basculées en asynchrone (CLI, cron, queue). Pour les workflows asynchrones, la logique d’orchestration via webhooks est un bon complément (voir l’automatisation via n8n : n8n : automatiser la gestion des commandes e‑commerce via webhooks).

Références externes utiles côté LiteSpeed : documentation LSWS (LSWS Docs). Ne vous contentez pas de “copier un template” : les valeurs par défaut sont rarement adaptées à un e‑commerce modulaire.

WaitQ LiteSpeed : définition, lecture “queueing theory”, et corrélation directe avec 503/TTFB

WaitQ (Wait Queue) dans LiteSpeed correspond au nombre de requêtes en attente, typiquement parce que toutes les ressources de traitement sont occupées (workers LSAPI saturés, limites de connexions atteintes, throttling). Ce n’est pas une métrique “nice-to-have” : c’est la matérialisation de la latence avant même que PHP commence à travailler. Quand WaitQ monte, votre TTFB s’allonge mécaniquement, puis les timeouts client/proxy tombent.

Pour expliquer WaitQ sans folklore : c’est du queueing. La loi de Little (L = λW) est le cadre minimal pour relier longueur de file (L), débit (λ) et temps d’attente (W). John D.C. Little formalise cette relation dans « A Proof for the Queuing Formula: L = λW » (Operations Research, 1961, DOI : https://doi.org/10.1287/opre.9.3.383). Traduction opérationnelle : à débit constant, si vous laissez la file grandir, vous augmentez le temps d’attente moyen ; si vous réduisez le service time (PHP plus rapide) ou augmentez la capacité (plus de workers, plus de CPU, moins de contention), la file retombe.

Concrètement, dans le panel LiteSpeed (Real-Time Stats), vous surveillez au minimum :

  • WaitQ (file)
  • Req/Sec (débit)
  • Busy Workers / External App (occupation LSAPI)
  • MaxConn / Soft/Hard limits (selon votre conf)

La plupart des shops “sains” ont WaitQ ~0 en régime normal, avec de petites dents pendant les pics (quelques requêtes) si les pages non cachées sont lentes. Si WaitQ reste >0 de façon continue (ou grimpe à des dizaines/centaines), ce n’est pas “un peu de lenteur”, c’est de la saturation. Et c’est exactement le scénario qui mène à des 503 côté utilisateur ou reverse proxy. Pour la démarche de diagnostic 503 (logs, limites, timeouts), recoupez avec Diagnostiquer une erreur HTTP 503 : logs, ressources, limites.

Grille de lecture simple (pratique pour décider où agir) :

  • WaitQ ↑ + Busy Workers LSAPI ↑ (proche du max) : saturation PHP “pure” (workers tous occupés). Actions : accélérer le temps de service (OPcache, SQL, modules) ou augmenter la capacité sans swap (RAM/CPU/workers).
  • WaitQ ↑ + Busy Workers LSAPI bas : souvent une limite ailleurs (max connections, throttling, limites LiteSpeed/vhost, backlog, voire un goulot I/O qui empêche LSAPI de tourner correctement). Actions : vérifier limites serveur, erreurs, iowait, files descriptors.
  • WaitQ en dents de scie corrélées à des tâches admin : contention front/back-office. Actions : isoler admin, limiter, asynchroniser.

Le pattern classique en prod :

  1. Dégradation SQL (requêtes lentes) ou cache applicatif mal configuré → chaque requête PHP dure plus longtemps.
  2. Les workers LSAPI se bloquent plus longtemps → moins de throughput.
  3. WaitQ monte (file) → TTFB explose, même si “le CPU n’est pas à 100%”.

D’où l’intérêt de corréler WaitQ avec les slow queries (Requêtes MySQL lentes : activer le slow query log) et avec un objectif TTFB réaliste (TTFB PrestaShop : réduire le time-to-first-byte sous 200 ms). Une file d’attente côté LSWS est souvent le symptôme visible d’un temps de service qui a dérivé (DB, I/O disque, appels externes, modules).

Runbook “Performances PHP LiteSpeed” : mesurer, corriger, vérifier (sans casser la prod)

Pré-requis : accès admin LiteSpeed (ou OpenLiteSpeed), accès SSH root/sudo, capacité à recharger LiteSpeed/LSAPI proprement, et idéalement un environnement de staging. Risque principal : une mauvaise valeur (trop de workers, OPcache trop gros, ou reload mal géré) peut provoquer swap, OOM killer, ou indisponibilité. Si vous n’avez pas de runbook de rollback, commencez par ça (voir aussi la logique de monitoring/alerting côté PrestaShop : PrestaShop performance monitoring, tests de charge et runbooks et la centralisation des logs/alertes : Centralisation des logs et alertes).

Étape 1 — Baseline chiffrée (avant changement).

  • Relevez dans LiteSpeed : WaitQ, Busy Workers, Req/Sec en période représentative.
  • Sur l’OS : load average, iowait, mémoire, swap (vmstat 1, sar -q, free -m).
  • Côté PHP : stats OPcache (hit rate, memory usage). Si vous ne les exposez pas déjà, un endpoint admin-only via opcache_get_status(false) est souvent plus fiable qu’un phpinfo() public.
  • Côté DB : slow query log activé et analysé.

Ajoutez une mini-cible (SLO interne) pour cadrer l’analyse : par exemple, “WaitQ ~0 hors pics” et “p95 TTFB stable sur les pages non cacheées”. Sans objectif, on “tune” indéfiniment.

Objectif : pouvoir dire “après changement, WaitQ moyen a baissé de X, p95 TTFB de Y, CPU de Z” plutôt que “ça a l’air mieux”. Pour une méthode d’audit reproductible, voir : Audit performance PrestaShop : méthode en 6 étapes reproductibles.

Étape 2 — Fixer OPcache en premier (c’est le gain le plus propre).

  • Augmentez opcache.memory_consumption et opcache.max_accelerated_files si vous observez saturation/évictions.
  • Stabilisez revalidate_freq selon votre stratégie de déploiement.
  • Vérifiez que vous n’avez pas d’OPcache “par vhost” incohérent (mauvais php.ini chargé, plusieurs SAPIs, etc.).

Après changement, attendez une phase de “warm-up” : OPcache doit se remplir. Sur PrestaShop, un warm-up minimal consiste à naviguer automatiquement sur les routes principales (home, catégorie, produit, recherche, panier, checkout, admin login) et à déclencher un cache template si nécessaire. Ne confondez pas warm-up OPcache et warm-up Varnish/CDN : ce sont deux couches distinctes.

Étape 3 — Dimensionner LSAPI sur la RAM (puis limiter).

  • Mesurez RSS moyen d’un worker en charge.
  • Fixez le nombre de workers max pour éviter swap.
  • Ajoutez un recyclage (max_requests) si vous voyez une dérive mémoire.

Symptôme de “trop de workers” : CPU qui monte, load average élevé, latences irrégulières, et au final WaitQ qui ne baisse pas (vous avez juste augmenté la contention). Symptomatique sur PrestaShop quand MySQL et PHP se battent pour le cache disque/CPU.

Étape 4 — Suivre WaitQ comme un SLO (pas comme un graphe décoratif).

  • Alertez si WaitQ > 0 de manière continue sur N minutes.
  • Alertez si WaitQ dépasse un seuil (ex. > 20) même sur peu de temps (signe de saturation brutale).
  • Corrélez avec p95/p99 TTFB et les 503.

Si vous avez déjà une stack Prometheus/Grafana, vous pouvez industrialiser cette corrélation (voir l’approche monitoring : Surveillance PrestaShop : tableau de bord et seuils). Sinon, Netdata est souvent un compromis rapide pour capturer iowait/CPU/mémoire et visualiser les dégradations (Netdata monitoring).

Étape 5 — Traiter la cause racine côté appli (souvent SQL ou modules).

Quand WaitQ est haut, la tentation est d’ajouter des workers. Ça marche… jusqu’au moment où MySQL sature, et vous déplacez le problème. Sur PrestaShop, les gains “durables” viennent fréquemment de :

Un bon test de “cause racine” : si vous améliorez une requête lente (ou désactivez un module coûteux) et que WaitQ baisse immédiatement à débit identique, vous venez de réduire le temps de service moyen : c’est exactement le levier qui stabilise un shop en pic.

À partir du moment où votre temps de service PHP baisse (p95), WaitQ retombe naturellement. C’est le signal que votre tuning LiteSpeed/LSAPI/OPcache sert à quelque chose : vous avez augmenté la capacité utile, pas juste empilé des processus.


À lire aussi