~/textes
Textes
Des notes sur le temps, l'audio, les systèmes embarqués, et le débogage. 18 textes.
Remettre les basses qui n'ont jamais été enregistrées
J'ai préparé une sélection musicale pour un système maison de 1500 watts et synthétisé une octave de sous-graves manquante dans des pistes coupées sous les 40 Hz. Mesurer d'abord, corriger seulement ce que les chiffres justifient, ne toucher à rien d'autre.
Qu'est-ce qui met ces octets sur le fil?
L'interface de stockage d'un cluster en production tirait de 4 à 12 fois plus que l'interface publique où arrivent les requêtes clientes. Arriver à un chiffre défendable voulait dire se méfier de tout, y compris de mon propre profileur, qui se trompait d'un facteur 30.
Une optimisation Dovecot que le compilateur effaçait depuis le début
Une commande IMAP THREAD provoquait une panique dans le code de tableaux de Dovecot. Le plantage était bien réel, mais le commentaire juste au-dessus décrivait une optimisation qui n'avait jamais tourné dans aucun build -O2 livré. ATTR_PURE l'avait transformée en code mort, et la même annotation décidait quelle ligne apparaissait dans la trace.
Un cluster Ceph que je jette à chaque redémarrage
Je voulais un vrai point d'accès Ceph (S3, CephFS, RBD) pour tester du code applicatif, sans cluster k8s ni un seul octet qui touche mon SSD. Un conteneur, ~30 secondes, tout en RAM. C'est le balayage des versions qui est devenu intéressant.
Le stratum 1 à 20 piasses que chrony mettait sur le banc
J'ai bâti un serveur NTP discipliné au GPS sur un ESP32 à 20 piasses, avec un horodatage interne à la nanoseconde. Puis chrony a refusé de s'en servir. Le correctif n'était pas dans l'horloge. Il était dans tout ce que j'avais tenu pour acquis sur la façon de servir l'heure.
On ne peut pas faire la moyenne des percentiles
Trouver la vraie capacité d'un serveur IMAP veut dire générer de la charge à partir de plusieurs hôtes en même temps. Le difficile, c'est pas de produire la charge. C'est de combiner leurs latences en un seul P99 honnête sans se mentir en douce.
L'horloge gelée que personne n'a remarquée
Un serveur NTP discipliné par GPS a cessé d'être discipliné, et chaque moniteur restait au vert. L'indice, c'était une valeur freq_ppb qui se répétait octet pour octet sur trois secondes, ce qu'un servo vivant ne fait jamais.
130 images par seconde, dérivées d'un fil
J'ai bâti un démon Go qui pilote un visualiseur de musique sur un Pi, puis j'ai découvert que le débit d'images n'était pas à moi de choisir. Le protocole LED l'avait déjà décidé. Le vrai travail, c'était un budget de latence que je devais mesurer avant que les lumières paraissent verrouillées sur la musique.
ts2phc et gpsd ne peuvent pas partager un port série
Un seul port série USB, deux programmes qui veulent tous les deux un accès exclusif. ts2phc veut uniquement du RMC pour discipliner l'horloge PTP; gpsd veut tout le reste. J'ai écrit un démultiplexeur qui donne à chacun un port synthétique façonné exactement comme il l'attend.
Les cœurs de réserve d'un BeagleBone gardent l'heure mieux que son noyau
Le pilote PPS standard de Linux horodate l'impulsion GPS dans un gestionnaire d'interruption et paye pour ça ~20 µs de gigue. Le BeagleBone a deux cœurs temps réel à 200 MHz que Linux ne touche jamais. J'ai déplacé l'horodatage dans un de ces cœurs-là, et le décalage de l'horloge est tombé dans les bas nanosecondes.
Tidal a cessé de servir du lossless, alors j'ai lu le jeton
Le plugin amont s'est mis à tirer du 320 kbps au lieu du FLAC lossless, et changer le client ID n'a rien donné. Le jeton d'accès avait figé ses droits au moment de l'émission, bien avant qu'aucune requête ne parte.
Le jeu qui redémarrait l'hyperviseur
La VM de jeu d'un ami forçait un redémarrage à froid de son hôte Proxmox dès qu'un jeu se lançait, sans rien dans les journaux. La cause se trouvait sous chaque couche qu'on n'arrêtait pas de modifier : un transitoire de puissance au lancement du jeu qui déclenchait une faute PCIe et réinitialisait la machine.
Le contrôleur de fournaise qui se figeait quand le WiFi tombait
Un contrôleur de fournaise sur RP2040 se figeait au hasard, toujours quand le WiFi tombait. Le coupable était une sonde de connectivité qui ouvrait une connexion TCP pour décider si elle pouvait en ouvrir une. Un délai d'expiration l'aurait masquée; retirer la sonde a réglé le problème.
Le GPS qui répond 0xFF quand il n'a rien à dire
Lire un GPS u-blox par I2C libère l'UART, mais l'interface I2C n'a aucune notion de repos. Quand le module n'a rien à envoyer, chaque lecture revient en remplissage 0xFF. Voici le petit pont qui transforme ça en refclock pour chrony.
Un seul socket pour tout le sous-réseau
Un scanner d'hôtes qui ouvrait un socket brut et une goroutine par hôte s'écroulait sur tout ce qui dépassait un /24. La réécriture utilise un seul socket et deux goroutines peu importe le nombre d'hôtes, avec un chemin rapide ARP et un repli sans privilèges.
Ma station météo ment au sujet du soleil
Une station Ecowitt bon marché rapporte des valeurs UV et de rayonnement solaire qui lisent trop haut et qui sautillent. Le répéteur qui les transmet à Weather Underground lisse et met ces canaux à l'échelle d'abord, et ne laisse jamais une API lente en amont bloquer la station.
Indexer un registre de jetons on-chain en Rust
Une blockchain te remet un journal de transferts en ajout seul, sous trois formes différentes, derrière un RPC à débit limité qui réécrit discrètement sa propre histoire récente. Replier ça en un registre de soldes correct, c'est tout le travail, et la plupart des façons de se tromper ne se manifestent pas avant des semaines.
La version Python marchait. Je l'ai réécrite en Rust pareil.
Porter un indexeur de chaîne fonctionnel de Python vers Rust, là où les parties faciles deviennent plus dures et les parties floues se font attraper. La plupart de ce que la réécriture m'a rapporté, ce n'était pas de la vitesse, c'était des bogues qu'il devenait impossible d'écrire.