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 Benoît Sibaud (site web personnel) . En réponse au journal Il était une fois... la procrastination. Évalué à 10.
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.
Vaste question. J'y vois de très nombreuses réponses :