2 points par GN⁺ 2023-08-12 | 1 commentaires | Partager sur WhatsApp
  • Le goulot d’étranglement Postgres d’une application SaaS monolithique s’est aggravé : l’utilisation CPU restait entre 60 et 80 %, et a même atteint 100 % une fois, provoquant une courte interruption
  • Le scaling vertical consistant à passer à une instance de base de données plus grande avait déjà atteint ses limites, ce qui rendait difficile d’absorber davantage de charge par la même approche
  • Le sharding des écritures et les microservices peuvent accroître la capacité et la résilience opérationnelle, mais ils laissent en permanence un coût de complexité sur les sauvegardes, le monitoring, les migrations, l’ORM et jusqu’à la topologie réseau
  • En pratique, pendant trois mois, l’équipe a supprimé des requêtes lourdes, optimisé le code Rails, ajusté Postgres et déplacé certaines requêtes coûteuses en lecture seule vers une base de données répliquée
  • Résultat : le pic hebdomadaire d’utilisation CPU de la base de données est passé de 90 % à 30 %, laissant davantage de marge pour exploiter plus longtemps le système actuel avant de basculer vers une architecture de nouvelle génération

Le goulot d’étranglement Postgres et la fin du scaling vertical

  • À mesure que la charge d’une application SaaS monolithique augmentait, les performances de Postgres sont devenues le principal goulot d’étranglement
    • L’utilisation CPU restait dans la plage 60–80 %
    • Au moins une fois, elle est montée jusqu’à 100 %, provoquant une courte interruption
  • Auparavant, chaque fois que la base de données devenait trop sollicitée, l’équipe gagnait du temps en la remplaçant par une instance plus grande
    • Cette approche permettait de se concentrer sur d’autres tâches, comme le développement de fonctionnalités
    • À ce moment-là, l’instance la plus grande était déjà utilisée, rendant tout scaling vertical supplémentaire impossible

Une architecture de nouvelle génération séduisante, mais coûteuse

  • Les principales options discutées étaient le sharding des écritures et les microservices
  • Le sharding des écritures consiste à disposer de clusters de bases de données indépendants et à écrire les données dans une base donnée selon une stratégie de partitionnement
    • Il peut potentiellement augmenter la capacité d’un facteur à deux ou trois chiffres
  • Les microservices consistent à diviser le monolithe en plusieurs services, chacun disposant de son propre stockage de données
    • Cela permet de choisir un stockage de données adapté à la charge de travail de chaque service
  • Les deux approches élargissent les options en matière de tolérance aux pannes et de résilience opérationnelle, mais l’objectif immédiat était de ramener les performances de la base de données dans un état maîtrisable

Une fois introduite, la complexité continue de coûter

  • L’augmentation de la complexité dépasse le coût de mise en œuvre d’une nouvelle structure : elle se transforme ensuite en coût d’attention
  • Si l’on choisit le sharding de base de données, chaque décision technique future devra composer avec cette nouvelle complexité
    • Sauvegardes
    • Monitoring
    • Migrations
    • ORM
    • Topologie réseau
  • Les microservices créent une charge similaire, et le maintien de cette architecture supplémentaire peut retarder, voire faire abandonner, la livraison de fonctionnalités

Chercher d’abord de la marge dans le système actuel

  • Même lorsqu’une grande transition d’architecture semble nécessaire, il reste souvent de la marge supplémentaire dans le système existant
  • Ajuster les workloads, optimiser les performances et ajouter des systèmes auxiliaires peut faire gagner plusieurs mois, voire plusieurs années
  • Si ces options sont praticables, elles valent la peine d’être tentées avant de construire un nouveau système de nouvelle génération

Les optimisations réellement effectuées

  • Lors du premier chantier, deux ingénieurs se sont principalement consacrés aux problèmes de performance de la base de données pendant environ 3 mois
    • Il n’y avait pas de solution miracle unique
    • Ils ont utilisé la télémétrie pour repérer les requêtes lourdes
    • Ils ont identifié dans la codebase Rails les endroits où ces requêtes étaient déclenchées
    • Ils ont optimisé ou supprimé les requêtes
    • Ils ont ajusté plusieurs paramètres Postgres
  • Lors du second chantier, deux autres ingénieurs ont modifié la codebase pour exécuter certaines requêtes coûteuses en lecture seule sur une base de données répliquée
    • Ils ont séparé de la base principale les requêtes SELECT les plus fréquentes, provoquées par le polling du client web

