ShardingManager Woes

So my main project is still growing and I'm by no means at a point where sharding is required, but I'm also not one to put off something that I know will be a headache later on. With that in mind I have implemented a sharding setup in my application now following the official guide. My application works and starts and given the lower guild count, only one shard gets spawned (shard 0). I've done this following many comments from people advising there is no harm in having it setup now to save me work later on. My issue comes from the fact that no matter what I do, the shard property on client is always null even though I know the client was started within a shard. This means I'm unable to use any of the shard utilities like broadcastEval and fetchClientValues for instance. I've asked for general help regarding this in #djs-help-v14 but I'm hoping with it's own thread here, someone might have more insight on what I could be doing wrong. Disclosure: I'm using DJS Dev at the moment, but I've been having this same behaviour with 14.7.1 and also now 14.8.0 as well. DJS: discord.js@14.8.1-dev.1678752596-41077c9.0 Node: 19.3.0
5 Replies
Unknown User
Unknown User•16mo ago
Message Not Public
Sign In & Join Server To View
sludge
sludge•16mo ago
sludge
sludge•16mo ago
import { ExtendedShardingManager } from './structures/ShardingManager';
import { fastifyInit } from './utils/fastify';

export const shardMgr = new ExtendedShardingManager(`${__dirname}/${process.env.CLIENT_FILE}`, {
token: process.env.CLIENT_TOKEN,
execArgv: ['-r', 'ts-node/register']
});

shardMgr.on('shardCreate', (shard) => {
shardMgr.logger.info(`Shard Started: ID ${shard.id}`);
});

process.on('unhandledRejection', (error: Error) => {
shardMgr.logger.error(error?.message ? error.message : error);
error?.stack ? shardMgr.logger.error(error.stack) : null;
});

process.on('warning', (warn: Error) => warn.message.includes('experimental') ? null : shardMgr.logger.warn(warn.message));

fastifyInit();
shardMgr.spawn();
import { ExtendedShardingManager } from './structures/ShardingManager';
import { fastifyInit } from './utils/fastify';

export const shardMgr = new ExtendedShardingManager(`${__dirname}/${process.env.CLIENT_FILE}`, {
token: process.env.CLIENT_TOKEN,
execArgv: ['-r', 'ts-node/register']
});

shardMgr.on('shardCreate', (shard) => {
shardMgr.logger.info(`Shard Started: ID ${shard.id}`);
});

process.on('unhandledRejection', (error: Error) => {
shardMgr.logger.error(error?.message ? error.message : error);
error?.stack ? shardMgr.logger.error(error.stack) : null;
});

process.on('warning', (warn: Error) => warn.message.includes('experimental') ? null : shardMgr.logger.warn(warn.message));

fastifyInit();
shardMgr.spawn();
ExtendedShardingManager is simply the normal ShardingManager but with my custom logger added to it and fastifyInit is just my fastify stuff being started. need to start it with shard manager otherwise it tries to start multiple times with the clients sure
import { ShardingManager, ShardingManagerOptions } from 'discord.js';
import { eventLogger } from '../utils/logger';

export class ExtendedShardingManager extends ShardingManager {
constructor(file: string, options: ShardingManagerOptions) {
super(file, options)
}
logger = eventLogger;
}
import { ShardingManager, ShardingManagerOptions } from 'discord.js';
import { eventLogger } from '../utils/logger';

