Portage de MariaDB sur IBM AIX : 3 semaines de souffrance technique

Partie 1 : Apporter MariaDB à la plateforme qui alimente les systèmes les plus critiques du monde

Dans la vie, il y a des décisions que tu prends en sachant très bien qu’elles te feront souffrir. Se marier. Avoir des enfants. Courir un marathon. Porter MariaDB 11.8 sur IBM AIX.

Voici l’histoire de la dernière – et pourquoi je la referais sans hésiter.

Chapitre 1 : “C’est si dur que ça ?”

Tout a commencé par une question innocente lors d’une réunion d’équipe : “Pourquoi n’avons-nous pas MariaDB sur nos systèmes AIX ?”

Voici ce que les gens qui n’ont jamais travaillé avec AIX ne comprennent pas : AIX ne plaisante pas. Lorsque les banques ont besoin d’un temps de disponibilité de cinq neuf pour leurs systèmes bancaires de base, elles utilisent AIX. Lorsque les compagnies aériennes ont besoin de systèmes de réservation qui ne peuvent pas tomber en panne, elles utilisent AIX. Quand Oracle, Informix ou DB2 doivent fournir des performances absolument brutales pour des charges de travail OLTP critiques, ils utilisent AIX.

AIX n’est pas à la mode. AIX n’a pas de mascotte cool. AIX ne fera pas l’objet d’articles de blogs technologiques essoufflés sur la “perturbation”. Mais lorsque les choses ne peuvent absolument pas échouer, AIX est là, faisant tranquillement son travail pendant que tous les autres sont occupés à redémarrer leurs conteneurs.

Alors pourquoi MariaDB ne prend-elle pas officiellement en charge AIX ? L’économie est simple : la communauté open source s’est concentrée sur Linux, et le portage nécessite une expertise spécifique à la plateforme. MariaDB prend officiellement en charge Linux, Windows, FreeBSD, macOS et Solaris. AIX ne figure pas sur la liste – non pas parce que c’est une mauvaise plate-forme, mais parce que personne n’a encore fait le travail.

Chez LibrePower, c’est exactement ce que nous faisons.

Ma première erreur a été de dire tout haut : “Il suffit sans doute de le compiler et d’ajuster quelques éléments”.

Leçon n° 1: Lorsque quelqu’un dit ” compile-le simplement ” à propos d’un logiciel sur AIX, il est sur le point d’en apprendre beaucoup sur la programmation des systèmes.

Chapitre 2 : CMake et les trois invités inattendus

Le premier jour de la compilation a été… éducatif. CMake sur AIX, c’est comme jouer aux cartes avec quelqu’un qui a une compréhension très différente des règles – et qui s’attend à ce que tu les découvres toi-même.

Le bogue de la fonction fantôme

AIX a une caractéristique intéressante : il déclare des fonctions dans les en-têtes pour des raisons de compatibilité, même si ces fonctions n’existent pas réellement au moment de l’exécution. C’est comme si ton GPS te disait “tourne à droite dans 200 mètres” mais que la rue était un mur de briques.

CMake fait un CHECK_C_SOURCE_COMPILES pour tester si pthread_threadid_np() existe. Le code se compile. CMake dit “super, on l’a !” Le binaire démarre et… BOOM. Symbole non trouvé.

Il s’avère que pthread_threadid_np() est réservé à macOS. AIX le déclare dans les en-têtes parce que… eh bien, je ne suis pas encore tout à fait sûr. Peut-être pour une raison de compatibilité POSIX qui avait un sens il y a des décennies ? Quelle que soit la raison, GCC le compile sans problème et l’éditeur de liens ne se plaint pas jusqu’à l’exécution.

Même chose avec getthrid(), qui est spécifique à OpenBSD.

La solution:

IF(NOT CMAKE_SYSTEM_NAME MATCHES "AIX")
  CHECK_C_SOURCE_COMPILES("..." HAVE_PTHREAD_THREADID_NP)
ELSE()
  SET(HAVE_PTHREAD_THREADID_NP 0)  # Trust but verify... okay, just verify
ENDIF()

poll.h : Cache-cache