Résultats et principes d’exploitation

  • Ensemble, ces deux chantiers ont fait passer le pic hebdomadaire d’utilisation CPU de la base de données de 90 % à 30 %
    • Pic hebdomadaire d’utilisation CPU : {l:90,30}
  • La marge CPU a fortement augmenté, tout comme la capacité à délester la base de données principale
  • En touchant plusieurs parties de la codebase et grâce à la collaboration de plusieurs développeurs, une connaissance distribuée du système existant s’est également constituée
  • La complexité n’est pas toujours mauvaise, et il faudra un jour passer à une structure plus complexe avant d’atteindre les limites fondamentales de l’architecture de base de données
  • D’ici là, tirer d’abord le maximum du système actuel permet de travailler plus longtemps avec des systèmes ennuyeux et simples, ce qui est aussi avantageux en termes de coûts et de pragmatisme

1 commentaires

 
GN⁺ 2023-08-12
Avis sur Hacker News
  • En voyant les problèmes de performance des bases de données, mon idée la plus tranchée pour les nouveaux projets est de concevoir les chemins chauds de l’application de façon à n’utiliser aucun join
    Le stockage ne coûte pas cher, donc on peut tout dénormaliser et tout mettre à jour dans une transaction. C’est vraiment surprenant de voir à quel point supprimer les joins accélère les choses. Pour les requêtes analytiques ponctuelles, il suffit de répliquer vers une autre base de données dédiée à l’analyse
    J’ai des sentiments mitigés sur DynamoDB d’Amazon, mais l’idée qu’il faut d’abord planifier les patterns d’usage, puis définir le schéma ensuite, mérite d’être reprise aussi dans les bases de données relationnelles. Ces temps-ci, j’en viens même à penser que les joins sont inutiles hors usages analytiques. Les principales bases de données ont des propriétés ACID et le stockage est ridiculement bon marché, donc autant dénormaliser
    Pour éviter les partitions chaudes, mieux vaut utiliser quelque chose de proche d’un UUID plutôt que des entiers. Ce n’est pas une solution miracle et il y a des inconvénients, mais on peut s’habituer à des performances « toujours correctes » et scalables horizontalement plutôt qu’aux excellentes performances d’entiers qui finiront un jour par s’effondrer
    Idée encore plus brûlante : indexer toutes les colonnes, mais ce sera un sujet pour un autre jour

    • Franchement, je ne suis pas du tout d’accord. En construisant une startup, de la 1re à la 5e année, nous nous sommes très peu souciés des performances ; la 6e année, un problème de performance sur une table précise a éclaté et nous l’avons optimisé avec plusieurs mois de travail d’ingénieurs
      Du point de vue de la dette technique, essayer de tout rendre rapide dès le départ aurait coûté bien plus cher. La vitesse de développement aurait fortement ralenti, au point que nous aurions probablement échoué lors de plusieurs crises
      À la place, nous avons payé quelques milliers de dollars par mois de coûts machine en plus de ce qui était réellement nécessaire, et nous avons économisé plusieurs mois de travail à un moment où nous ne pouvions pas recruter assez d’ingénieurs et où le coût d’opportunité sur le développement de fonctionnalités était élevé. Comme il était impossible de savoir à l’avance où serait le goulot d’étranglement, rendre tout rapide dès le départ aurait demandé 10 à 20 fois plus de travail. Certains goulots étaient inattendus
      Les joins peuvent être néfastes à grande échelle, mais la plupart des startups n’ont pas de problème d’échelle, au moins au début. La dénormalisation peut être une bonne optimisation, mais on paie un coût de vitesse à chaque changement, pour synchroniser toutes les copies. Quelqu’un finira par introduire un bug qui ne met pas à jour un champ dénormalisé non canonique, et les utilisateurs verront des données périmées. En général, utiliser des joins puis optimiser plus tard avec un cache read-aside, etc., revient moins cher au total que de tordre le schéma
    • Si les index sont correctement définis et que les joins se font sur le même serveur, ils ne devraient pas avoir un impact important sur les performances. Dire que « supprimer les joins rend les choses beaucoup plus rapides » n’est pas vrai si l’on utilise correctement les joins
      À l’inverse, écrire une même donnée à 20 endroits au lieu d’un seul est bien plus lent en termes de performance, et les requêtes deviennent extrêmement complexes et sujettes aux bugs. On met à jour 18 endroits et on en oublie 2
      On cite le stockage bon marché comme avantage de la dénormalisation, mais ici le stockage est le plus petit des problèmes. La surface de bugs beaucoup plus grande et les mauvaises performances en écriture sont les vrais problèmes, et ces performances en écriture peuvent facilement grignoter aussi les performances en lecture
    • Il existe des applications « hautes » (tall) et des applications « larges » (wide). Presque tous les conseils sur la conception et l’optimisation des bases de données s’adressent aux applications hautes
      Une application haute fait essentiellement une seule chose, et le reste sert à l’épauler. La plupart des Big Tech qui viennent à l’esprit entrent dans cette catégorie. Dans leur modèle de données, il n’y a que quelques concepts moteurs vraiment importants
      Facebook, en pratique, ce sont des personnes, des publications et des publicités. Netflix, ce sont surtout des comptes et des séries/films. Pour les produits Amazon, les éléments clés sont les vendeurs, les acheteurs et les produits, avec éventuellement quelques concepts logistiques derrière
      S’il y a beaucoup d’applications hautes, c’est parce que c’est plus facile. C’est beaucoup plus facile que les applications larges qu’on appelle souvent « enterprise ». Si les logiciels enterprise sont mauvais, c’est parce qu’ils sont difficiles, et c’est là que se trouvent les domaines les moins explorés et d’énormes opportunités. Les acteurs installés comme Oracle y échouent lamentablement, et si vous y entrez avec un mode de pensée d’application haute, vous échouerez aussi
      Des conseils comme « n’utilisez jamais de joins » ou « concevez autour d’une seule table » ont du sens pour les applications hautes, mais sont de très mauvais conseils pour les applications larges. On voit souvent des entreprises d’applications très hautes échouer lamentablement quand elles tentent quelque chose hors de leur cœur de compétence, parce qu’elles sont remplies de gens qui sacralisent ce type de conseils
      Ces conseils sont faits pour des entreprises déjà prospères, qui font des choses faciles et ont déjà cueilli tous les fruits à portée de main. Même les applications hautes qui ne sont pas encore devenues victimes de leur succès n’ont pas besoin de massacrer leur modèle de données pour les performances. Seules les entreprises déjà gigantesquement prospères, qui cherchent à extraire la dernière goutte de performance, se posent ce genre de questions — et ce sont précisément celles qui ont le moins besoin de conseils. Les conseils centrés sur les applications hautes, du type « si les FAANG le font, tu dois le faire aussi » ou « et si tu as un milliard d’utilisateurs ? », contaminent l’esprit de ceux qui veulent faire quelque chose de plus intéressant que montrer des publicités à des milliards de personnes
    • Ces dernières années, j’ai vu davantage de souffrances causées par des développeurs s’appuyant sur la dénormalisation que de problèmes de performance liés à des joins sur de grosses tables
      Les gros joins peuvent être corrigés plus tard en les poussant dans des vues matérialisées, ou en faisant de l’ETL vers un stockage en colonnes. Mais si quelqu’un copie une colonne subtotal_cents dans les modèles Order, Invoice, Payment, NotificationEmail et UserProfileRecentOrders, puis la fait référencer ou mettre à jour à 296 endroits, le chemin du retour vers un état sain devient long
    • J’ai très rarement vu les joins eux-mêmes être le goulot d’étranglement de performance. S’il y a un principe unique que j’ai observé, c’est : « ne faites pas des tables trop grandes »
      En général, les problèmes apparaissent dans les tables de type historique. Les données nécessaires aux opérations quotidiennes ne représentent qu’une infime partie de la table réelle, mais quelle que soit la présence d’index, travailler sur une énorme table finit forcément par ralentir. Le simple fait d’ajouter davantage d’index devient lui-même un problème
      Au moins dans les bases de données relationnelles traditionnelles, indexer toutes les colonnes ne suffit pas : il faut définir les bons index sur combinaisons de colonnes qui pourront être utilisés. DynamoDB peut être différent
  • Tirez le maximum de ce que vous avez ; après l’avoir fait pendant un moment, changez votre façon d’aborder le problème et, en grattant ici, puis là, puis encore là, vous vous rendrez soudain compte qu’il reste énormément à gagner
    Nous avons optimisé un énorme monolithe pendant environ deux mois : depuis un état à moins de 2 000 RPS, où le PM et l’équipe pensaient qu’il n’y avait plus rien à extraire, un changement de matériel nous a fait monter à moins de 3 200 RPS ; quelques jours de retouches nous ont amenés à 4 000 RPS, un peu plus d’efforts à 10 000 RPS, puis environ une semaine plus tard à 40 000 RPS
    On a entendu : « c’est suffisant, pas besoin d’aller plus loin », mais après avoir changé pas mal de choses, on est passé à plus de 2 millions de RPS sur une seule machine ; un mois plus tard, le système traitait de façon stable plus de 40 millions de RPS avec une faible latence. Il reste encore un peu de marge pour pousser davantage
    Aujourd’hui, nous n’utilisons même pas 5 % de la capacité que l’on pourrait en tirer. Rien que changer notre manière de penser le problème a produit ce genre d’évolution. Le simple passage d’un vieux serveur à un nouveau n’avait fait passer le système que de 1 800 RPS à un peu plus de 3 000 RPS. Ajouter du matériel ne corrigeait pas le problème de fond, et ajouter de la complexité ne faisait que repousser le problème. En changeant notre façon de réfléchir au problème, le problème lui-même et la réponse ont changé

    • Je suis vraiment curieux de savoir ce que ce monolithe faisait si mal pour qu’on puisse en tirer autant de performances. Les requêtes étaient-elles mauvaises, trop nombreuses, n’y avait-il aucun cache, faisait-il de façon synchrone des choses qui pouvaient être traitées en parallèle ?
    • Je me demande si vous pourriez partager la nature de ces requêtes ou leur protocole, ainsi que la taille de payload moyenne des requêtes et des réponses
  • Pour compléter l’idée de « découper le monolithe en plusieurs services interconnectés, chacun avec son propre stockage de données pouvant évoluer selon ses contraintes », il n’est pas nécessaire, à ce stade, d’extraire tous les microservices possibles. Il suffit de demander : « quelle séparation aurait le plus grand impact ? »
    Dans mon cas, j’ai extrait de Mongo certaines données de séries temporelles vers Cassandra. La structure de tables de Cassandra convenait beaucoup mieux. Ce jeu de données avait un schéma bien défini, et Cassandra pouvait compacter les données de manière bien plus efficace. Pour cette partie, nous n’avions pas besoin de la flexibilité des documents JSON
    Comme ces données représentaient la majeure partie de l’ensemble, Mongo est ensuite devenu tout à fait satisfaisant. Une seule séparation a été nécessaire. Techniquement, c’était un monolithe avant comme après ; c’est simplement le même service qui s’est mis à écrire dans deux bases de données
    Ironiquement, plus tard, un architecte du genre très théorique a voulu regrouper toutes les données dans un stockage de documents JSON, et nous avons eu plusieurs discussions du type : « nous avons déjà emprunté cette voie et nous savons où elle mène »

    • Il est intéressant de voir l’idée des microservices lancée comme une « solution » trop évidente. Ce n’en est pas une
      Le passage à l’échelle vertical devrait être la première réponse évidente. Beaucoup de gens passent à côté de ce point, et l’article l’aborde en partie, mais le scaling vertical ressemble beaucoup à du scaling horizontal qui ne casse pas la cohérence de la base de données
      Il y a beaucoup de marge à extraire, et il est rare qu’un problème de performance justifie de sortir facilement des anti-patterns comme ignorer les jointures ou la validation des données
    • L’article a aussi procédé à une forme de séparation, d’une certaine manière, puisqu’il a déplacé certaines requêtes SELECT lourdes vers un réplica
    • Curieusement, je rencontre souvent le problème inverse. Je dois sans cesse expliquer pourquoi Cassandra n’est pas adapté à des données relativement éphémères et fréquemment mises à jour. À cause des tombstones
    • Le fait qu’un seul serveur puisse lire depuis deux stockages de données est aussi important. Comme pour les réplicas de lecture mentionnés dans un commentaire voisin, rien n’empêche de lire à la fois Postgres et Redis depuis le même processus
  • Si les développeurs savent lire les résultats de EXPLAIN/ANALYZE et faire correctement l’indexation et l’optimisation des requêtes, on peut éviter quantité de décisions de suringénierie
    Il suffit de journaliser les requêtes, de filtrer celles qui s’exécutent très souvent ou prennent beaucoup de temps, de mettre en cache celles qui sont fréquentes et d’optimiser celles qui sont lourdes. En le faisant systématiquement, le système devient plus sain
    D’après mon expérience, ce qui aide beaucoup, ce sont l’APM, les logs de requêtes lentes, les réplicas de lecture/écriture de base de données, le partitionnement et le sharding

    • Lire la sortie d’explain peut déjà être un défi assez important. Les bases de données sont un monde à part, surtout si l’on écrit très peu de SQL directement
      Des outils comme https://explainmysql.com montrent plus clairement ce qu’il faut réellement optimiser, ce qui donne un cadre plus accessible aux développeurs qui en savent assez pour configurer une base de données, mais pas assez pour comprendre son fonctionnement interne
      J’imagine que quelqu’un est déjà en train de créer un système d’IA dans lequel on met le schéma et les logs, et qui lance magiquement le SQL à améliorer. Je ne sais pas si je lui ferais confiance, mais beaucoup d’entreprises préféreraient utiliser ce genre de chose plutôt que recruter un DBA dédié
    • Je me demande s’il existe de bonnes ressources pour comprendre les plans d’exécution SQL. Dans mon projet actuel, nous avons beaucoup de problèmes de performance avec des requêtes MS SQL Server
      Je ne sais pas s’il faut toujours préciser un indice d’index pour chaque requête. Il arrive qu’une requête semble ne pas utiliser un index pourtant existant. Utiliser les plans d’exécution SQL m’aiderait sans doute à mieux comprendre ce problème
    • Je ne compte plus le nombre de sessions « architecture de nouvelle génération » que j’ai vues, alors que la plupart auraient pu être remplacées par un audit préalable de l’implémentation actuelle
      On ne corrige pas du mauvais code avec une nouvelle architecture. On ne fait que repousser le problème à plus tard
  • Cela me rappelle l’une de mes citations préférées : « On part à la guerre avec l’armée qu’on a, pas avec celle qu’on voudrait ou qu’on aimerait avoir plus tard. »
    On peut avoir envie d’ignorer le fait que cette phrase vienne de Donald Rumsfeld. Il a quand même eu quelques excellentes formules, comme « unknown unknowns ».
    J’y pense souvent quand je travaille en équipe. Tout le monde n’est pas parfaitement d’accord, ni ne partage la même compréhension ou les mêmes objectifs communs. Certains peuvent travailler de façon inefficace, ou d’une manière que je ne privilégierais pas. Mais comme il vaut mieux avoir une équipe que pas d’équipe du tout, il faut trouver la meilleure façon d’atteindre l’objectif avec l’équipe qu’on a.
    Cela s’applique aussi très bien aux systèmes.

    • Rumsfeld a pas mal de très bonnes citations. La plupart viennent du contexte où il expliquait comment la guerre en Irak avait pu tourner à un tel désastre, et c’est justement dans cette situation qu’on aurait eu cruellement besoin du leadership que ses citations seules laissaient penser qu’il pouvait apporter.
    • Chaque fois que j’entends le nom Rumsfeld, je repense à cette scène où, pendant plus de dix minutes, il refusait de nier qu’il était un lézard.
      https://www.youtube.com/watch?v=XH_34tqxAjA
    • « Aucun plan de bataille ne survit au contact avec l’ennemi. »
      https://www.google.com/search?q=no+battle+plan+survives
    • Le « l’ennemi a aussi son mot à dire » de Mattis est aussi un bon rappel à la réalité. Les gens se mettent très en colère, mais c’est utile dans des contextes comme la sécurité, la confidentialité ou les DRM.
    • J’aime aussi cette phrase similaire de Steven Pressfield : « L’athlète sait qu’il ne viendra jamais de jour où il se réveillera sans douleur. Il doit jouer blessé. »
      Cela s’applique davantage à nous-mêmes qu’aux systèmes.
  • D’après mon expérience, dans les applications web construites au-dessus d’un ORM, il existe énormément de fruits faciles à cueillir côté optimisation des requêtes quand la charge de la base de données devient un problème.
    Au-delà des bases du type « y a-t-il un problème N+1 ? », les ORM ne produisent pas toujours les requêtes optimales. Je ne voudrais pas construire une application web de production complexe sans ORM, mais il faut parfois pouvoir s’en échapper.
    Il faut profiler les requêtes réellement exécutées en production et qui consomment le plus de ressources. Les examiner, puis comprendre la forme des tables concernées. L’ORM utilise parfois des jointures alors qu’il faudrait en réalité une sous-requête, et l’inverse arrive aussi. Il faut parfois pré-agréger une partie des résultats ou ajuster les conditions WHERE dans des jointures complexes.
    J’ai déjà vu une requête générée par un ORM, exécutée assez régulièrement, mettre la base de données à genoux : elle prenait plus de 20 secondes et est passée sous la seconde avec seulement quelques petites modifications.

    • Je travaille en ce moment avec l’ORM Python SQLAlchemy, et faire en sorte qu’un INSERT utilise RETURNING n’est pas trivial. Il faut définir l’option peu intuitive expire_on_commit=False.
      Même cette option ne garantit pas l’utilisation de RETURNING : il est utilisé si le driver de base de données et la base le prennent en charge, et si l’ORM le prend en charge pour cette combinaison driver/base. Le SQL généré apparaît dans les logs, mais il n’existe pas d’API pour l’inspecter directement ; il n’y a donc aucun moyen d’imposer l’usage de RETURNING dans la suite de tests sans capturer soi-même les logs et les parser. Heureusement, dans le framework Pytest, c’est très simple.
      J’aime les ORM, mais ce genre de chose est frustrant de complexité à plusieurs niveaux. Je comprends aussi que SQLAlchemy soit une énorme bibliothèque et que tout ne puisse pas être simple. Mais cet exemple illustre bien les compromis liés à l’usage d’un ORM.
      Je sais qu’en utilisant directement insert() dans Core, on obtient le comportement voulu. Ici, je parle du cas où l’on fait .add() d’un objet ORM dans une AsyncSession.
    • Le gros problème des ORM, c’est que l’orienté objet n’est pas une bonne façon d’organiser le logiciel ou les données dans la plupart des domaines.
      La plupart de la logique métier s’exprime mieux avec le langage de l’algèbre relationnelle et quelques extensions qu’avec l’OOP.
  • « Le vrai coût de l’augmentation de la complexité, et souvent le coût de loin le plus important, c’est l’attention » revient aussi à parler de charge cognitive.
    J’en ai assez de travailler sur des systèmes de microservices où il y a toujours du downtime et où personne ne sait comment l’ensemble fonctionne. La plupart sont en réalité des monolithes distribués : les changements traversent plusieurs services et doivent être déployés dans un ordre précis. Les données doivent être répliquées, les tâches synchronisées, et l’état partagé.
    https://www.youtube.com/watch?v=y8OnoxKotPQ

    • Des microservices dont « personne ne sait comment l’ensemble fonctionne » sont une odeur d’architecture très courante. L’idée initiale est justement que personne ne puisse, ni ne doive, connaître l’ensemble.
      Mais si quelqu’un doit connaître l’ensemble pour corriger ou modifier le système, c’est un signal fort que des règles comme la responsabilité unique ou la bonne abstraction via des API ont été violées. Dans mon expérience, c’est pourtant très fréquent. Pour déboguer un pipeline composé de N microservices, il faut souvent exécuter et builder localement les N services.
      À strictement parler, c’est un monolithe auquel on a ajouté des partitions réseau et une variabilité infinie de build/déploiement. Au final, j’y vois un environnement de travail extrêmement difficile, qui dépasse les capacités de n’importe quel programmeur humain.
  • J’aime bien cet article. Ces derniers mois, j’ai essayé de faire passer le même message à mon manager, sans grand succès.
    Notre cluster Redshift surchargé s’est effondré plusieurs fois, et comme nous l’avons déjà poussé au maximum avec des nœuds RA3, nous sommes sur le point de lancer une grosse migration vers une « infrastructure de nouvelle génération ». Par infrastructure de nouvelle génération, il faut comprendre trois clusters Redshift gérés avec CDK.
    La nouvelle infrastructure sera bien plus complexe que la configuration actuelle, et je ne suis pas convaincu qu’elle sera la solution miracle que tout le monde espère.

  • C’est la solution ennuyeuse. Sauf si l’on construit un système ultra-critique dont dépendent des vies ou l’intégrité physique de personnes, cela devrait être la réponse par défaut. Le coût total de possession sera clairement beaucoup plus bas.
    Si l’on n’a pas les ressources pour exploiter un gros système redondant, j’ai trop souvent vu la complexité ajoutée par cette redondance devenir elle-même le problème. Mieux vaut se concentrer sur la simplicité.
    Si la complexité nécessite d’ajouter beaucoup de personnes pour la prendre en charge, mais que le budget et l’évaluation des risques ne le justifient pas, l’option la plus simple est de loin préférable. Je ne dis pas que je n’ai jamais vu de cas où avancer finissait par nécessiter un énorme projet, mais parfois, même celui-ci me semble inférieur au cumul de la complexité supportée jusque-là. Cela dépend énormément de ce que l’on est en train de construire.

  • Les solutions qui consistent à chercher des optimisations de performance dans un système pour en extraire les performances restantes sont vraiment réjouissantes
    Ça me rappelle le livre de Richard L. Sites, Understanding_Software_Dynamics. Ce livre explique comment mesurer et corriger les problèmes de latence, et à quel point la réduction de la latence à grande échelle peut générer des économies importantes
    Mesurer ce genre de problème et raisonner dessus est difficile, mais les solutions sont souvent simples. Par exemple, à la page 9, il dit : « [a] simple change paid for 10 years of my salary. »
    J’aimerais un jour réaliser une optimisation ayant un tel impact

    • J’ai fait ce genre de chose au moins une fois chez Google. Google utilise une quantité énorme de ressources machine et dispose aussi d’excellents outils de performance, donc c’est un terrain fertile
      https://research.google/pubs/pub36575/
      Mais il y a aussi beaucoup de gens brillants autour de soi, donc si l’on trouve une grosse opportunité, il y a généralement une raison pour laquelle personne d’autre ne s’y est attaqué. Elle peut être technique ou organisationnelle
      Pour prendre un exemple de ce dernier cas, Google ne récompense généralement pas beaucoup ce type de travail, sauf lorsqu’il y a une pression sur les ressources. Il se peut que j’aie reçu environ 100 dollars de bonus entre collègues pour une optimisation, mais certainement pas une commission de 10 %, une promotion, ni le droit de ne pas venir travailler pendant 10 ans tout en étant payé. En général, l’entreprise préfère que les ingénieurs travaillent à la croissance du chiffre d’affaires plutôt qu’à la réduction des coûts. Savoir si cette politique est la bonne dépasse mon niveau de rémunération
    • Ce qui me gêne dans la solution finale, c’est que la requête n’a été optimisée qu’après avoir lancé une instance avec la plus grosse configuration possible
      À ce stade, il n’était plus possible d’augmenter la configuration en quelques clics dans la console d’administration, et c’est sans doute pour cela qu’il a fallu vraiment se creuser la tête pour résoudre le problème de capacité. Si cette partie précise du code avait été optimisée plus tôt, une configuration d’instance aussi grosse n’aurait peut-être jamais été nécessaire