Journal Il était une fois… la procrastination

Posté par  (site Web personnel) . Licence CC By‑SA.
Étiquettes :
82
31
mai
2020

(TLDR: c’est l’histoire d’un geek qui veut écrire un commentaire et, une chose en amenant une autre, il doit écrire du code, de la doc, déboguer, modifier les données en production, écrire un journal et discuter procrastination.)

Étape 1, où une mémoire ressurgit : il était une fois un journal sur Window Maker 0.95.9. Cela faisait longtemps que le sujet Window Maker n’avait pas été abordé sur LinuxFr.org, et ceci me rappelle des souvenirs lointains de modération multiple des versions de Window Maker.

Étape 2, où l’étiquette n’est pas respectée : logiquement, il suffirait que je clique sur l'étiquette WindowMaker pour retrouver l’information que je recherche. Ah… apparemment tous les contenus ne sont pas étiquetés. Alors il suffirait que je me lance dans une recherche manuelle direct en base de données sur les journaux et les dépêches pour rafraîchir ma mémoire.

Étape 3, qui commence avec une simple recherche en base : il y a 15 journaux et 25 dépêches qui contiennent Window et Maker dans leur titre, cela va donc être rapide. Par contre, le titre ne mentionne pas forcément la version. Mais en triant par date, ça devrait suffire pour retrouver les annonces multiples de versions. En même temps, quitte à voir les 40 contenus, autant vérifier qu’ils sont bien étiquetés.

Étape 4, où je me lance dans l’étiquetage : j’ouvre les 40 contenus pour vérifier qu’ils sont bien étiquetés ; je pourrais vérifier en base directement, mais bon j’avais besoin de retrouver les dépêches portant sur la même version initialement, et puis 40 contenus ça ira vite (pensais-je). Tiens je tombe sur un lien est en erreur.

Étape 5, où je tombe sur une dépêche en erreur : bizarre, le lien (pas terrible d’ailleurs /news/windowmaker comme adresse) vers la dépêche renvoie un code d’erreur HTTP 500. Retrouvons son numéro en base de données en cherchant par rapport à son adresse. Hein ? Deux résultats, comment ça deux résultats sur une adresse unique ? Visiblement il s’agit d’une dépêche acceptée et une dépêche rejetée, en collision ? OK, il suffit de changer l’adresse de la dépêche rejetée, de toute façon personne ne peut y accéder. Heureusement que c’est un cas unique.

SELECT id, cached_slug, state, created_at FROM news WHERE cached_slug='windowmaker';
+-------+-------------+-----------+---------------------+
| id    | cached_slug | state     | created_at          |
+-------+-------------+-----------+---------------------+
| 31527 | windowmaker | published | 2000-03-31 16:48:49 |
| 15470 | windowmaker | refused   | 2004-02-18 07:42:06 |
+-------+-------------+-----------+---------------------+

Étape 6, où je vérifie que c’est un cas unique : il s’avère finalement qu’après ma première correction, il reste encore 60 cas de collision sur l’identifiant unique, le slug, avec 18 collisions pour le premier cas, 5 pour le second cas, et ensuite deux pour les 58 cas restants. Crotte, zut, flûte, saperlipopette. Bon il faut donc corriger ça aussi. Heureusement que c’est limité aux dépêches.

Étape 7, où je vérifie les autres tables : ok l’unicité est assurée ailleurs. Sauf pour deux chemins de comptes utilisateurs. Et quatre chemins de journaux (même titre et même compte donc). Et dix-huit chemins d’entrées de forums. Snif. Je voulais juste écrire un commentaire moi, initialement…

Étape 8, où il faudrait revoir le schéma de la base de données :

Les créations d’index en base de données suivant le schéma SQL :

  • il n’y a pas d’unicité imposée niveau SQL pour les chemins basés sur deux champs différents, comme les liens /users/<user>/liens/<titre>, les journaux /users/<user>/journaux/<titre> et les entrées de forums /forums/<forum>/posts/<titre>.
  • et il n’y en a pas non plus pour les chemins basés sur un seul champ, comme /forums/<forum>, /sections/<section>, /sondages/<sondage>, /suivi/<suivi>, /users/<user> et /wiki/<wikipage>. Ça serait probablement facile à faire pourtant.

