01
READY délimite l’état.
Un nouveau READY attend la fin des tâches antérieures avant que Fjord remplace l’état des serveurs. Les événements suivants démarrent ensuite, ce qui empêche les anciennes tâches d’écraser le nouvel état.
Dans les coulisses de Fjord
Un bot en Rust. Notre propre moteur graphique. Et les frameworks, bibliothèques et bases de données sur lesquels repose Fjord.
Découvrez ce que nous développons et les technologies qui composent Fjord.
Fjord
Fjord traite les événements Discord dans l’ordre et exécute les tâches indépendantes en parallèle. Twilight relie le bot à Discord et garde les données des serveurs disponibles localement. Dragonfly stocke les événements en attente et participe à la coordination des connexions à la passerelle.
01
Un nouveau READY attend la fin des tâches antérieures avant que Fjord remplace l’état des serveurs. Les événements suivants démarrent ensuite, ce qui empêche les anciennes tâches d’écraser le nouvel état.
02
Les événements d’un même serveur démarrent dans leur ordre d’arrivée. Chacun attend la fin du précédent afin que les modifications de ce serveur ne se dépassent pas.
03
Le répartiteur distingue les tâches par clé. Plusieurs serveurs peuvent avancer simultanément ; seules les tâches de même clé s’attendent mutuellement.
04
Le même message parvient à AutoMod et au journal d’activité en même temps. Chacun le traite séparément pour sa propre tâche.
05
Les événements Discord en attente et leurs données sont stockés dans Dragonfly. AutoMod et les journaux d’activité utilisent aussi ce stockage commun pour les états temporaires. Le bot continue de piloter l’exécution des tâches.
06
Dès qu’un filtre AutoMod actif se déclenche, la vérification de ce message s’arrête. Les autres filtres ne produisent pas une seconde mesure pour le même événement.
07
Twilight maintient en mémoire les serveurs, membres, rôles, salons, émojis, présences et états vocaux à jour. Les messages ne font pas partie de ce cache.
08
Les interactions sont planifiées indépendamment des événements de serveur. READY marque une frontière d’état au sein de chaque connexion à la passerelle.
09
Dragonfly coordonne la responsabilité de chaque shard, c’est-à-dire de chaque connexion à la passerelle. Après un transfert, l’ancien responsable ne peut plus prendre en charge ni terminer d’autres événements.
01 / Notre propre développement
Fjell est notre moteur graphique développé en Rust. Il associe fonds, avatars et textes pour créer des cartes de bienvenue personnelles.
Nous voulions maîtriser la création de ces images. Nous avons donc développé une solution adaptée à Fjord qui nous offre une liberté créative.
La maîtrise des coûts compte tout autant. Fjell offre des performances maximales sans perte de qualité, tout en optimisant l’utilisation du processeur et de la mémoire pendant la génération. Nous réduisons ainsi considérablement nos coûts de serveur.
Notre travail va jusqu’au traitement des pixels. Nos propres boucles SIMD utilisent AVX2, FMA et BMI1/BMI2 pour calculer plusieurs pixels en parallèle.
Mesure des performances
Quatre niveaux de charge avec des variations de cartes contrôlées et le même protocole de test.
| Parallèle | Cartes/s | Cartes/min | Rendu moy. | Rendu P50 | Rendu P95 | Rendu P99 | Total moy. | Total P50 | Total P95 | Total P99 | CPU moy. | RAM moy. |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 797,48 | 47.849 | 1,062 ms | 1,061 ms | 1,226 ms | 1,242 ms | 1,253 ms | 1,251 ms | 1,428 ms | 1,473 ms | 88,3 % | 381,2 MiB |
| 2 | 1.577,95 | 94.677 | 1,038 ms | 1,004 ms | 1,225 ms | 1,421 ms | 1,266 ms | 1,236 ms | 1,470 ms | 1,715 ms | 173,5 % | 360,1 MiB |
| 4 | 3.173,00 | 190.380 | 1,023 ms | 1,011 ms | 1,188 ms | 1,230 ms | 1,259 ms | 1,243 ms | 1,448 ms | 1,557 ms | 344,9 % | 375,3 MiB |
| 8 | 5.850,25 | 351.015 | 1,050 ms | 1,026 ms | 1,194 ms | 1,290 ms | 1,366 ms | 1,349 ms | 1,578 ms | 1,803 ms | 647,0 % | 385,8 MiB |
Débit mesuré comparé à une mise à l’échelle linéaire parfaite.
| Workers | Mesuré | Linéaire idéal | Mise à l’échelle | Efficacité |
|---|---|---|---|---|
| 1 | 797,48 | 797,48 | 1,00× | 100,0 % |
| 2 | 1.577,95 | 1.594,96 | 1,98× | 98,9 % |
| 4 | 3.173,00 | 3.189,92 | 3,98× | 99,5 % |
| 8 | 5.850,25 | 6.379,84 | 7,34× | 91,7 % |
Les quatre tests locaux ont duré 60 secondes chacun sur un AMD Ryzen 9 9950X. Ils utilisaient 100 arrière-plans légers avec des couleurs ordinaires ou des illustrations et des fichiers d’environ 100 à 300 Ko, généralement proches de 200 Ko. La couche liquide restait désactivée et la police constante ; les couleurs et positions des shaders du texte et du cercle variaient entre les cartes.
Le temps de rendu comprend la création de l’image ainsi que l’encodage et la compression PNG jusqu’au fichier PNG final. Le transfert réseau et le temps de téléchargement ne sont pas inclus.
Le générateur de charge utilisait 1, 2, 4 ou 8 requêtes parallèles selon le test. Une nouvelle requête ne commençait qu’après la réception complète et la validation du PNG précédent. L’utilisation CPU indiquée a été mesurée sous cette charge précise. Le moteur graphique pouvait répartir son travail sur plusieurs cœurs.
* Mesures internes non vérifiées indépendamment, réalisées dans les conditions décrites. Elles ne constituent ni une caractéristique garantie, ni une garantie, ni un engagement de performance. Les performances réelles peuvent varier selon le système, la charge et l’environnement.
Test aléatoire intensif
Un protocole beaucoup plus variable avec 1 000 arrière-plans, 237 polices et des effets aléatoires à chaque requête.
| Parallèle | Cartes/s | Cartes/min | Rendu moy. | Rendu P50 | Rendu P95 | Rendu P99 | Total moy. | Total P50 | Total P95 | Total P99 | CPU moy. | RAM moy. |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 92,65 | 5.559 | 9,775 ms | 10,478 ms | 12,781 ms | 13,401 ms | 10,788 ms | 11,553 ms | 14,006 ms | 14,784 ms | 93,6 % | 1.205,6 MiB |
| 2 | 184,53 | 11.072 | 9,944 ms | 10,621 ms | 12,858 ms | 13,550 ms | 10,832 ms | 11,576 ms | 13,938 ms | 14,698 ms | 189,1 % | 1.278,8 MiB |
| 4 | 358,82 | 21.529 | 10,209 ms | 10,855 ms | 13,240 ms | 13,997 ms | 11,138 ms | 11,857 ms | 14,363 ms | 15,212 ms | 377,7 % | 1.300,9 MiB |
| 8 | 660,52 | 39.631 | 10,897 ms | 11,610 ms | 14,343 ms | 15,336 ms | 12,101 ms | 12,889 ms | 15,838 ms | 16,880 ms | 744,8 % | 1.308,6 MiB |
Débit mesuré comparé à une mise à l’échelle linéaire parfaite.
| Workers | Mesuré | Linéaire idéal | Mise à l’échelle | Efficacité |
|---|---|---|---|---|
| 1 | 92,65 | 92,65 | 1,00× | 100,0 % |
| 2 | 184,53 | 185,30 | 1,99× | 99,6 % |
| 4 | 358,82 | 370,60 | 3,87× | 96,8 % |
| 8 | 660,52 | 741,20 | 7,13× | 89,1 % |
Ces quatre tests locaux ont duré 60 secondes chacun sur le même AMD Ryzen 9 9950X. Chaque requête choisissait parmi 1 000 arrière-plans plus exigeants de 800 Ko à 2,4 Mo et 237 polices. Les textes, la transparence, la taille de la police et de l’avatar, le contour, les couleurs du texte et du cercle ainsi que l’angle, la force et l’intensité des shaders variaient. Environ la moitié des cartes activait aussi la couche liquide avec une couleur, une opacité et un flou aléatoires.
Le générateur de charge utilisait 1, 2, 4 ou 8 workers parallèles en boucle fermée. Chaque worker ne lançait la requête suivante qu’après réception complète et validation du PNG précédent. Tous les tests de 60 secondes se sont achevés sans erreur, expiration de délai ni PNG invalide.
Le temps de rendu comprend la création de l’image ainsi que l’encodage et la compression PNG jusqu’au fichier PNG final. Le transfert réseau et le temps de téléchargement ne sont pas inclus.
* Mesures internes non vérifiées indépendamment, réalisées dans les conditions décrites. Elles ne constituent ni une caractéristique garantie, ni une garantie, ni un engagement de performance. Les performances réelles peuvent varier selon le système, la charge et l’environnement.
02 / Nos fondations techniques
En plus de notre propre moteur, nous utilisons Rust, Leptos, TypeScript, Vue, Twilight, Dragonfly, PostgreSQL, ScyllaDB et RustFS. Chaque technologie a un rôle précis dans le système.
Langage de programmation
Rust est à la base de notre bot Discord, du backend, de l’éditeur de cartes et du moteur graphique Fjell.
Logo © Rust Foundation. CC BY 4.0 · sans modification
Framework web
Nous utilisons Leptos pour certaines parties de notre interface web. Le framework amène Rust dans le navigateur et s’intègre naturellement au reste de notre stack.
Logo des mainteneurs de Leptos. MIT · sans modification
Langage de programmation
Nous écrivons les pages publiques interactives en TypeScript. La vérification des types nous aide à garder les composants et leurs états cohérents.
Logo officiel TypeScript. Utilisé selon les règles de sa marque.
Framework web
Vue structure les composants du site public, de la navigation et de la documentation aux autres zones interactives. Ces interfaces restent ainsi cohérentes et faciles à faire évoluer.
Logo © Evan You. CC BY-NC-SA 4.0 · sans modification
Bibliothèque pour Discord
Nous utilisons Twilight pour nous connecter à Discord. Cette bibliothèque relie notre bot écrit en Rust à Discord.
Logo de Fezhoof. CC BY-SA 4.0 · sans modification
Base de données
Dragonfly est notre stockage temporaire commun pour les événements Discord, la coordination des shards et des connexions multiples à la passerelle, AutoMod et les journaux d’activité.
Logiciel sous BSL 1.1 avec un droit d’utilisation supplémentaire.
Base de données relationnelle
PostgreSQL complète notre couche de données pour les données et structures relationnelles. Il fonctionne aux côtés de ScyllaDB comme un composant distinct de notre infrastructure.
Base de données
ScyllaDB fait partie de notre couche de données aux côtés de PostgreSQL. Nous utilisons les deux systèmes pour des tâches différentes au sein de Fjord.
Stockage d’objets
Nous utilisons RustFS pour le stockage d’objets compatible S3. Il ajoute une couche de stockage distincte aux côtés de PostgreSQL et ScyllaDB.
Logiciel sous Apache 2.0. RustFS est une marque de RustFS, Inc.