export class ExtendedShardingManager extends ShardingManager {
constructor(file: string, options: ShardingManagerOptions) {
super(file, options)
}
logger = eventLogger;
}
eventLogger is a winston instance only imports logger does are the things it needs for itself:
import { createLogger, format, transports, addColors, config, Logger, LeveledLogMethod } from 'winston';
import { createLogger, format, transports, addColors, config, Logger, LeveledLogMethod } from 'winston';
understood. I added the execArgv because I needed to be able to test it was working locally in my dev instance before committing it live. on production, CLIENT_FILE is a file named bot.js, on dev it's uncompiled bot.ts an overview would be: main process; sharding manager, fastify, winston child process: djs client, prisma KEK this should be fun too big for discord, one sec https://sourceb.in/4YkyMPL6RK just to be clear, everything else works as intended. the client starts, all commands etc work. its literally just shard thats always null yeah of course, uno momento https://sourceb.in/eSa33PWj1R thats exactly what I thought too but it's really confusing me mhm doesn't even have a token prop when I console log it https://sourceb.in/udYf9y6pBe but it shows shardCount and the shards array, and shard is still null 🙃 is it possible I'm just being denied shard usage because the application is in too few guilds? I don't
d.js docs
d.js docs•16mo ago
Please add the following code to your code base outside of any other event listeners and provide the full log output relevant to your issue.
client
.on("debug", console.log)
.on("warn", console.log)
client
.on("debug", console.log)
.on("warn", console.log)
• Note: if you initialize your Client as bot or other identifiers you need to use these instead of client • If the output is too long to post consider using a bin instead: gist | paste.gg | sourceb.in | hastebin
sludge
sludge•16mo ago
oh ok, one second I did debug too, so here's that first of all for the application start
Provided token: OTc0Nzk0NTc4MjkwOTYyNDYy.Gegalf.**************************************
Preparing to connect to the gateway...
2023-03-16 12:56:05 Webhooks Listening (http://0.0.0.0:9119) <--- from my logger
[WS => Manager] Fetched Gateway Information
URL: wss://gateway.discord.gg
Recommended Shards: 1
[WS => Manager] Session Limit Information
Total: 1000
Remaining: 928
[WS => Manager] Spawning shards: 0
[WS => Shard 0] [CONNECT]
Gateway : wss://gateway.discord.gg/
Version : 10
Encoding : json
Compression: none
[WS => Shard 0] Setting a HELLO timeout for 20s.
2023-03-16 12:56:09 Shard Started: ID 0 <--- from my logger
[WS => Shard 0] [CONNECTED] Took 195ms
[WS => Shard 0] Clearing the HELLO timeout.
[WS => Shard 0] Setting a heartbeat interval for 41250ms.
[WS => Shard 0] [IDENTIFY] Shard 0/1 with intents: 4675
[WS => Shard 0] [READY] Session 4abdff85b4bfb7a0d5ec492daec6c3ca | Resume url wss://gateway-us-east1-b.discord.gg.
[WS => Shard 0] [ReadyHeartbeat] Sending a heartbeat.
[WS => Shard 0] Shard received all its guilds. Marking as fully ready.
[WS => Shard 0] Heartbeat acknowledged, latency of 122ms.
Provided token: OTc0Nzk0NTc4MjkwOTYyNDYy.Gegalf.**************************************
Preparing to connect to the gateway...
[WS => Manager] Fetched Gateway Information
URL: wss://gateway.discord.gg
Recommended Shards: 1
[WS => Manager] Session Limit Information
Total: 1000
Remaining: 927
[WS => Manager] Spawning shards: 0
[WS => Shard 0] [CONNECT]
Gateway : wss://gateway.discord.gg/
Version : 10
Encoding : json
Compression: none
[WS => Shard 0] Setting a HELLO timeout for 20s.
[WS => Shard 0] [CONNECTED] Took 185ms
[WS => Shard 0] Clearing the HELLO timeout.
[WS => Shard 0] Setting a heartbeat interval for 41250ms.
[WS => Shard 0] [IDENTIFY] Shard 0/1 with intents: 4675
[WS => Shard 0] [READY] Session 340304cab7087ab2a06f19aaacab500f | Resume url wss://gateway-us-east1-b.discord.gg.
[WS => Shard 0] [ReadyHeartbeat] Sending a heartbeat.
[WS => Shard 0] Shard received all its guilds. Marking as fully ready.
[WS => Shard 0] Heartbeat acknowledged, latency of 119ms.
Provided token: OTc0Nzk0NTc4MjkwOTYyNDYy.Gegalf.**************************************
Preparing to connect to the gateway...
2023-03-16 12:56:05 Webhooks Listening (http://0.0.0.0:9119) <--- from my logger
[WS => Manager] Fetched Gateway Information
URL: wss://gateway.discord.gg
Recommended Shards: 1
[WS => Manager] Session Limit Information
Total: 1000
Remaining: 928
[WS => Manager] Spawning shards: 0
[WS => Shard 0] [CONNECT]
Gateway : wss://gateway.discord.gg/
Version : 10
Encoding : json
Compression: none
[WS => Shard 0] Setting a HELLO timeout for 20s.
2023-03-16 12:56:09 Shard Started: ID 0 <--- from my logger
[WS => Shard 0] [CONNECTED] Took 195ms
[WS => Shard 0] Clearing the HELLO timeout.
[WS => Shard 0] Setting a heartbeat interval for 41250ms.
[WS => Shard 0] [IDENTIFY] Shard 0/1 with intents: 4675
[WS => Shard 0] [READY] Session 4abdff85b4bfb7a0d5ec492daec6c3ca | Resume url wss://gateway-us-east1-b.discord.gg.
[WS => Shard 0] [ReadyHeartbeat] Sending a heartbeat.
[WS => Shard 0] Shard received all its guilds. Marking as fully ready.
[WS => Shard 0] Heartbeat acknowledged, latency of 122ms.
Provided token: OTc0Nzk0NTc4MjkwOTYyNDYy.Gegalf.**************************************
Preparing to connect to the gateway...
[WS => Manager] Fetched Gateway Information
URL: wss://gateway.discord.gg
Recommended Shards: 1
[WS => Manager] Session Limit Information
Total: 1000
Remaining: 927
[WS => Manager] Spawning shards: 0
[WS => Shard 0] [CONNECT]
Gateway : wss://gateway.discord.gg/
Version : 10
Encoding : json
Compression: none
[WS => Shard 0] Setting a HELLO timeout for 20s.
[WS => Shard 0] [CONNECTED] Took 185ms
[WS => Shard 0] Clearing the HELLO timeout.
[WS => Shard 0] Setting a heartbeat interval for 41250ms.
[WS => Shard 0] [IDENTIFY] Shard 0/1 with intents: 4675
[WS => Shard 0] [READY] Session 340304cab7087ab2a06f19aaacab500f | Resume url wss://gateway-us-east1-b.discord.gg.
[WS => Shard 0] [ReadyHeartbeat] Sending a heartbeat.
[WS => Shard 0] Shard received all its guilds. Marking as fully ready.
[WS => Shard 0] Heartbeat acknowledged, latency of 119ms.
warn never gets fired for anything ... I don't know how or why client would be starting twice
import { ExtendedClient } from './structures/Client';

export const client = new ExtendedClient();

client.on('warn', console.log)

client.start();
import { ExtendedClient } from './structures/Client';

export const client = new ExtendedClient();

client.on('warn', console.log)

client.start();
the start method you've seen in my ExtendedClient class above also, removed the debug event because I was checking just warn um, did that. but I'm doing it before the start method runs so its mostly null/useless https://sourceb.in/nkUFTakLV1 I do note the very first line though about "Multiple clients created" nope haha wait what do you mean ohhh I cheated and eval'd it. sorry, I'll log it in the code of course, I thought it'd be easier. my apologies so just to see what could be happening, I decided to log this.shard inside the start method, as the last thing and it fires once immediately as null, then my fastify loads and sharding manager spawns the shards and then it fires again but with the expected data so you're right, multiple clients are starting but I don't understand how or why my code isn't elegant but I'm following the same logic the guide does, letting the sharding manager be the one to load the client PeepoSalute I'll come back with the results soon omg. I actually worked it out. even though it's a gateway application, I use HTTP for interactions. I do this because I proxy them through my webhook server, so I can set the bot to maintenance mode if I want and users get a helpful response if the bot is offline, rather than red "Application did not respond" text. I do this by listening for a forwarded HTTP request using fastify, and then forward the APIInteraction from discord to client.actions.InteractionCreate.handle. to do this, I was importing client from bot and fastify was loading this with itself along with the shard manager. effectively spawning a client actually a dumb problem to have haha. all my own doing as well understood, thank you for being so patient with me on this too any idea what would be the best way of making client available to this file without importing it the way I am here?
import { APIInteraction } from 'discord.js';
import { FastifyRequest } from 'fastify';
import { client } from '../bot';

export const interactionWebhook = async function(req: FastifyRequest) {
const interaction = req.body as APIInteraction;
//@ts-ignore
client.actions.InteractionCreate.handle(interaction);
}
import { APIInteraction } from 'discord.js';
import { FastifyRequest } from 'fastify';
import { client } from '../bot';

export const interactionWebhook = async function(req: FastifyRequest) {
const interaction = req.body as APIInteraction;
//@ts-ignore
client.actions.InteractionCreate.handle(interaction);
}
actually, don't worry. this is basic JS and I need to work it out on my own entirely for user QoL. if the application is offline for whatever reason, you'll get met with an ugly red text saying it didn't respond. but with this, I can actually respond on my webhook server that forwards the interaction, stating that the application is offline its low priority, I'll get rid of it for now thats fair. in hindsight, I typically know if I've forced the bot offline so in those instances I'll just add the interactions endpoint URL in dev portal as needed