AIX a <sys/poll.h>. C’est juste là. Tu peux cat. Mais CMake ne le détecte pas.

Après trois heures de débogage d’une erreur “POLLIN undeclared” dans viosocket.c, j’ai découvert que la solution consistait simplement à forcer la définition :

cmake ... -DHAVE_SYS_POLL_H=1

Trois heures. Pour un drapeau.

(Pour être honnête, il s’agit d’un problème de détection de plate-forme CMake, et non d’un problème AIX. Les vérifications de CMake supposent des dispositions d’en-tête de type Linux).

Les plugins maudits

À 98 % de la compilation – 98 % ! – le plugin wsrep_info a explosé avec des symboles non définis. Parce qu’il dépend de Galera. Que nous n’utilisons pas. Mais CMake le compile quand même.

Egalement S3 (nécessite les symboles Aria), Mroonga (nécessite Groonga), et RocksDB (profondément lié aux optimisations spécifiques à Linux).

Configuration finale de CMake :

-DPLUGIN_MROONGA=NO -DPLUGIN_ROCKSDB=NO -DPLUGIN_SPIDER=NO 
-DPLUGIN_TOKUDB=NO -DPLUGIN_OQGRAPH=NO -DPLUGIN_S3=NO -DPLUGIN_WSREP_INFO=NO

Cela ressemble à une amputation chirurgicale, mais il s’agit en fait de tailler dans le gras. Ces plugins sont des cas limites dont peu de déploiements ont besoin.

Chapitre 3 : Thread Pool, ou comment j’ai appris à arrêter de m’inquiéter et à aimer le Mutex

C’est là que les choses sont devenues intéressantes. Et par “intéressant”, je veux dire “j’ai failli me donner un tic permanent”.

MariaDB a deux modes de gestion des connexions :

  • une-filière-par-connexion: Un thread par client. Simple. S’adapte comme une voiture qui monte une côte.
  • pool de threads: Un pool fixe de threads gère toutes les connexions. Élégant. Efficace. Et non disponible sur AIX.

Pourquoi ? Parce que le pool de threads nécessite des API de multiplexage d’E/S spécifiques à la plateforme :

PlateformeAPIStatut
LinuxepollPris en charge
FreeBSD/macOSkqueuePris en charge
Solarisports d’événementsPris en charge
WindowsIOCPPris en charge
AIXpollsetNon pris en charge (jusqu’à présent)

Alors… à quel point la mise en œuvre de la prise en charge des jeux de vote peut-elle être difficile ?

(Note de la rédaction : à ce stade, l’auteur a besoin d’une pause de 20 minutes et d’une boisson).

Le problème ONESHOT

Linux epoll possède un merveilleux drapeau appelé EPOLLONESHOT. Il garantit qu’un descripteur de fichier ne déclenche des événements qu’une seule fois jusqu’à ce que tu le réarmes explicitement. Cela empêche deux threads de traiter la même connexion simultanément.

Le pollset AIX est déclenché par niveau. Uniquement déclenché par niveau. Pas d’options. Si des données sont disponibles, il les signale. Encore et encore et encore. Comme un collègue serviable qui te rappelle sans cesse ce courriel auquel tu n’as pas encore répondu.

Onze versions de la sagesse croissante

Il s’en est suivi onze itérations de code, toutes plus élaborées les unes que les autres, pour essayer de simuler le comportement de ONESHOT :

v1-v5 (L’âge de l’innocence)

J’ai essayé de modifier les drapeaux d’événements avec PS_MOD. “Si je mets l’événement à 0, il ne se déclenchera plus”, me suis-je dit. Spoiler : il n’a pas cessé de se déclencher.

v6-v7 (L’ère des machines à états)

“Je sais ! Je vais maintenir l’état interne et filtrer les événements en double.” Le problème : il y a une fenêtre de temps entre le moment où le noyau te donne l’événement et celui où tu mets à jour ton état. Dans cette fenêtre, un autre thread peut recevoir le même événement.

v8-v9 (La phase de déni)

“Je vais mettre l’état à PENDANT avant de traiter”. Ça a marché… en quelque sorte… jusqu’à ce que ça ne marche plus.

