15 commentaires
Intéressant. io_uring + PostgreSQL sur NVMe devrait voler. Si iostat est clean, ça sent le kernel ou le scheduler CPU.
Tu utilises IORING_SETUP_SQPOLL ? Et kernel sched_debug ou perf sched montrent quoi pendant ces pics ?
J'irais regarder le run queue latency avec bpftrace. C'est possible que les threads PostgreSQL se fassent préempter par le scheduler même si l'utilisation globale CPU est basse.
Genre un bpftrace -e 'kprobe:finish_task_switch { @runqlat[comm] = hist(ktime - @start[prev_comm]) } kprobe:schedule { @start[prev_comm] = ktime }' pour voir.
Oui, IORING_SETUP_SQPOLL est activé, et les threads sqpoll sont bindés sur des cores isolés via isolcpus. Le kernel est 6.6.10.
J'ai lancé le bpftrace pendant le test. J'ai des histogrammes bizarres pour postgres :
@runqlat[postgres]:
[4ms, 8ms) @ 32
[8ms, 16ms) @ 15
[16ms, 32ms) @ 8
[32ms, 64ms) @ 2Normalement ça devrait être beaucoup plus bas non ? Genre sous le milliseconde.
Clairement. Des latences de 32-64ms dans la run queue c'est énorme pour PostgreSQL surtout avec des WALs. Ça indique une contention CPU, même si l'utilisation globale est basse.
Quelles sont tes options de boot kernel ? transparent_hugepage est en always ? Quid des C-states et P-states ?
Ah oui les C-states agressifs peuvent poser problème. Les CPUs se mettent en veille profonde trop vite et le temps de réveil ajoute de la latence.
Essayez de désactiver THP (transparent_hugepage=never) et limiter les C-states profonds (processor.max_cstate=1 ou intel_idle.max_cstate=1 si Intel) dans les paramètres du GRUB.
Ok j'ai vérifié : transparent_hugepage est bien sur always. Les C-states sont par défaut donc ça va jusqu'à C6.
J'applique les changements dans GRUB_CMDLINE_LINUX_DEFAULT :
transparent_hugepage=never processor.max_cstate=1 intel_idle.max_cstate=1Je reboot le serveur et relance le test.
Pense aussi à swappiness (vm.swappiness). Si c'est trop haut et que tu tapes un peu dans la mémoire, le kernel peut commencer à swapper des pages alors que tu as de la RAM libre, juste pour libérer du cache disk.
Mets-le à 1 ou 5 max pour PostgreSQL sur un serveur dédié.
Et si ça persiste, regarde si tu n'as pas un cgroup ou systemd unit qui limite les CPU shares ou le quota pour le processus PostgreSQL.
Ça m'est arrivé qu'une règle de cgroup mal faite limite un service sans que ce soit évident, et PostgreSQL souffrait de micro-stalls.
Bon j'ai rebooté avec transparent_hugepage=never, processor.max_cstate=1, intel_idle.max_cstate=1 et vm.swappiness=1. Les résultats du bpftrace runqlat sont bien meilleurs, on est majoritairement sous le milliseconde.
Cependant, les pics de latence fsync ne sont pas totalement partis. Ils sont moins fréquents et moins hauts (max 200ms) mais encore présents. Les IOPS et CPU restent bas.
Ok, donc le scheduler est moins un problème. On a écarté les C-states et THP.
Le problème pourrait être lié à NUMA et la distribution des interrupts des NVMe. Tes NVMe sont sur quels PCIe lanes par rapport aux sockets CPU ? Est-ce que irqbalance est actif et bien configuré ?
J'ai fouillé dmesg, aucun NVMe reset ou erreur. irqbalance est actif et semble bien distribuer les IRQs des NVMe.
En fait je viens de checker les cgroups plus en détail. On a un systemd unit pour un microservice critique qui utilise :
CPUQuota=90%
CPUAccounting=yesCe service a des bursts d'activité. Il est dans le même slice systemd que PostgreSQL.
Bingo ! CPUQuota à 90% pour un service critique dans le même slice que PostgreSQL ça peut être mortel.
Quand ce service fait son burst, il peut très bien accaparer 90% du temps CPU disponible pour le slice, et PostgreSQL se retrouve avec les miettes, même si le CPU global est bas. Les latences que tu vois sont dues à la starvation CPU des threads PostgreSQL.
Exactement. CPUQuota est une limite absolue. Si ce microservice en a besoin, il prend. Le scheduler va ensuite attribuer ce qui reste aux autres. Pour PostgreSQL qui est très sensible à la latence, c'est un tueur.
Le mieux est de mettre PostgreSQL dans un cgroup dédié avec des CPU shares élevées ou même de l'CPUQuota si tu veux lui garantir une part minimale.
C'était ça. J'ai déplacé le microservice dans un slice systemd séparé avec son propre CPUQuota.
Depuis on a relancé le test de charge. Les latences fsync sont maintenant stables, majoritairement sous les 5ms, et le bpftrace runqlat est nickel.
Merci beaucoup à vous deux pour avoir creusé ça avec moi. C'était un mélange de kernel tuning et de cgroup qui m'a fait tourner en bourrique !
Laisser une réponse
Vous devez être connecté pour poster un message !
Hello
On a migré notre
PostgreSQL 15sur une nouvelle infra bare metal, avec des NVMe sur PCIe Gen4 et un kernel 6.6. On a activéio_uringpour lesWALsen modefdatasync.Le souci c'est que sous forte charge d'écriture (genre 20k TPS), on voit des pics de latence
fsyncqui montent à 500ms-1s, alors que les disques ont l'air sous-utilisés d'aprèsiostat(queue depth basse, %util < 30%). LeCPUdu serveur n'est pas saturé non plus.Quelqu'un a déjà vu ce genre de comportement avec
io_uringetPostgreSQL?