01
READY delimita el estado.
Un nuevo READY espera las tareas anteriores antes de renovar el estado de los servidores. Los eventos posteriores comienzan después, de modo que el trabajo antiguo no puede sobrescribir el estado nuevo.
Una mirada al interior de Fjord
Un bot en Rust. Un motor gráfico propio. Y los frameworks, bibliotecas y bases de datos sobre los que se apoya Fjord.
Aquí mostramos qué desarrollamos nosotros y qué tecnologías se unen en Fjord.
Fjord
Fjord procesa los eventos de Discord en orden y ejecuta tareas independientes en paralelo. Twilight conecta el bot con Discord y mantiene los datos del servidor disponibles localmente. Dragonfly almacena eventos pendientes y ayuda a coordinar las conexiones del gateway.
01
Un nuevo READY espera las tareas anteriores antes de renovar el estado de los servidores. Los eventos posteriores comienzan después, de modo que el trabajo antiguo no puede sobrescribir el estado nuevo.
02
Los eventos de un mismo servidor empiezan en orden de llegada. Cada uno espera a que termine el anterior para que los cambios del servidor no se adelanten entre sí.
03
El dispatcher distingue las tareas por clave. Los servidores distintos pueden avanzar a la vez; solo esperan entre sí las tareas que comparten clave.
04
El mismo mensaje llega a AutoMod y al registro de actividad a la vez. Cada uno lo trata por separado para su propia tarea.
05
Los eventos pendientes de Discord y sus datos se almacenan en Dragonfly. AutoMod y los registros de actividad también utilizan este almacén compartido para estados temporales. El bot sigue controlando la ejecución de las tareas.
06
Cuando se activa un filtro de AutoMod, termina la comprobación de ese mensaje. Los demás filtros no provocan una segunda medida para el mismo evento.
07
Twilight mantiene actualizados en memoria servidores, miembros, roles, canales, emojis, presencias y estados de voz. Los mensajes no forman parte de esta caché.
08
Las interacciones se planifican independientemente de los eventos del servidor. READY establece un límite de estado dentro de cada conexión del gateway.
09
Dragonfly coordina la responsabilidad de cada shard, es decir, de cada conexión del gateway. Tras un traspaso, el responsable anterior no puede asumir ni completar más eventos.
01 / Desarrollo propio
Fjell es nuestro motor gráfico propio, escrito en Rust. Combina fondos, avatares y textos para crear tarjetas de bienvenida personales.
Queríamos controlar cómo se crean estas imágenes. Por eso desarrollamos una solución adaptada a Fjord que nos da libertad creativa.
La eficiencia de costes es igual de importante. Fjell ofrece el máximo rendimiento sin perder calidad y mantiene el uso de CPU y memoria durante la generación lo más eficiente posible. Así reducimos drásticamente los costes de servidor.
Nuestra implementación llega hasta el procesamiento de píxeles. Los bucles SIMD propios utilizan AVX2, FMA y BMI1/BMI2 para calcular varios píxeles en paralelo.
Medición de rendimiento
Cuatro niveles de carga con variaciones controladas de las tarjetas y la misma configuración de prueba.
| Paralelo | Tarjetas/s | Tarjetas/min | Renderizado prom. | Renderizado P50 | Renderizado P95 | Renderizado P99 | Total prom. | Total P50 | Total P95 | Total P99 | CPU prom. | RAM prom. |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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 |
Rendimiento medido frente a un escalado lineal perfecto.
| Workers | Medido | Lineal ideal | Escalado | Eficiencia |
|---|---|---|---|---|
| 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 % |
Las cuatro pruebas locales duraron 60 segundos cada una en un AMD Ryzen 9 9950X. Se utilizaron 100 fondos ligeros con colores normales o ilustraciones y archivos de unos 100 a 300 KB, normalmente alrededor de 200 KB. La capa líquida permaneció desactivada y la fuente fue constante; entre tarjetas variaron los colores y las posiciones de los shaders del texto y del círculo.
El tiempo de renderizado incluye la generación de la imagen y la codificación y compresión PNG hasta obtener el PNG final. No incluye la transferencia por red ni el tiempo de descarga.
El generador de carga utilizó 1, 2, 4 u 8 solicitudes paralelas según la prueba. Cada nueva solicitud comenzaba después de recibir y validar por completo el PNG anterior. El uso de CPU indicado se produjo exactamente bajo esta carga. El motor gráfico podía repartir el trabajo entre varios núcleos.
* Mediciones internas no verificadas de forma independiente y realizadas bajo las condiciones descritas. No constituyen una característica garantizada, garantía ni compromiso de rendimiento. El rendimiento real puede variar según el sistema, la carga y el entorno.
Prueba aleatoria agresiva
Una configuración mucho más variable con 1.000 fondos, 237 fuentes y efectos aleatorios en cada solicitud.
| Paralelo | Tarjetas/s | Tarjetas/min | Renderizado prom. | Renderizado P50 | Renderizado P95 | Renderizado P99 | Total prom. | Total P50 | Total P95 | Total P99 | CPU prom. | RAM prom. |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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 |
Rendimiento medido frente a un escalado lineal perfecto.
| Workers | Medido | Lineal ideal | Escalado | Eficiencia |
|---|---|---|---|---|
| 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 % |
Estas cuatro pruebas locales duraron 60 segundos cada una en el mismo AMD Ryzen 9 9950X. Cada solicitud eligió entre 1.000 fondos más exigentes de entre 800 KB y 2,4 MB y entre 237 fuentes. Se variaron textos, transparencia, tamaños de fuente y avatar, contorno, colores de texto y círculo, y ángulo, fuerza e intensidad de los shaders. Aproximadamente la mitad de las tarjetas activó además la capa líquida con color, opacidad y desenfoque aleatorios.
El generador de carga utilizó 1, 2, 4 u 8 workers paralelos en un bucle cerrado. Cada worker inició la siguiente solicitud solo después de recibir y validar por completo el PNG anterior. Todas las pruebas de 60 segundos finalizaron sin errores, tiempos de espera agotados ni PNG no válidos.
El tiempo de renderizado incluye la generación de la imagen y la codificación y compresión PNG hasta obtener el PNG final. No incluye la transferencia por red ni el tiempo de descarga.
* Mediciones internas no verificadas de forma independiente y realizadas bajo las condiciones descritas. No constituyen una característica garantizada, garantía ni compromiso de rendimiento. El rendimiento real puede variar según el sistema, la carga y el entorno.
02 / Nuestra base técnica
Además de nuestro propio motor, usamos Rust, Leptos, TypeScript, Vue, Twilight, Dragonfly, PostgreSQL, ScyllaDB y RustFS. Cada tecnología cumple una función concreta en el sistema.
Lenguaje de programación
Rust es la base de nuestro bot de Discord, el backend, el editor de tarjetas y el motor gráfico Fjell.
Logotipo © Rust Foundation. CC BY 4.0 · sin modificaciones
Framework web
Usamos Leptos en partes de nuestra interfaz web. El framework lleva Rust al navegador y encaja con el resto de nuestro stack.
Logo de los mantenedores de Leptos. MIT · sin modificaciones
Lenguaje de programación
Escribimos las páginas públicas interactivas en TypeScript. La comprobación de tipos nos ayuda a mantener coherentes los componentes y sus estados.
Logotipo oficial de TypeScript. Uso conforme a sus directrices de marca.
Framework web
Vue organiza los componentes del sitio público, desde la navegación y la documentación hasta otras áreas interactivas. Así podemos ampliar la interfaz de forma coherente.
Logotipo © Evan You. CC BY-NC-SA 4.0 · sin modificaciones
Biblioteca para Discord
Utilizamos Twilight para conectarnos a Discord. Esta biblioteca enlaza nuestro bot escrito en Rust con Discord.
Logotipo de Fezhoof. CC BY-SA 4.0 · sin modificaciones
Base de datos
Dragonfly es nuestro almacén temporal compartido para eventos de Discord, coordinación de shards y múltiples conexiones del gateway, AutoMod y registros de actividad.
Software bajo BSL 1.1 con una autorización de uso adicional.
Base de datos relacional
PostgreSQL complementa nuestra capa de datos para información y estructuras relacionales. Funciona junto a ScyllaDB como una parte independiente de nuestra infraestructura.
Base de datos
ScyllaDB forma parte de nuestra capa de datos junto a PostgreSQL. Utilizamos ambos sistemas para tareas diferentes dentro de Fjord.
Almacenamiento de objetos
Usamos RustFS para almacenamiento de objetos compatible con S3. Añade una capa de almacenamiento separada junto a PostgreSQL y ScyllaDB.
Software bajo Apache 2.0. RustFS es una marca de RustFS, Inc.