v10 (Espoir)

J’ai enfin trouvé la solution : PS_DELETE + PS_ADD. Lorsque tu reçois un événement, supprime immédiatement le fd du pollset. Lorsque tu es prêt à recevoir d’autres données, ajoute-le à nouveau.

// On receiving events: REMOVE
for (i = 0; i < ret; i++) {
    pctl.cmd = PS_DELETE;
    pctl.fd = native_events[i].fd;
    pollset_ctl(pollfd, &pctl, 1);
}

// When ready: ADD
pce.command = PS_ADD;
pollset_ctl_ext(pollfd, &pce, 1);

Ça a marché ! Avec -O2.

Avec -O3segfault.

La nuit noire de l’âme (Le bug -O3)

Imagine mon visage. J’ai un code qui fonctionne parfaitement avec -O2. J’active -O3 pour les benchmarks de production et le serveur se plante avec “Got packets out of order” ou un segfault dans CONNECT::create_thd().

J’ai passé deux jours à penser qu’il s’agissait d’un bug du compilateur. GCC 13.3.0 sur AIX. J’ai accusé le compilateur. J’ai blâmé l’éditeur de liens. J’ai tout blâmé sauf mon propre code.

Le problème était plus subtil : MariaDB a deux chemins de code concurrents qui appellent io_poll_wait sur le même pollset :

  • Les blocs d’écoute avec timeout=-1
  • Le travailleur fait appel à timeout=0 pour les vérifications non bloquantes.

Avec -O2, la synchronisation était telle que les collisions étaient rares. Avec -O3, le code était plus rapide, les collisions se produisaient plus souvent, et boom – race condition.

v11 (L’illumination)

La solution consistait à créer un mutex dédié protégeant à la fois pollset_poll et toutes les opérations de pollset_ctl:

static pthread_mutex_t pollset_mutex = PTHREAD_MUTEX_INITIALIZER;

int io_poll_wait(...) {
    pthread_mutex_lock(&pollset_mutex);
    ret = pollset_poll(pollfd, native_events, max_events, timeout);
    // ... process and delete events ...
    pthread_mutex_unlock(&pollset_mutex);
}

Oui, il sérialise l’accès au pollset. Oui, c’est théoriquement plus lent. Mais tu sais ce qui est encore plus lent ? Un serveur qui tombe en panne.

Le code final de la v11 a passé 72 heures de tests de résistance avec 1 000 connexions simultanées. Aucun plantage. Aucune fuite de mémoire. Aucun “paquet hors service”.

Chapitre 4 : La chose -blibpath (en fait une caractéristique)

Une véritable caractéristique d’AIX : tu dois spécifier explicitement le chemin de la bibliothèque au moment de la liaison avec -Wl,-blibpath:/your/path. Si tu ne le fais pas, le binaire ne trouvera pas libstdc++ même s’il se trouve dans le même répertoire.

Au début, cela te semble ennuyeux. Puis tu réalises : AIX préfère les chemins explicites et déterministes aux recherches implicites. Dans les environnements de production où “ça a marché sur ma machine” n’est pas acceptable, c’est une caractéristique, pas un bogue.

Chapitre 5 : Stabilité – Les chiffres qui comptent

Après tout ce travail, où en sommes-nous ?

Le RPM est publié sur aix.librepower.org et déployé sur un système IBM POWER9 (12 cœurs, SMT-8). MariaDB 11.8.5 fonctionne sur AIX 7.3 avec le pool de threads activé. Le serveur a passé avec succès une suite d’assurance qualité brutale :

TestRésultat
100 connexions simultanées
500 connexions simultanées
1 000 connexions
30 minutes de charge soutenue
11+ millions de requêtes
Fuites de mémoireZÉRO

1 648 482 400 octets de mémoire – constants pendant 30 minutes. Pas un seul octet de dérive. Le serveur a fonctionné pendant 39 minutes en charge continue et s’est éteint proprement.

Il fonctionne. Il est stable. Il est prêt à être mis en production.

Impact du pool de fils

Le travail sur le pool de threads a permis des gains massifs pour les charges de travail simultanées :

