Construire un service web hautement disponible sans base de données
(blog.screenshotbot.io)- Screenshotbot utilise l’état RAM du processus serveur web comme stockage de données, sans base de données séparée, et restaure l’état après une panne grâce à des snapshots et un journal de transactions
- En réduisant la sérialisation SQL, les allers-retours avec la base de données et les services distincts de tâches en arrière-plan, l’approche simplifie l’expérience de développement en processus unique et exploite des index en mémoire ainsi qu’un traitement basé sur des threads
- La haute disponibilité est assurée par l’algorithme de consensus Raft, qui réplique le journal de transactions sur 3 serveurs ; en cas de panne du leader, un nouveau leader reprend les requêtes en quelques secondes
- L’implémentation réelle combine Common Lisp, bknr.datastore, le projet open source bknr.cluster, Baidu Braft et EFS, avec des redémarrages serveur généralement limités à une fois tous les 1 à 2 mois
- Même lorsqu’un CI de grands clients enterprise appelle l’API des centaines de fois à chaque commit et Pull Request, l’utilisation CPU reste au maximum autour de 20 % sur une machine 4 cœurs avec 16 Go de RAM, l’essentiel de la charge venant du traitement d’images
Une architecture qui traite l’état en RAM comme une base de données
- Pour les services web traditionnels, il était courant de choisir un framework web comme Rails, Django ou Node avec une base de données comme MySQL, PostgreSQL ou MongoDB
- Au cours des 10 dernières années, l’environnement a évolué au point de justifier un réexamen de cette approche
- les disques sont devenus plus rapides avec NVMe
- les disques comme EBS/EFS sont devenus plus robustes
- la RAM est devenue suffisamment abordable pour que les données de la plupart des startups tiennent en mémoire
- il est possible de louer des machines avec des centaines de cœurs
- en 2014, l’algorithme de consensus Raft a été publié et les implémentations robustes se sont multipliées
- Cette architecture fusionne le service web et l’instance de base de données, et utilise l’état mémoire du processus comme une base de données
- Au lieu de sérialiser les données comme avec SQLite, elle manipule directement les objets et champs en RAM comme état de l’application
Explore : une façon de simplifier les itérations produit initiales
- Si toutes les données sont en RAM, il n’est pas nécessaire de les sérialiser en requêtes SQL, et le modèle où plusieurs serveurs frontend se connectent à une base unique devient moins nécessaire
- Quand la charge augmente, on peut y répondre en utilisant un serveur plus gros avec davantage de RAM et de CPU
- Les index peuvent utiliser des structures comme des tables de hachage en mémoire plutôt que des B-tree conçus pour la latence disque
- chez Screenshotbot, les index basés sur des functional collections ont été importants pour la scalabilité
- Comme la lecture des données revient à lire la RAM, il n’y a pas besoin de structures spécialisées ni d’Async-IO pour réduire les allers-retours avec la base de données
- Les tâches en arrière-plan deviennent des threads exécutés dans le même grand processus, et la gestion de la concurrence peut être largement résolue avec des mutex et condition variables en mémoire
Récupération après panne : snapshots et journal de transactions
- Pour se prémunir contre un crash du processus, l’état complet de la RAM est régulièrement enregistré sous forme de snapshots
- Les changements depuis le dernier snapshot sont d’abord inscrits sur disque dans un journal de transactions, puis l’état mémoire est modifié
foo.setBar(2)consigne une transaction indiquant que le champbardefooest passé à 2, puis modifie réellement le champnew Foo()consigne une transaction indiquant qu’un objet Foo a été créé, puis renvoie le nouvel objet
- Au redémarrage, le système relit d’abord le snapshot puis rejoue le journal de transactions pour restaurer l’état
- Les modifications d’index n’ont pas besoin d’être ajoutées au journal de transactions
- par exemple, si le champ
bardeFooest indexé,setBarmet à jour l’index, et cette mise à jour se reproduit aussi lors du chargement du snapshot ou du rejeu des transactions
- par exemple, si le champ
La forme de code rendue possible par le processus unique
- Comme toutes les requêtes sont traitées dans le même processus, il est possible de stocker des closures dans la mémoire du serveur et de les utiliser pour traiter les requêtes de pages
- Les URL de type
https://screenshotbot.io/n/nnnnnnnde Screenshotbot sont mappées vers des closures internes - Comme les closures conservent des références d’objets, il est moins nécessaire de transmettre des identifiants d’objets ou de sérialiser des objets à chaque changement de page
- Le périmètre de débogage, de profiling et de monitoring se réduit aussi à un seul service
- il n’y a plus de points d’observation de base de données séparés comme les slow query logs de MySQL
- si ce service unique tombe, le site tombe aussi, mais l’idée est qu’avec un seul service et un seul serveur, les possibilités d’échec sont également plus faibles
- si le serveur tombe, AWS peut lancer un nouveau serveur en quelques minutes
- Le code de test devient aussi plus simple, car il n’est pas nécessaire de mocker la base de données
Expand : assurer la haute disponibilité avec Raft
- Quand des clients à haut risque exigent une disponibilité de 99,999 %, une architecture à serveur unique ne suffit plus
- en cas de panne serveur, il peut falloir plusieurs minutes à AWS pour le relancer
- il peut aussi falloir plusieurs minutes au processus pour restaurer le snapshot disque
- pendant un redéploiement, le redémarrage du service peut interrompre le serveur pendant plusieurs minutes
- L’algorithme de consensus Raft réplique le journal de transactions du serveur web/base de données, vu comme une machine à états finis
- Avec une réplication sur 3 machines, si le leader tombe, un nouveau leader est élu en quelques secondes et continue de traiter les requêtes
- Sans changer profondément la façon dont les développeurs écrivent le code, cela permet de transformer un service simple en une base de données hautement disponible
- Dans une configuration basée sur Raft, il est aussi possible de faire des déploiements rolling sans arrêter les serveurs
Extract : sharding et goulots d’étranglement attendus
- Lorsqu’il faudra gérer davantage de grands clients, il sera possible d’appliquer du sharding, comme le font les grandes entreprises avec leurs bases de données
- Chaque shard peut constituer son propre cluster
- Screenshotbot fournit déjà un cluster dédié par client enterprise
- Meta a basculé vers Raft pour gérer la réplication de ses clusters MySQL, et Screenshotbot utilise une approche similaire sans base de données séparée
- Le principal goulot d’étranglement pressenti concerne la montée en charge du commit-thread
- les threads de lecture se parallélisent bien
- un seul commit-thread applique les transactions une par une
- comme Raft valide plusieurs transactions ensemble sur disque, la latence disque n’est pas un facteur important
- le point d’attention est le cas où le coût CPU de l’application des transactions dépasse les performances d’un seul cœur
- dans ce cas, il est possible de profiler le coût des commits pour déplacer une partie du travail hors du thread transactionnel ou d’envisager le sharding
La stack réelle de Screenshotbot
- Screenshotbot utilise Common Lisp
- L’implémentation initiale utilisait MySQL, mais comme il était difficile d’y gérer la concurrence, elle a été remplacée par bknr.datastore
- bknr.datastore est une bibliothèque qui fournit pour Common Lisp la structure décrite dans l’étape Explore
- Dans cette architecture, les threads d’un processus unique traitent les requêtes web ; un multithreading solide est donc essentiel
- pour cette raison, Ruby et Python sont jugés inadaptés
- Comme les closures sont conservées en mémoire serveur, il est difficile de redémarrer souvent le serveur
- un redémarrage fait perdre les closures
- à la place, le code est mis à jour via du hot reloading dans le processus en cours d’exécution
- Common Lisp fournit des mécanismes standard comme
reinitialize-instance, qui permettent de mettre à jour les objets existants lors d’un changement de définition de classe
Cluster, stockage de fichiers et échelle opérationnelle
- Les redémarrages serveur ont actuellement lieu en général une fois tous les 1 à 2 mois
- Lorsqu’un redémarrage est nécessaire, un redémarrage rolling est effectué dans le cluster Raft
- Chaque déploiement utilise un cluster de 3 serveurs, ce qui permet de tolérer la panne d’un serveur
- Kubernetes n’est pas utilisé actuellement, et cela n’est pas encore considéré comme nécessaire
- L’implémentation Raft est une bibliothèque maison construite au-dessus de bknr.datastore
- bknr.cluster a été publié en open source
- en interne, elle utilise Braft de Baidu
- Braft gère les snapshots en arrière-plan, ce qui permet au serveur de continuer à traiter les requêtes pendant la création d’un snapshot
- Les fichiers image ou autres blobs qui ne doivent pas être stockés dans le datastore sont enregistrés sur EFS, partagé par les trois serveurs
- EFS est un NFS hautement disponible
- il est jugé plus simple à utiliser que S3, car il n’oblige pas à gérer séparément des conditions d’erreur
- comme il écrit sur disque sans interagir avec un serveur externe, il offre aussi une meilleure testabilité
Performances actuelles et périmètre d’application
- Screenshotbot prend en charge plusieurs grands clients enterprise, dont un client particulièrement connu
- Il s’exécute dans le CI de ce client et reçoit des centaines de requêtes API à chaque commit et Pull Request
- Malgré cette charge, une machine 4 cœurs avec 16 Go de RAM suffit pour traiter les requêtes
- les serveurs de réplication utilisent des machines similaires et restent la plupart du temps inactifs
- l’utilisation CPU culmine autour de 20 %
- la majeure partie de l’usage CPU provient du traitement d’images
- Il est important de ne pas concevoir pour une échelle bien supérieure à celle réellement nécessaire
- Si vous choisissez Common Lisp, cette configuration est disponible dans Screenshotbot OSS
1 commentaires
Commentaires sur Hacker News
Cette architecture ressemble grosso modo à la manière dont HashiCorp Nomad, Consul et Vault ont été conçus (je suis l’un des mainteneurs de Nomad). C’est clairement une architecture atypique, mais une fois qu’on s’y habitue, l’expérience développeur est assez bonne.
L’état en mémoire peut prendre la forme que l’on veut, ce qui permet de créer soi-même des fonctions d’indexation et de requête adaptées à l’application. On peut utiliser SQLite
:memory:pour la FSM Raft, mais si l’on peut créer ou trouver un magasin transactionnel en mémoire (nous utilisons notre propre go-memdb), lire l’état devient un simple appel de fonction. Empêcher les lectures obsolètes ou le biais en écriture est aussi simple. Comme chaque objet écrit porte un index Raft, on peut créer une API du genre « interroger le follower pour l’objet foo, mais attendre au moins jusqu’à l’index 123 ». Cela retire beaucoup de « magie » que l’on délègue habituellement à un SGBDR ou à un stockage externe.Cela dit, je serais prudent avant de choisir cette architecture pour une nouvelle startup en dehors du domaine de « l’infrastructure ». Car, dans les faits, cela revient à construire sa propre base de données. Il faut bien choisir, ou écrire soi-même, des briques de base comme les RPC entre nœuds, la persistance disque et le magasin transactionnel d’état en mémoire. Les mises à niveau sont particulièrement difficiles : le nouveau code peut tenter d’écrire dans le journal Raft des entités que les nœuds en ancienne version ne comprennent pas. Pire encore, le mode de traitement peut changer et les anciens nœuds peuvent l’interpréter incorrectement. Il n’y a pas de repas gratuit.
:memory:pour la FSM Raft » correspondait à la conception de base utilisée par rqlite[1] pendant environ ses sept premières années. Mais rqlite est passé à SQLite sur disque, et avec le mode WAL etPRAGMA synchronous=OFF[2], c’était aussi rapide que l’écriture en RAM, ou suffisamment proche. Cela a aussi permis d’éviter les contraintes d’une base SQLite:memory:, dont l’une est la limite de taille maximale de 2 Go. J’aurais dû utiliser le mode disque dès le départ, mais je ne le sais que maintenant.Comme rqlite utilise la même bibliothèque Raft[3] que Nomad, vous savez peut-être déjà une partie de tout cela.
Le problème des mises à niveau existe réellement. Je me demande si Nomad le rencontre souvent sur le terrain. En dix ans de développement de rqlite, l’introduction d’un nouveau type de Raft Entry a été très rare, et les utilisateurs réels n’y ont été confrontés qu’une seule fois. Une des façons de gérer cela consiste à déployer d’abord une version qui comprend le nouveau type mais ne l’écrit jamais, puis, une fois cette version entièrement déployée, à passer à une version qui l’utilise effectivement. Cela dit, je ne l’ai jamais fait en pratique, et cela exige aussi de la discipline de la part des utilisateurs finaux.
[1] https://www.rqlite.io
[2] Cela peut paraître dangereux, mais dans la conception actuelle de rqlite, la base SQLite principale est entièrement reconstruite au démarrage à partir du journal Raft (le journal Raft est fsync à chaque écriture). Ainsi, même si la base SQLite est corrompue à cause d’une coupure de courant ou autre, ce n’est pas très significatif, car la base SQLite n’est pas le magasin de données faisant autorité pour rqlite.
[3] https://github.com/hashicorp/raft
En revanche, une fois qu’on commence à utiliser la réplication en phase Expand, il y a effectivement des défis d’ingénierie. Mais ce sont tous des problèmes solubles. En Common Lisp, le fait de pouvoir recharger le code à chaud aide aussi, car certaines migrations deviennent beaucoup plus simples.
Il y a des décennies, PG écrivait qu’à Viaweb ils n’utilisaient pas de base de données, et qu’il trouvait étrange que les applications web deviennent comme des frontends de bases de données alors que ce n’était pas le cas des applications desktop[0]. HN n’utilise pas non plus de base de données
Mais ce n’est plus vraiment exact tel quel, car les applications desktop et mobiles modernes utilisent souvent des bases de données, généralement SQLite. Il s’est avéré que le stockage et l’interrogation de données relationnelles sont très utiles dans un très large éventail d’applications
[0] https://www.paulgraham.com/vwfaq.html
Dans vwfaq, il est toujours question de lire des données depuis le disque, et aussi de « démarrer un processus pour répondre à une requête HTTP ». Ici, « base de données » semble désigner un serveur séparé qui persiste les données, avec une architecture où il faut communiquer avec un autre serveur pour les récupérer
Avec cette définition, SQLite sort évidemment de la catégorie des bases de données. Et si l’on lit déjà des données depuis le disque, alors soit on utilise une base de données, soit on a implémenté une couche de persistance maison créée ad hoc. La question est de savoir si lire les données depuis SQLite au démarrage de l’application compte toujours comme utiliser une base de données
Le problème de cette façon de voir les choses, c’est qu’elle ignore que l’essence d’une base de données est de stocker et récupérer des données de manière pratique, sans avoir à se soucier des détails de bas niveau. Stocker des données dans une base de données ne veut pas dire lancer quelque part une instance Postgres et aller chercher les données via le web. Si vous gardez toutes les données en mémoire et que vous avez un processus qui enregistre des snapshots sur disque avec une structure de données de type log-structured, félicitations : vous venez de créer votre propre base de données
Deuxièmement, au cours des vingt dernières années, les requêtes RDBMS sont devenues beaucoup plus simples. Toutes sortes d’ORM et de row mappers sont apparus pour réduire le code répétitif
Des fonctionnalités avancées utiles aux applications desktop et mobiles, comme la recherche plein texte, sont aussi apparues. Aujourd’hui, utiliser un RDBMS dans une application desktop est un bon choix
Le web a énormément grandi, et beaucoup de ces pratiques ne fonctionneraient plus aujourd’hui. Si je poussais une correction live sur une machine de production, avec le niveau actuel d’exigence en matière de tests, pendant que je suis au téléphone avec un client, beaucoup ici douteraient de mon état mental
Je comprends l’envie d’expérimenter quelque chose d’intéressant, mais faire tout ça pour éviter d’apprendre les bases les plus élémentaires de MySQL ou Postgres me semble être une perte de temps énorme. Il suffit de construire par-dessus, surtout si ça tourne dans le cloud public
Les arguments sur l’augmentation de la latence aller-retour ou les problèmes de concurrence ne me convainquent pas. Pour ce dernier point, il existe des solutions simples, comme un réglage de base ou l’isolation des clients bruyants. Dans un autre billet de leur blog, ils parlent de la possibilité d’ajouter 10 millions de lignes par jour et de défis d’indexation, mais ce n’est littéralement rien. Même à 10 fois ce volume, je ne pense pas que cela justifie d’ingénier une solution sur mesure
Tant que ce n’est pas vraiment nécessaire, « pire, c’est mieux » reste la bonne approche, et quand ce moment arrive, on le sait clairement. À ce stade, on connaît aussi le goulot d’étranglement, ce qui permet de réagir plus intelligemment que de surconcevoir dès le départ
En revanche, voir l’auteur du blog avancer, pour justifier de ne pas choisir SQLite, qu’il pourrait y avoir des fonctionnalités inutiles, c’est déconcertant. C’est absurde et ça ne justifie rien
Le billet se lit comme une mauvaise solution à un problème hypothétique, suivie d’une tentative désespérée d’empiler des raisonnements forcés pour rejeter la solution évidente
Je me demande si je me trompe, ou si cet effort supplémentaire, controversé mais apparemment inutile, fait en réalité partie du produit et de la manière de réussir dans cet espace
C’est étrange de commencer par « je ne parle pas de quelque chose comme SQLite, où les données sont toujours sérialisées », pour finir au bout du compte avec son propre journal de transactions, qui nécessite de la sérialisation et de la réplication. La réplication de bases de données fonctionne déjà comme ça à la base.
Si toute la charge arrive sur un seul serveur, autant faire tourner la base de données sur ce serveur et oublier les « architectures spéciales pour réduire les allers-retours vers la base ». Si toutes les données tiennent en RAM, on peut, si on le souhaite, utiliser un ramdisk pour la base de données et répliquer vers un stockage persistant avec des outils standard. C’est réellement plus simple.
Donc, en gros, ça ressemble à https://litestream.io/. Grâce à la configuration Raft explicite, le basculement pourrait être plus rapide. Je ne suis pas utilisateur de Litestream, donc je ne connais pas les nuances, mais ça paraît très similaire.
Indépendamment de cette simplification excessive, j’aime assez l’idée en elle-même, et je trouve que l’article défend plutôt bien le concept. Pour beaucoup de systèmes, même en cas de succès anormalement élevé, cela s’étendra suffisamment pour absorber la majeure partie, voire la totalité, de l’activité, et les performances seront absurdement bonnes par rapport à presque toutes les alternatives.
Il suffit de démarrer une base à froid puis d’exécuter deux fois un
selectassez volumineux.Une base de données transactionnelle est simple dans les phases Expand et Extract, mais dans la phase Explore, elle devient une charge supplémentaire en poussant à se concentrer sur des problèmes d’infrastructure plutôt que sur le produit. Dans la phase Explore, il n’y a pas de clients, donc pas de données, et la fiabilité des données n’est pas importante.
La manière de bknr.datastore, où tout reste en mémoire (sans réplication), est simple dans la phase Explore. Mais une fois entré dans la phase Expand, elle introduit une charge opérationnelle pour garantir la cohérence des données.
Cela dit, au moment d’atteindre la phase Expand, le produit a déjà été validé et beaucoup de code a déjà été écrit. Le réécrire autour d’une base de données transactionnelle n’a pas de sens ; il est plus simple d’ajouter une réplication Raft par-dessus.
Le raisonnement de cet article est déroutant. Est-ce censé être une manière de rendre les applications avec état plus simples et plus rapides ?
Les prémisses sont faibles et les affirmations excessives. L’auteur exagère la difficulté de la sérialisation pour faire paraître un argument faible plus solide.
Les gens comme ça sont généralement difficiles à côtoyer au travail. Je suis content qu’ils aient trouvé une startup à couler, pour que je n’aie pas à traiter avec eux.
Quand on démarre un nouveau projet, la structure de données est généralement une « liste d’éléments avec des attributs ». Par exemple, je suis en train d’écrire une application de fitness : les données sont une liste d’exercices, et chaque exercice a un titre, une description, une URL vidéo et d’autres propriétés.
En général, je commence par mettre ces éléments dans des fichiers YAML dans un répertoire
data. En réalité, c’est un dialecte YAML personnalisé qui retire les bizarreries du YAML d’origine. Chaque valeur est une chaîne, sans conversion de type magique. Créer un nouvel élément consiste simplement à fairevim crunches.yamlet à saisir les données ; l’édition et la suppression sont aussi très faciles avec cette structure de données.Quand le projet grossit, je crée généralement un schéma de DB et je déplace les éléments vers MariaDB ou SQLite.
Cette fois, je pense déplacer les éléments (les exercices) dans une colonne JSON d’une DB SQLite. Toutes les propriétés d’un élément seront stockées dans un unique champ JSON, et je veux écrire un petit explorateur de DB permettant de modifier le champ JSON comme du YAML. L’objectif est de conserver la commodité de l’édition de données lisibles par un humain.
Écrire l’explorateur de DB devrait être assez direct. Avec un peu de ncurses, parcourir les tables, en sélectionner une, parcourir les lignes, insérer et supprimer des lignes. Pour modifier un champ, lancer Vim. Si le champ est du JSON, le convertir en YAML avant de l’envoyer à Vim, puis le reconvertir en JSON quand l’utilisateur quitte Vim.
Ce qui est décrit au début de l’article ressemble fondamentalement à la manière dont fonctionnaient les machines NUMA (par exemple SGI Altix ou UV). Les avantages qu’elles mettaient en avant étaient aussi la faible latence et la possibilité de paralléliser le travail par multithreading sur une grande quantité de RAM. Le clustering est apparu comme une alternative peu coûteuse à des machines dépassant le million de dollars. Il y a aussi des similitudes avec la persistance de l’AS/400, où l’application écrit simplement en mémoire et où c’est mappé de façon transparente sur le disque.
On remonte donc le temps vers les avantages des machines NUMA en cluster, mais avec du matériel bon marché. Il y a eu des améliorations en chemin, et l’article était intéressant à lire.
Une autre technique du passé consistait à supprimer la pile TCP/IP à l’intérieur du cluster afin d’éliminer les problèmes associés. Des solutions comme Active Messages étaient une fine couche au-dessus du matériel. Il existe aussi des conceptions de routeurs réseau avec une cohérence forte intégrée. Il y a beaucoup de choses qu’ils peuvent faire.
En grandissant, il existe aussi des opportunités matérielles. Côté CPU, SGI a fait deux choses. Les machines NUMA ont augmenté le nombre de CPU et la quantité de RAM dans un même système, et des FPGA ont été branchés directement sur le bus mémoire pour servir d’accélérateurs personnalisés. Enfin, certains articles de recherche en informatique ont modifié le jeu d’instructions des processeurs, le réseau interne à la puce, etc., afin d’éliminer ou de réduire les goulets d’étranglement du multithreading. Des puces comme OpenPiton augmentent le nombre de cœurs avec des cœurs ouverts et personnalisables (par exemple 32).
« Imaginez tout ce qu’on pourrait construire de génial si l’on n’avait pas besoin de sérialiser les données sous forme de requêtes SQL » : cela existe déjà dans des implémentations suffisamment mûres du modèle d’acteurs[0], par exemple Akka Event Sourcing[1]. Le problème suivant y est aussi traité
« Mais la partie importante, c’est comment récupérer quand le processus crashe. La réponse est simple : il suffit de prendre périodiquement un snapshot de toute la RAM »
C’est résolu pour l’essentiel sans avoir à créer une « nouvelle architecture pour le développement web ». Il existe aussi des tentatives open source explorant le protocole RAFT avec des acteurs ici[2] et ici[3]
0 - https://en.wikipedia.org/wiki/History_of_the_Actor_model
1 - https://doc.akka.io/docs/akka/current/typed/persistence.html
2 - https://github.com/Michael-Dratch/RAFT_Implementation
3 - https://github.com/invkrh/akka-raft
En choisissant quelque chose comme Cosmos DB, MongoDB ou DynamoDB comme fournisseur de persistance, on peut aussi interroger l’état persisté
https://learn.microsoft.com/en-us/dotnet/orleans/grains/grai...
https://learn.microsoft.com/en-us/dotnet/orleans/grains/tran...
https://learn.microsoft.com/en-us/dotnet/orleans/grains/even...
Ma première pensée a été : « Ah, c’est la façon dont je faisais quand j’écrivais en Common Lisp ; c’est amusant que quelqu’un ait redécouvert cette technique »
Mais non : il y avait simplement d’autres gens de Lisp
« La RAM est très bon marché » est peut-être l’une des plus grandes idées reçues chez les développeurs
Les SSD se sont améliorés d’un facteur 100 à 10 000 en débit ou en IOPS, et les performances par dollar des vCPU ont aussi progressé d’un facteur 20 à 50. On est passé du 45/32 nm au 5/3 nm, et le nombre d’instructions par cycle est aussi bien plus élevé
Mais le prix de la RAM n’a presque pas baissé autant que celui des CPU ou des SSD. Elle est peut-être beaucoup plus rapide, on peut installer davantage de mémoire grâce à des puces plus denses, et le nombre de canaux est peut-être passé de deux à 8 ou 12. Mais si l’on regarde les prix spot de la DRAM entre 2008 et 2022, le prix minimal de la DRAM a atteint trois fois à peu près le même niveau, autour de 2,8 dollars/Go. Sur la même période, au fil des cycles, il oscillait entre 6 et 8 dollars/Go. Autrement dit, si vous avez acheté de la DRAM aux points bas ou aux points hauts de ces quelque 15 dernières années, le prix était grosso modo similaire, à ±10 à 20 % près en ignorant l’inflation
Ce n’est qu’à la mi-2022 que la barrière des 2,8 dollars/Go a été franchie, avec une chute proche de 1 dollar/Go, avant de se stabiliser autour de 2 dollars/Go pour la DDR5
Aujourd’hui, on peut mettre 4 To de RAM dans un serveur. Cela ne veut pas dire que la DRAM soit incroyablement bon marché. Le développeur moyen, ou celui d’une grande entreprise tech, gagne beaucoup plus qu’en 2010, ce qui donne l’impression que la RAM est devenue bien plus abordable. En réalité, même aux plus bas des 15 dernières années, la baisse du prix de la DRAM dépasse à peine un facteur 2. Et il est très probable que les prix de la DRAM repartent fortement à la hausse d’ici un ou deux ans
Pour prendre un exemple simple, si un nœud donné était limité à 16 Go de RAM il y a 20 ans, il aurait fallu 256 nœuds pour obtenir 4 To de RAM dans le système (hors surcharge de chaque OS)
Aujourd’hui, un seul nœud peut contenir ces 4 To dans un unique châssis
Le coût total des puces de RAM elles-mêmes n’a peut-être pas changé, mais le coût d’utilisation effective de cette RAM dans un système physique a chuté de façon spectaculaire