Qué resuelve el sharding
Discord exige que los bots en más de 2,500 guilds usen sharding. Cada shard es una conexión WebSocket independiente que maneja un subconjunto de guilds. Sin sharding, la conexión única se convierte en un cuello de botella y Discord rechaza la conexión del bot cuando supera el umbral. Incluso por debajo de 2,500 guilds, el sharding puede mejorar la confiabilidad al aislar fallos en un solo shard en lugar de tumbar todo el bot.
El SpraxiumShardManager envuelve el ShardingManager de discord.js y agrega logging del ciclo de vida, manejo de errores por rate limit, soporte automático de TypeScript para procesos hijo, handlers de apagado limpio y una tabla de resumen que se imprime cuando todos los shards están listos. Configuras el sharding llamando a useSharding en main.ts, y el framework se encarga de todo lo demás.
Cuando el sharding está habilitado, el proceso principal (el padre) no se conecta por sí mismo al gateway. En su lugar, crea procesos hijo, uno por shard. Cada proceso hijo ejecuta el código completo de tu bot, incluyendo listeners, commands y services. El proceso padre solo administra los hijos y recopila información de estado.
Habilitar sharding
Llama a useSharding en el constructor de la aplicación dentro de main.ts con al menos un valor para totalShards. Configurarlo como 'auto' deja que Discord decida la cantidad óptima de shards según el número de guilds de tu bot y el valor recomendado del endpoint /gateway/bot. Para bots pequeños o para desarrollo, usa un número fijo como 1 o 2.
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();También puedes pasar opciones de sharding de forma condicional según una verificación en runtime. Esto es útil cuando quieres que el mismo archivo de entrada se ejecute como proceso único en desarrollo y como proceso shardeado en producción.
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();Opciones de shard
La interfaz ShardOptions acepta cuatro campos. Solo totalShards es obligatorio. Los valores por defecto están diseñados para funcionar sin configuración adicional en la mayoría de los bots, respetando el rate limit de identify de Discord con un retraso seguro de 5.5 segundos entre cada spawn.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
| totalShards | Número o auto | (obligatorio) | Cantidad de shards a crear, o auto para que Discord la decida. |
| shardList | Lista de números | todos los shards | Subconjunto de IDs de shard a crear. Útil para ejecutar shards específicos en máquinas específicas. |
| respawn | boolean | true | Define si un shard debe reiniciarse automáticamente si falla. |
| spawnDelay | number | 5500 | Milisegundos de espera entre la creación de cada shard, respetando el rate limit de identify. |
Ciclo de vida del spawn
Cuando tu bot inicia con sharding habilitado, el proceso padre sigue una secuencia específica. Primero crea un ShardingManager de discord.js apuntando a tu archivo de entrada. Luego crea los procesos hijo uno por uno con el retraso configurado. Cada hijo se conecta al gateway de Discord, dispara el evento clientReady y reporta su cantidad de guilds al padre. Cuando todos los shards están listos, el padre imprime una tabla de resumen.
El proceso padre escucha tres eventos en cada shard: disconnect, reconnecting y death. Las desconexiones y reconexiones se registran como advertencias. Las muertes se registran como errores y, si respawn está habilitado, discord.js crea automáticamente un proceso de reemplazo. El proceso padre sigue en ejecución mientras al menos un shard esté vivo.
La salida de consola durante el spawn se ve así. Cada shard imprime su estado y cantidad de guilds cuando está listo, seguido de una línea de resumen con el tag del bot, el total de shards, el total de guilds y el tiempo de arranque.
★ spraxium v1.0.0 discord.js v14.x
⟳ Sharding spawning 3 shards...
› Shard 0 ready (1200 guilds)
› Shard 1 ready (1150 guilds)
› Shard 2 ready (980 guilds)
◆ MyBot#1234 · 3 shards · 3330 total guilds · ready in 18200msDetección de procesos hijo de shard
La función utilitaria isShardChild devuelve true cuando el proceso actual fue creado por el ShardingManager. Discord.js inyecta una variable de entorno SHARDS en cada proceso hijo, y isShardChild comprueba su presencia. Esto es útil para omitir configuración que solo debe ejecutarse una vez en el padre, como imprimir un banner o registrar slash commands globales.
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');
}
}El framework usa isShardChild internamente para omitir el banner de arranque en procesos hijo (evitando salida duplicada entre N shards) y para suprimir el mensaje "Logged in as" que, de otro modo, se imprimiría N veces.
Advertencia para desarrollo
Cuando el sharding está habilitado en modo desarrollo (NODE_ENV=development), el framework registra una advertencia. Múltiples procesos de shard ralentizan los reinicios, incrementan el uso de memoria y tienen más probabilidad de alcanzar los rate limits de Discord al reiniciar con frecuencia. Para desarrollo, usa una sola instancia: no configures sharding o establece totalShards en 1.
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();