ConfigurationMélange de 100 clientspar rapport à la ligne de base
Original -O2 one-thread-per-connection11.34s
-O3 + pool-of-threads v111.96s83% plus rapide

Pour les charges de travail OLTP à forte concordance, c’est la différence entre “lutter” et “voler”.

Ce que j’ai appris (jusqu’à présent)

  1. CMake suppose qu’il s’agit de Linux. Sur les systèmes non-Linux, vérifie manuellement que la détection des fonctionnalités est correcte. Les faux positifs te feront mal au moment de l’exécution.
  2. Les entrées/sorties déclenchées par niveau nécessitent de la discipline. EPOLLONESHOT existe pour une raison. Si ton système ne l’a pas, prépare-toi à mettre en œuvre ta propre sérialisation.
  3. -O3 expose les bogues latents. Si ton code “fonctionne avec -O2 mais pas avec -O3”, tu as une condition de course. Le compilateur fait son travail ; le bogue est le tien.
  4. Les mutex sont tes amis. Oui, elles ont des frais généraux. Mais tu sais ce qui est le plus coûteux ? Déboguer les conditions de course à 3 heures du matin.
  5. AIX récompense la compréhension profonde. C’est un système qui ne pardonne pas les raccourcis, mais une fois que tu as compris ses conventions, il est prévisible et robuste. Ce n’est pas pour rien que les banques l’utilisent encore – et qu’elles continueront à le faire dans un avenir prévisible.
  6. L’écosystème est important. Des projets comme linux-compat de LibrePower rendent le développement moderne viable sur AIX. Contribuer à cet écosystème profite à tout le monde.

Qu’est-ce qu’on fait maintenant ? La question de la performance

Le serveur est stable. Le pool de discussion fonctionne. Mais il y a une question en suspens à laquelle je n’ai pas encore répondu :

Quelle est sa rapidité par rapport à Linux ?

J’ai effectué un test de recherche vectorielle – le type d’opération qui permet d’améliorer les fonctions de recherche de l’IA. Index MHNSW (Hierarchical Navigable Small World) de MariaDB, 100 000 vecteurs, 768 dimensions.

Linux sur du matériel POWER9 identique : 971 requêtes par seconde.

AIX avec notre nouvelle version : 42 requêtes par seconde.

Vingt-trois fois plus lent.

Mon cœur s’est effondré. Trois semaines de travail, et nous sommes 23x plus lents que Linux ? Sur un matériel identique?

Mais voici ce qu’il en est de l’ingénierie : lorsque les chiffres n’ont pas de sens, il y a toujours une raison. Et parfois, cette raison s’avère être une bonne nouvelle surprenante.

Dans la deuxième partie, je couvrirai :

  • Comment nous avons découvert que l’écart de 23x était surtout une erreur de configuration.
  • Le compilateur qui a tout changé
  • Pourquoi “AIX est lent” s’est avéré être un mythe
  • Le “musée des échecs” complet des optimisations qui n’ont pas fonctionné.

Les RPM sont publiés sur aix.librepower.org. La version GCC est stable et prête pour la production.

Mais l’histoire de la performance ? C’est là que les choses deviennent vraiment intéressantes.

La deuxième partie arrive bientôt.

TL;DR

  • MariaDB 11.8.5 fonctionne maintenant sur AIX 7.3 avec le pool de threads activé
  • Première mise en œuvre d’un pool de threads pour AIX à l’aide d’un pollset (11 itérations pour obtenir une simulation ONESHOT correcte).
  • Le serveur est stable: 1 000 connexions, plus de 11 millions de requêtes, aucune fuite de mémoire.
  • Le pool de threads offre une amélioration de 83 % pour les charges de travail simultanées
  • Le premier test de recherche vectorielle montre un écart de 23x par rapport à Linux – mais est-ce que c’est tout ?
  • RPMs publiés sur aix.librepower.org
  • La deuxième partie sera bientôt disponible: L’enquête sur les performances

Des questions ? Des idées ? Tu veux contribuer à l’écosystème open source AIX ?

Ce travail fait partie de LibrePower – Open source software for IBM Power Systems.

Dépôt du projet AIX : gitlab.com/librepower/aix

SIXE