01
READY delimita o estado.
Um novo READY espera pelas tarefas anteriores antes de o Fjord renovar o estado dos servidores. Os eventos seguintes começam depois, impedindo que o trabalho antigo substitua o novo estado.
Nos bastidores do Fjord
Um bot em Rust. Um motor gráfico próprio. E os frameworks, bibliotecas e bases de dados em que o Fjord se apoia.
Aqui mostramos o que desenvolvemos e as tecnologias que se juntam no Fjord.
Fjord
O Fjord processa os eventos do Discord por ordem e executa tarefas independentes em paralelo. O Twilight liga o bot ao Discord e mantém os dados dos servidores disponíveis localmente. O Dragonfly guarda eventos pendentes e ajuda a coordenar as ligações ao gateway.
01
Um novo READY espera pelas tarefas anteriores antes de o Fjord renovar o estado dos servidores. Os eventos seguintes começam depois, impedindo que o trabalho antigo substitua o novo estado.
02
Os eventos do mesmo servidor começam pela ordem de chegada. Cada um espera pelo fim do anterior para que as alterações desse servidor não se ultrapassem.
03
O dispatcher distingue as tarefas por chave. Servidores diferentes podem avançar ao mesmo tempo; só as tarefas com a mesma chave esperam umas pelas outras.
04
A mesma mensagem chega ao AutoMod e ao registo de atividade ao mesmo tempo. Cada um trata-a separadamente para a sua tarefa.
05
Os eventos pendentes do Discord e os seus dados ficam no Dragonfly. O AutoMod e os registos de atividade também usam este armazenamento comum para estados temporários. O bot continua a controlar a execução das tarefas.
06
Quando um filtro ativo do AutoMod é acionado, termina a verificação dessa mensagem. Os outros filtros não provocam uma segunda ação para o mesmo evento.
07
O Twilight mantém servidores, membros, cargos, canais, emojis, presenças e estados de voz atualizados em memória. As mensagens não fazem parte deste cache.
08
As interações são agendadas independentemente dos eventos dos servidores. READY define uma fronteira de estado dentro de cada ligação ao gateway.
09
O Dragonfly coordena a responsabilidade por cada shard, ou ligação ao gateway. Após uma transferência, o responsável anterior deixa de poder assumir ou concluir novos eventos.
01 / Desenvolvimento próprio
Fjell é o nosso motor gráfico desenvolvido em Rust. Combina fundos, avatares e textos para criar cartões de boas vindas pessoais.
Queríamos controlar a criação destas imagens. Por isso, desenvolvemos uma solução adaptada ao Fjord que nos dá liberdade criativa.
A eficiência de custos é igualmente importante. O Fjell oferece desempenho máximo sem perda de qualidade e mantém o uso de CPU e memória durante a geração o mais eficiente possível. Isso reduz drasticamente os nossos custos de servidor.
A nossa implementação chega ao processamento dos píxeis. Ciclos SIMD próprios utilizam AVX2, FMA e BMI1/BMI2 para calcular vários píxeis em paralelo.
Medição de desempenho
Quatro níveis de carga com variações controladas dos cartões e a mesma configuração de teste.
| Paralelo | Cartões/s | Cartões/min | Renderização méd. | Renderização P50 | Renderização P95 | Renderização P99 | Total méd. | Total P50 | Total P95 | Total P99 | CPU méd. | RAM méd. |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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ébito medido comparado com um escalonamento linear perfeito.
| Workers | Medido | Linear ideal | Escalonamento | Eficiência |
|---|---|---|---|---|
| 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 % |
Os quatro testes locais duraram 60 segundos cada num AMD Ryzen 9 9950X. Foram usados 100 fundos leves com cores normais ou ilustrações e ficheiros de cerca de 100 a 300 KB, normalmente próximos de 200 KB. A camada líquida permaneceu desativada e a fonte constante; as cores e posições dos shaders do texto e do círculo variaram entre os cartões.
O tempo de renderização inclui a geração da imagem e a codificação e compressão PNG até ao PNG final. A transferência de rede e o tempo de download não estão incluídos.
O gerador de carga utilizou 1, 2, 4 ou 8 pedidos paralelos consoante o teste. Um novo pedido só começava depois de o PNG anterior ter sido totalmente recebido e validado. A utilização da CPU indicada ocorreu exatamente sob esta carga. O motor gráfico podia distribuir o trabalho por vários núcleos.
* Medições internas não verificadas de forma independente, realizadas nas condições descritas. Não constituem característica assegurada, garantia ou compromisso de desempenho. O desempenho real pode variar consoante o sistema, a carga e o ambiente.
Teste aleatório agressivo
Uma configuração muito mais variável com 1.000 fundos, 237 fontes e efeitos aleatórios em cada pedido.
| Paralelo | Cartões/s | Cartões/min | Renderização méd. | Renderização P50 | Renderização P95 | Renderização P99 | Total méd. | Total P50 | Total P95 | Total P99 | CPU méd. | RAM méd. |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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ébito medido comparado com um escalonamento linear perfeito.
| Workers | Medido | Linear ideal | Escalonamento | Eficiência |
|---|---|---|---|---|
| 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 % |
Estes quatro testes locais duraram 60 segundos cada no mesmo AMD Ryzen 9 9950X. Cada pedido escolheu entre 1.000 fundos mais exigentes de 800 KB a 2,4 MB e 237 fontes. Variaram-se textos, transparência, tamanhos da fonte e do avatar, contorno, cores do texto e do círculo, além do ângulo, força e intensidade dos shaders. Cerca de metade dos cartões também ativou a camada líquida com cor, opacidade e desfocagem aleatórias.
O gerador de carga utilizou 1, 2, 4 ou 8 workers paralelos num ciclo fechado. Cada worker iniciou o pedido seguinte apenas depois de o PNG anterior ter sido totalmente recebido e validado. Todos os testes de 60 segundos terminaram sem erros, timeouts ou PNG inválidos.
O tempo de renderização inclui a geração da imagem e a codificação e compressão PNG até ao PNG final. A transferência de rede e o tempo de download não estão incluídos.
* Medições internas não verificadas de forma independente, realizadas nas condições descritas. Não constituem característica assegurada, garantia ou compromisso de desempenho. O desempenho real pode variar consoante o sistema, a carga e o ambiente.
02 / A nossa base técnica
Além do nosso próprio motor, usamos Rust, Leptos, TypeScript, Vue, Twilight, Dragonfly, PostgreSQL, ScyllaDB e RustFS. Cada tecnologia tem uma função clara no sistema.
Linguagem de programação
Rust é a base do nosso bot para Discord, do backend, do editor de cartões e do motor gráfico Fjell.
Logótipo © Rust Foundation. CC BY 4.0 · sem alterações
Framework web
Usamos Leptos em partes da nossa interface web. O framework leva Rust para o navegador e encaixa naturalmente no restante da nossa stack.
Logótipo dos mantenedores do Leptos. MIT · sem alterações
Linguagem de programação
Escrevemos as páginas públicas interativas em TypeScript. A verificação de tipos ajuda-nos a manter componentes e estados consistentes.
Logótipo oficial do TypeScript. Utilizado segundo as diretrizes da marca.
Framework web
Vue organiza os componentes do site público, da navegação e documentação a outras áreas interativas. Assim, as interfaces mantêm-se coerentes e fáceis de expandir.
Logótipo © Evan You. CC BY-NC-SA 4.0 · sem alterações
Biblioteca para Discord
Utilizamos Twilight para a ligação ao Discord. A biblioteca liga o nosso bot escrito em Rust ao Discord.
Logótipo de Fezhoof. CC BY-SA 4.0 · sem alterações
Base de dados
O Dragonfly é o nosso armazenamento temporário comum para eventos do Discord, coordenação de shards e várias ligações ao gateway, AutoMod e registos de atividade.
Software sob BSL 1.1 com uma autorização de utilização adicional.
Base de dados relacional
O PostgreSQL complementa a nossa camada de dados para dados e estruturas relacionais. Funciona ao lado do ScyllaDB como uma parte separada da nossa infraestrutura.
Base de dados
O ScyllaDB faz parte da nossa camada de dados ao lado do PostgreSQL. Usamos os dois sistemas para tarefas diferentes dentro do Fjord.
Armazenamento de objetos
Usamos RustFS para armazenamento de objetos compatível com S3. Ele adiciona uma camada de armazenamento separada ao lado de PostgreSQL e ScyllaDB.
Software sob Apache 2.0. RustFS é uma marca da RustFS, Inc.