Étape 9, où je m’interroge sur le script de vérification de cohérence de la base de données : pourtant normalement on a deux scripts qui vérifient périodiquement l’état des bases MariaDB (SQL) et Redis. Sauf qu’ils vérifient que tous les slug existants correspondent à des données (pour détecter des slugs inutiles) et pas qu’ils sont globalement uniques (un même contenu peut avoir plusieurs slugs, si son titre a changé par exemple, mais on aurait voulu éviter que plusieurs contenus puissent avoir le même slug puisque ça donnerait une collision).

On récapitule : je dois corriger un script, faire une migration du schéma des données, nettoyer les infos incorrectes en base de données, écrire mon commentaire initial, et en plus maintenant je me dis que je devrais écrire un journal pour raconter tout ça. Comment j’ai réussi à me générer autant de travail moi ?

Je commence à écrire et tester ma série de requêtes type

SELECT cached_slug, COUNT(*) AS cnt, 'forums with same slug' FROM forums GROUP BY cached_slug HAVING cnt > 1;

et je redécouvre que c’est en fait plus compliqué que cela. Redécouvre parce que j’aurais dû documenter mieux ce schéma, tiens ça ferait une tâche de plus.

Étape 10, où je réalise que c’est plus compliqué : en fait le slug stocké dans chaque table est le slug utilisé par défaut, mais il en existe d’autres possibles, stockés dans une autre table, et du coup l’unicité globale est à la fois sur la table du contenu concerné (par exemple news) mais aussi sur toutes les entrées de type News de la table friendly_id_slugs. On est donc partie pour des requêtes SQL plus complexes.

On peut commencer à dépiler.

On peut compléter le script avec des requêtes du type

SELECT S.slug, COUNT(*) AS cnt, 'news with the same slug' FROM (SELECT id AS sluggable_id, cached_slug AS slug FROM news UNION SELECT sluggable_id, slug FROM friendly_id_slugs WHERE sluggable_type='News') AS S GROUP BY S.slug HAVING cnt > 1;

Et apprendre que désormais c’est 69 collisions pour les dépêches, 18 pour les entrées de forums, 6 pour les journaux et 2 pour les utilisateurs.

Pas besoin de modifier le schéma finalement

Suivront aussi de longues heures pour produire un diagramme du schéma SQL (avec draw.io) et pour finir par le commiter.

Les collisions sont des faux positifs (« vero/véro », « beatrice/béatrice ») sur des anciens comptes (il n’y a plus de caractères accentués normalement dans les slugs) ou des envois quasi simultanés ou des reliquats d’opération précédant la migration en Ruby on Rails de 2011.

Au passage je retrouve l’info que je cherchais initialement et je publie le commentaire initialement souhaité.

Je me lance dans la correction de tous les soucis dans les slugs de dépêches, notamment en supprimant de très anciennes dépêches supprimées (jamais publiées, jamais visibles et donc inutiles).

Ensuite je revérifie toutes les dépêches avec un petit curl, par acquit de conscience, tout en sachant que cela va bien se passer et que cela ne peut plus échouer (« tester c’est douter »). Deux dépêches sortent en HTTP 500… (bim ! étape 11, quand il n’y en a plus il y en a encore) Il s’avère qu’il s’agit de précédentes modifications manuelles et erronées en base de données (probablement suite à la suppression d’un ou plusieurs commentaires), sur l’arborescence des commentaires. J’en profite pour corriger aussi ces soucis-là aussi (histoire d’avoir des arborescences réellement composées de commentaires du même contenu).

Enfin j’ajoute des tests dans le script de vérification de la base de données SQL pour détecter les soucis sur les slugs et sur l’arborescence des commentaires. Et au passage un test de plus pour vérifier que le décompte de commentaires par contenu (un champ dénormalisé nodes.comments_count qui stocke localement le décompte des commentaires associés) est bien égal au nombre effectif de commentaires sur le contenu.

Il ne reste plus qu’à vérifier le contenu des bases SQL (MariaDB) et Redis avec les scripts, et à nettoyer ce qui dépasse (notamment depuis les suppressions des dépêches un peu plus haut qui ont laissé des résidus devenus inutiles côté Redis).

