Spraxium logoSpraxium

Visão Geral do Sharding

O sharding distribui o bot em vários processos para escalar além do limite de guilds de um único processo. Aprenda a configurar o SpraxiumShardManager, ajustar opções de spawn e detectar processos filhos de shard.

O que o sharding resolve

O Discord exige sharding para bots presentes em mais de 2.500 guilds. Cada shard é uma conexão WebSocket separada, responsável por um subconjunto dessas guilds. Sem sharding, a conexão única vira gargalo e o Discord simplesmente deixa de aceitar a conexão quando o bot passa desse limite. Mesmo abaixo de 2.500 guilds, sharding pode melhorar a confiabilidade porque isola falhas em um shard, em vez de derrubar o bot inteiro.

O SpraxiumShardManager encapsula o ShardingManager do discord.js e adiciona logging de lifecycle, tratamento de erro para rate limit, suporte automático a processos filhos em TypeScript, handlers de shutdown limpo e uma tabela-resumo impressa depois que todos os shards ficam prontos. A configuração acontece via useSharding em main.ts, e o framework cuida do resto.

Quando o sharding está ativado, o processo principal, o processo pai, não conecta no gateway por conta própria. Em vez disso, ele sobe processos filhos, um por shard. Cada processo filho roda o bot completo, incluindo listeners, comandos e serviços. O pai apenas gerencia esses filhos e coleta o status deles.

Ativando o sharding

Chame useSharding no builder da aplicação em main.ts, pelo menos com um valor para totalShards. Definir 'auto' faz o Discord decidir a quantidade ideal de shards com base no número de guilds do bot e no valor recomendado retornado por /gateway/bot. Para bots pequenos ou ambiente de desenvolvimento, vale usar um número fixo como 1 ou 2.

src/main.ts
import { SpraxiumFactory } from '@spraxium/core';
import { AppModule } from './app.module';

async function main() {
  const app = await SpraxiumFactory.create({
    token: process.env.DISCORD_TOKEN,
  });
  app.useModule(AppModule);
  app.useSharding({ totalShards: 'auto' });
  await app.listen();
}

main();

Você também pode aplicar a configuração de sharding condicionalmente com base em um check de runtime. Isso é útil quando o mesmo arquivo de entrada precisa rodar em processo único no desenvolvimento e em modo sharded na produção.

src/main.ts
import { SpraxiumFactory } from '@spraxium/core';
import { AppModule } from './app.module';

async function main() {
  const app = await SpraxiumFactory.create({
    token: process.env.DISCORD_TOKEN,
  });
  app.useModule(AppModule);

  if (process.env.ENABLE_SHARDING === 'true') {
    app.useSharding({ totalShards: 'auto' });
  }

  await app.listen();
}

main();

Opções de shard

A interface ShardOptions aceita quatro campos. Só totalShards é obrigatório. Os padrões foram pensados para funcionar sem ajuste na maioria dos bots, respeitando o identify rate limit do Discord com um atraso seguro de 5,5 segundos entre spawns.

OpçãoTipoPadrãoDescrição
totalShardsNúmero ou auto(obrigatório)

Quantidade de shards para subir, ou auto para deixar a decisão com o Discord.

shardListLista de númerostodos os shards

Subconjunto de IDs de shard que devem ser iniciados. Útil para rodar shards específicos em máquinas específicas.

respawnbooleantrue

Define se um shard deve ser reiniciado automaticamente ao cair.

spawnDelaynumber5500

Tempo, em milissegundos, entre o spawn de um shard e outro, respeitando o identify rate limit.

Ciclo de spawn

Quando o bot sobe com sharding ativado, o processo pai percorre uma sequência bem definida. Primeiro ele cria um ShardingManager do discord.js apontando para o seu arquivo de entrada. Depois sobe os processos filhos um por um, com o delay configurado. Cada filho conecta no gateway do Discord, dispara clientReady e reporta a quantidade de guilds de volta ao processo pai. Quando todos os shards ficam prontos, o pai imprime uma tabela-resumo.

O processo pai escuta três eventos em cada shard: disconnect, reconnecting e death. Desconexões e reconexões entram no log como aviso. Mortes entram como erro e, se respawn estiver ligado, o discord.js sobe automaticamente um processo substituto. O pai continua rodando enquanto pelo menos um shard continuar vivo.

Durante o spawn, a saída do terminal fica parecida com isto. Cada shard informa o próprio status e a quantidade de guilds quando fica pronto, seguido por uma linha-resumo com a tag do bot, o total de shards, o total de guilds e o tempo de startup.

terminal
  [bot]   spraxium  v1.0.0  discord.js v14.x

  [spawn] Sharding   spawning 3 shards...

  ⬺ Shard 0    ready (1200 guilds)
  ⬺ Shard 1    ready (1150 guilds)
  ⬺ Shard 2    ready (980 guilds)

  [ready] MyBot#1234  ·  3 shards  ·  3330 total guilds  ·  ready in 18200ms

Detectando processos filhos de shard

A função utilitária isShardChild retorna true quando o processo atual foi iniciado pelo ShardingManager. O discord.js injeta a variável de ambiente SHARDS em cada processo filho, e isShardChild verifica justamente a presença dessa variável. Isso é útil para pular setup que só deve rodar uma vez no processo pai, como imprimir banner ou registrar slash commands globais.

src/services/startup.service.ts
import { Injectable } from '@spraxium/core';
import { isShardChild } from '@spraxium/core';

@Injectable()
export class StartupService {
  async onApplicationBoot() {
    if (isShardChild()) return;
    console.log('Running parent-only initialization');
  }
}

O framework usa isShardChild internamente para pular o banner de startup nos processos filhos, evitando saída duplicada em N shards, e também para suprimir a mensagem “Logged in as”, que do contrário apareceria N vezes.

Aviso para desenvolvimento

Quando o sharding está ativo no modo de desenvolvimento (NODE_ENV=development), o framework registra um aviso. Vários processos de shard deixam os restarts mais lentos, aumentam o uso de memória e elevam a chance de atingir rate limits do Discord quando você reinicia o bot com frequência. Em desenvolvimento, prefira uma única instância, seja deixando sharding desligado, seja definindo totalShards como 1.

src/main.ts
import { SpraxiumFactory } from '@spraxium/core';
import { AppModule } from './app.module';

async function main() {
  const app = await SpraxiumFactory.create({
    token: process.env.DISCORD_TOKEN,
  });
  app.useModule(AppModule);
  app.useSharding({
    totalShards: process.env.NODE_ENV === 'production' ? 'auto' : 1,
  });
  await app.listen();
}

main();