Ainsi qu’à finir de rédiger ce journal.

Et là on arrive à la fin de cette histoire dlfpienne (commencée le 10 mai).

Si je devais lui donner un titre, je l’appellerais procrastination.

  • # Remarques

    Posté par  . Évalué à 10 (+10/-1).

    Étape 8, où il faudrait revoir le schéma de la base de données :

    Ma première idée aurait plutôt était de corriger le code. Probablement la requête pour qu'il ne prenne que le contenu validé ou qu'il prenne tout tris par ordre de validation + date et prenne le premier. C'est beaucoup moins impactant comme modification. Sinon il faut aussi s'assurer de tous les usages (les écritures ne vont pas exploser par exemple).

    Étape 9, où je m’interroge sur le script de vérification de cohérence de la base de données[…]

    Je suis surpris de l'utilité de ce script pour une base de données SQL. L'un des grands intérêt d'SQL c'est qu'il donne tous les outils pour avoir des données cohérentes et sa force c'est que les données restent cohérentes, elles ne le sont pas qu'au moment on ton cron vient de passer.

    Et là on arrive à la fin de cette histoire dlfpienne (commencée le 10 mai).

    Pas mal même si tu n'en es pas à Donald Knuth qui pour écrire un livre à créé un TeX, Metafont, Computer Modern, MetaPost,… :)

    • [^] # Re: Remarques

      Posté par  (site Web personnel) . Évalué à 10 (+16/-0).

      Ma première idée aurait plutôt était de corriger le code.

      On est normalement dans une situation qui n'est pas censée arriver. Plutôt qui n'est plus censée arriver. Les soucis proviennent des données avant le passage en Ruby on Rails, ça relève de la dette technique. Je vais néanmoins regarder ce que je sais faire niveau code sur ce point.

      Je suis surpris de l'utilité de ce script pour une base de données SQL. L'un des grands intérêts d'SQL c'est qu'il donne tous les outils pour avoir des données cohérentes et sa force c'est que les données restent cohérentes, elles ne le sont pas qu'au moment où ton cron vient de passer.

      Vaste question. J'y vois de très nombreuses réponses :

      • cela suppose que les développeurs sont parfaits : ils ne font pas d'erreurs de conception, pas d'erreurs de développement, pas d'oublis, ils ont une maîtrise parfaite du SQL, ils gèrent tous les cas du passé, et ils pensent à faire toutes les adaptations nécessaires sur les contraintes à gérer ;
      • cela suppose que les outils sont parfaits : les bibliothèques de développement n'ont pas de bugs ou de lacunes, la base de données non plus (pour mémoire MySQL n'avait pas de clés étrangères à un moment, et MySQL gérait mal l'UTF-8 sur 4 octets à un moment, pour ne citer que deux exemples) ;
      • la confiance n'exclut pas le contrôle : ceinture et bretelles, ça ne coûte pas grand-chose d'avoir un script en plus, qui vérifie de façon différente certains points, en l'occurrence précisément les points que l'on ne garantit pas par la base de données elle-même. Et la duplication de vérification peut aussi être faite par des personnes différentes (par exemple le dév mettra des contraintes dans le code, le DBA dans la base SQL et l'adminsys dans un script shell externe) ;
      • certaines vérifications doivent être faites a posteriori : le script vérifie aussi l'absence de spammeurs non filtrés, basé sur les comportements de spammeurs déjà détectés, et en remontant dans le temps, au cas où l'on en aurait laissé passer, pour les virer avec retard ;
      • certaines vérifications sont pénibles à écrire en SQL : par exemple nodes.content_type contient News, Diary, etc., nodes.content_id contient l'identifiant qui correspond à ce que l'on trouvera respectivement dans news.id, diaries.id, etc. Même chose pour les slugs avec du sluggable_type et du sluggable_id. Écrire des contraintes dessus repose à ma connaissance sur des triggers et il faut vraiment être sûr de comment ça s'intégrer avec Ruby On Rails par exemple (qui fait toujours des tas de choses implicitement). Pareil on a un materialized_path composé d'une concaténation de chaînes de 12 octets chacune contenant un id de commentaire : vérifier que la taille est un multiple de 12 et que chaque sous-chaîne de 12 caractères correspond à un entier utilisé comme id dans la table comments mais aussi un id qui est sur le bon contenu, ça peut être compliqué à écrire (et on peut aussi se dire qu'il n'y a aucune raison que ça soit corrompu un jour, même si l'expérience montre que si) ;
      • les scripts garantissent aussi une cohérence la base SQL et Redis, ce que ne permettent pas les contraintes SQL (par exemple on va trouver dans Redis la tribune associée à une dépêche refusée, donc si on supprime cette dépêche, il faut nettoyer côté Redis aussi) ;
      • il y a des plantages qui peuvent interrompre des opérations en cours. On peut espérer que tout soit transactionnel, mais, un, ce n'est pas le cas, deux, les transactions ne seront pas multi-base de données ;
      • il y a des opérations manuelles en base parfois (notamment pour virer des comptes ou gérer des spammeurs), et tant que cela n'est pas codé proprement, on peut avoir une bête typo en base de données (le souci sur les arborescences de commentaires est juste un zéro manquant dans une chaîne de caractère, soit cinq zéros d'affilée au lieu de six, une erreur facile à faire pour un humain sur un copier-coller par exemple) ;
      • on a des données dénormalisées, ce qui augmente les risques de désynchronisation (comme les nodes.comment_count vs le nombre de commentaires ou le cached_slug qui est une copie locale) ;
      • il y a le poids de l'historique ou dette technique : on a des données très anciennes, on a pu avoir des soucis de conversion dans le passé, les contraintes ont changé au fil du temps, le code est surtout utilisé sur les nouvelles données donc on peut avoir des surprises sur les anciennes (par exemple le code d'édition des anciennes dépêches qui n'existent qu'en HTML est actuellement cassé car il cherche désespérément la dernière version en Markdown), etc. ;
      • de fait, le script trouve des choses, régulièrement. Il détecte le bug sur les tags avec espace (le code RoR lui est "persuadé" d'avoir géré correctement), il détecte les oublis lors d'opérations manuelles comme une purge complète de compte suite à une demande d'un utilisateur, etc. ;
      • il assure une non-régression : on a déjà eu des soucis dans le passé sur certains cas, on met en place un test pour savoir si cela se reproduit (en espérant que cela n'arrive plus car on a fait des corrections ailleurs pour l'éviter) ;
      • je suis largement plus à l'aise pour faire des vérifications externes que pour modifier du code Ruby On Rails ;
      • dans mon expérience pro et dans mon expérience associative, on m'a très souvent dit que c'était inutile de vérifier les données ainsi, et pourtant j'ai détecté ainsi de nombreux problèmes distincts, à pas cher, sur des projets différents, avec des bases SQL ou NoSQL variées. Pour faire un parallèle hasardeux, dans certains domaines sensibles (aéronautique, nucléaire), on s'assure d'avoir deux ou plus implémentations différentes d'un même comportement pour être sûr qu'au moins une va marcher et détecter les incohérences, ici c'est un peu ça de façon limitée, ciblée et pas coûteuse.
      • [^] # Re: Remarques

        Posté par  . Évalué à 3 (+1/-0). Dernière modification le 01/06/20 à 15:31.

        Salut,

        Mais c'est super facile tout ça !

        Et accessoirement, je fais aussi revenir les femmes parties, mais c'est un peu plus long.

      • [^] # Re: Remarques

        Posté par  . Évalué à 10 (+8/-0).

        Salut,

        Pris d'un petite petite larme devant la peine que tu as eu, j'ai commencé ma carte de visite :

        kaos

        N'hésite pas une prochaine fois !

        • [^] # Re: Remarques

          Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

          Je crois que ça va drôlement aider, euh, des tas de gens.
          On peut payer en sacrifice de poulet ?

          OS préféré Mageia 7, CMS préféré SPIP, suite bureautique préférée LibreOffice, logiciel de dessin préféré Inkscape.

          • [^] # Re: Remarques

            Posté par  . Évalué à 4 (+2/-0).

            Salut,

            Pour les aspects pratiques, ma méthode est plus d'égorger un cierge et de brûler une chèvre.

            Mais ce ne sont que de menus détails qui peuvent s'arranger.

        • [^] # Re: Remarques

          Posté par  . Évalué à 3 (+2/-1).

          voir avec son pote omeho

          D'ailleurs sais-tu pourquoi Juliette est t'iste ? Pa'ce que 'oméo pa'ti…

          • [^] # Re: Remarques

            Posté par  . Évalué à 2 (+0/-0).

            Salut,

            Oui oui, je sais très bien, c'était un petit clin d'oeil pour ceux qui connaissent.

  • # Ploum avait raison...

    Posté par  . Évalué à 7 (+6/-0).

    Ploum vous avait pourtant bien dit de refaire DLFP en J2EE et non pas en Rails… j’attends avec impatience la migration Websphere / DB2 / AIX

  • # Si je devais lui donner un titre,

    Posté par  . Évalué à 9 (+7/-0).

    Si je devais lui donner un titre, je l’appellerais procrastination

    Si je devais lui donner un titre, je l'appellerais 'ampoule' :

    https://youtube.com/watch?v=AbSehcT19u0

    • [^] # Re: Si je devais lui donner un titre,

      Posté par  (site Web personnel) . Évalué à 4 (+1/-0).

      Tout à fait, j'aime beaucoup cet extrait, le côté « dès que je regarde quelque part, je trouve un truc qui ne marche pas » (comme je voudrais). Je dirais que c'est un mélange de « plus tu fais de choses, plus tu rencontres de problèmes », d'un regard plus intéressé par ce qui ne marche pas que par ce qui marche (j'ai travaillé dans les tests et de manière générale mon boulot a souvent été d'assurer que ça marche comme prévu), d'acceptation (j'aurais aussi pu me dire « je m'en fous » ou « je créé une entrée dans le suivi et on verra plus tard »), voire une forme de perfectionnisme (« je ne veux aucune erreur, tout doit marcher comme prévu »). Mais ça rentre aussi dans une des définitions de la procrastination qui est de se lancer dans une véritable frénésie d’activités pour éviter de faire une tâche que l'on veut éviter de faire (source https://fr.wikipedia.org/wiki/Procrastination ).

  • # Window Maker

    Posté par  (site Web personnel) . Évalué à 10 (+10/-1).

    Pour ma part, Windows m’écœure.

  • # Oh un journal sur la procrastination

    Posté par  (site Web personnel) . Évalué à 8 (+6/-1).

    Je le lirais demain !

    Incubez l'excellence sur https://linuxfr.org/board/

  • # bien tenté

    Posté par  . Évalué à 1 (+2/-1).

    Ne nous laissons pas abuser par cette mascarade: une fois de plus l'élite DLFPienne masque son mépris de Window Maker (à ne pas confonde avec le fabricant de fenêtre) par une pseudo justification technique. Mais ayons confiance: un jour la vérité éclatera, et le monde sera enfin informé de la suprématie imminente de Window maker.

  • # Passes le bonjour à Murphy

    Posté par  . Évalué à 3 (+1/-0). Dernière modification le 06/06/20 à 23:31.

    Ça fait du bien de lire tes aventures avec Murphy Benoît, ces petits moments ou te te demandes pourquoi j'ai foutu le nez la dedans et tu sens que ça va te faire la nuit si c'est pas la semaine pour un truc qui devait juste te prendre 3 minutes à la base.
    Toute la beauté de ce magnifique métier est résumé dans ce journal à la tension dramatique digne d'une série netflix version nerd.
    C'est bien de partager :)

    PS: En parlant de partager, voici une version pr0n hardcore d'une rencontre avec Murphy (bon ils l'ont bien cherché) avec une équipe de mecs qui veulent remettre en route un Xerox Alto https://www.youtube.com/watch?v=YupOC_6bfMI&list=PL-_93BVApb58I3ZV67LW3S_JEMFnDrQDj

  • # Jayce

    Posté par  (site Web personnel) . Évalué à 0 (+1/-2).

    /me, en train de chercher MultiDeskOS, frénétiquement…

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.