seyfert 3.1.3-dev-15068780757.0 → 3.1.3-dev-15124346914.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/workerclient.d.ts +7 -6
- package/lib/client/workerclient.js +6 -0
- package/lib/websocket/discord/heartbeater.d.ts +17 -0
- package/lib/websocket/discord/heartbeater.js +35 -0
- package/lib/websocket/discord/shard.d.ts +1 -1
- package/lib/websocket/discord/shard.js +4 -6
- package/lib/websocket/discord/shared.d.ts +2 -0
- package/lib/websocket/discord/worker.d.ts +3 -1
- package/lib/websocket/discord/workermanager.d.ts +4 -2
- package/lib/websocket/discord/workermanager.js +39 -22
- package/package.json +1 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { type DeepPartial, type MakeRequired, type When } from '../common';
|
|
1
|
+
import { type Awaitable, type DeepPartial, type MakeRequired, type When } from '../common';
|
|
2
2
|
import { EventHandler } from '../events';
|
|
3
3
|
import type { GatewayDispatchPayload } from '../types';
|
|
4
4
|
import { Shard, type ShardManagerOptions, type WorkerData } from '../websocket';
|
|
5
|
-
import type { WorkerMessages, WorkerShardInfo } from '../websocket/discord/worker';
|
|
5
|
+
import type { ClientHeartbeaterMessages, WorkerMessages, WorkerShardInfo } from '../websocket/discord/worker';
|
|
6
6
|
import type { ManagerMessages, ManagerSpawnShards } from '../websocket/discord/workermanager';
|
|
7
7
|
import type { BaseClientOptions, ServicesOptions, StartOptions } from './base';
|
|
8
8
|
import { BaseClient } from './base';
|
|
9
9
|
import type { Client, ClientOptions } from './client';
|
|
10
10
|
import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate';
|
|
11
11
|
import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate';
|
|
12
|
+
import type { WorkerHeartbeaterMessages } from '../websocket/discord/heartbeater';
|
|
12
13
|
import type { ShardData } from '../websocket/discord/shared';
|
|
13
14
|
import { Collectors } from './collectors';
|
|
14
15
|
import { type ClientUserStructure } from './transformers';
|
|
@@ -35,8 +36,8 @@ export declare class WorkerClient<Ready extends boolean = boolean> extends BaseC
|
|
|
35
36
|
get workerData(): WorkerData;
|
|
36
37
|
start(options?: Omit<DeepPartial<StartOptions>, 'httpConnection' | 'token' | 'connection'>): Promise<void>;
|
|
37
38
|
loadEvents(dir?: string): Promise<void>;
|
|
38
|
-
postMessage(body: WorkerMessages): unknown;
|
|
39
|
-
handleManagerMessages(data: ManagerMessages): Promise<any>;
|
|
39
|
+
postMessage(body: WorkerMessages | ClientHeartbeaterMessages): unknown;
|
|
40
|
+
handleManagerMessages(data: ManagerMessages | WorkerHeartbeaterMessages): Promise<any>;
|
|
40
41
|
calculateShardId(guildId: string): number;
|
|
41
42
|
private generateNonce;
|
|
42
43
|
private generateSendPromise;
|
|
@@ -51,8 +52,8 @@ export interface WorkerClientOptions extends BaseClientOptions {
|
|
|
51
52
|
commands?: NonNullable<Client['options']>['commands'];
|
|
52
53
|
handlePayload?: ShardManagerOptions['handlePayload'];
|
|
53
54
|
gateway?: ClientOptions['gateway'];
|
|
54
|
-
postMessage?: (body: unknown) => unknown
|
|
55
|
+
postMessage?: (body: unknown) => Awaitable<unknown>;
|
|
55
56
|
/** can have perfomance issues in big bots if the client sends every event, specially in startup (false by default) */
|
|
56
57
|
sendPayloadToParent?: boolean;
|
|
57
|
-
handleManagerMessages?(message: ManagerMessages):
|
|
58
|
+
handleManagerMessages?(message: ManagerMessages | WorkerHeartbeaterMessages): Awaitable<unknown>;
|
|
58
59
|
}
|
|
@@ -131,6 +131,12 @@ class WorkerClient extends base_1.BaseClient {
|
|
|
131
131
|
}
|
|
132
132
|
async handleManagerMessages(data) {
|
|
133
133
|
switch (data.type) {
|
|
134
|
+
case 'HEARTBEAT':
|
|
135
|
+
this.postMessage({
|
|
136
|
+
type: 'ACK_HEARTBEAT',
|
|
137
|
+
workerId: workerData.workerId,
|
|
138
|
+
});
|
|
139
|
+
break;
|
|
134
140
|
case 'CACHE_RESULT':
|
|
135
141
|
if (this.cache.adapter instanceof cache_1.WorkerAdapter && this.cache.adapter.promises.has(data.nonce)) {
|
|
136
142
|
const cacheData = this.cache.adapter.promises.get(data.nonce);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Awaitable } from '../../common';
|
|
2
|
+
export type WorkerHeartbeaterMessages = SendHeartbeat;
|
|
3
|
+
export type CreateHeartbeaterMessage<T extends string, D extends object = object> = {
|
|
4
|
+
type: T;
|
|
5
|
+
} & D;
|
|
6
|
+
export type SendHeartbeat = CreateHeartbeaterMessage<'HEARTBEAT'>;
|
|
7
|
+
export declare class Heartbeater {
|
|
8
|
+
sendMethod: (workerId: number, data: WorkerHeartbeaterMessages) => Awaitable<void>;
|
|
9
|
+
interval: number;
|
|
10
|
+
store: Map<number, {
|
|
11
|
+
ack: boolean;
|
|
12
|
+
interval: NodeJS.Timeout;
|
|
13
|
+
}>;
|
|
14
|
+
constructor(sendMethod: (workerId: number, data: WorkerHeartbeaterMessages) => Awaitable<void>, interval: number);
|
|
15
|
+
register(workerId: number, recreate: (workerId: number) => Awaitable<void>): void;
|
|
16
|
+
acknowledge(workerId: number): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Heartbeater = void 0;
|
|
4
|
+
class Heartbeater {
|
|
5
|
+
sendMethod;
|
|
6
|
+
interval;
|
|
7
|
+
store = new Map();
|
|
8
|
+
constructor(sendMethod, interval) {
|
|
9
|
+
this.sendMethod = sendMethod;
|
|
10
|
+
this.interval = interval;
|
|
11
|
+
}
|
|
12
|
+
register(workerId, recreate) {
|
|
13
|
+
if (this.interval <= 0)
|
|
14
|
+
return;
|
|
15
|
+
this.store.set(workerId, {
|
|
16
|
+
ack: true,
|
|
17
|
+
interval: setInterval(() => {
|
|
18
|
+
const heartbeat = this.store.get(workerId);
|
|
19
|
+
if (!heartbeat.ack) {
|
|
20
|
+
heartbeat.ack = true;
|
|
21
|
+
return recreate(workerId);
|
|
22
|
+
}
|
|
23
|
+
heartbeat.ack = false;
|
|
24
|
+
this.sendMethod(workerId, { type: 'HEARTBEAT' });
|
|
25
|
+
}, this.interval),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
acknowledge(workerId) {
|
|
29
|
+
const heartbeat = this.store.get(workerId);
|
|
30
|
+
if (!heartbeat)
|
|
31
|
+
return;
|
|
32
|
+
heartbeat.ack = true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.Heartbeater = Heartbeater;
|
|
@@ -23,7 +23,7 @@ export declare class Shard {
|
|
|
23
23
|
heart: ShardHeart;
|
|
24
24
|
bucket: DynamicBucket;
|
|
25
25
|
offlineSendQueue: ((_?: unknown) => void)[];
|
|
26
|
-
pendingGuilds
|
|
26
|
+
pendingGuilds?: Set<string>;
|
|
27
27
|
options: MakeRequired<ShardOptions, 'properties' | 'ratelimitOptions' | 'reconnectTimeout' | 'connectionTimeout'>;
|
|
28
28
|
isReady: boolean;
|
|
29
29
|
connectionTimeout?: NodeJS.Timeout;
|
|
@@ -24,7 +24,7 @@ class Shard {
|
|
|
24
24
|
};
|
|
25
25
|
bucket;
|
|
26
26
|
offlineSendQueue = [];
|
|
27
|
-
pendingGuilds
|
|
27
|
+
pendingGuilds;
|
|
28
28
|
options;
|
|
29
29
|
isReady = false;
|
|
30
30
|
connectionTimeout;
|
|
@@ -217,15 +217,13 @@ class Shard {
|
|
|
217
217
|
clearTimeout(this.connectionTimeout);
|
|
218
218
|
this.connectionTimeout = undefined;
|
|
219
219
|
if ((0, common_1.hasIntent)(this.options.intents, 'Guilds')) {
|
|
220
|
-
|
|
221
|
-
this.pendingGuilds.add(packet.d.guilds.at(i).id);
|
|
222
|
-
}
|
|
220
|
+
this.pendingGuilds = new Set(...packet.d.guilds.map(guild => guild.id));
|
|
223
221
|
}
|
|
224
222
|
this.data.resume_gateway_url = packet.d.resume_gateway_url;
|
|
225
223
|
this.data.session_id = packet.d.session_id;
|
|
226
224
|
this.offlineSendQueue.map(resolve => resolve());
|
|
227
225
|
this.options.handlePayload(this.id, packet);
|
|
228
|
-
if (this.pendingGuilds
|
|
226
|
+
if (this.pendingGuilds?.size === 0) {
|
|
229
227
|
this.isReady = true;
|
|
230
228
|
this.options.handlePayload(this.id, {
|
|
231
229
|
t: types_1.GatewayDispatchEvents.GuildsReady,
|
|
@@ -237,7 +235,7 @@ class Shard {
|
|
|
237
235
|
}
|
|
238
236
|
case types_1.GatewayDispatchEvents.GuildCreate:
|
|
239
237
|
case types_1.GatewayDispatchEvents.GuildDelete:
|
|
240
|
-
if (this.pendingGuilds
|
|
238
|
+
if (this.pendingGuilds?.delete(packet.d.id)) {
|
|
241
239
|
packet.t = `RAW_${packet.t}`;
|
|
242
240
|
this.options.handlePayload(this.id, packet);
|
|
243
241
|
if (this.pendingGuilds.size === 0) {
|
|
@@ -55,6 +55,8 @@ export interface WorkerManagerOptions extends Omit<ShardManagerOptions, 'handleP
|
|
|
55
55
|
*/
|
|
56
56
|
shardsPerWorker?: number;
|
|
57
57
|
workerProxy?: boolean;
|
|
58
|
+
/** @default 15000 */
|
|
59
|
+
heartbeaterInterval?: number;
|
|
58
60
|
path: string;
|
|
59
61
|
handlePayload?(shardId: number, workerId: number, packet: GatewayDispatchPayload): any;
|
|
60
62
|
handleWorkerMessage?(message: WorkerMessages): any;
|
|
@@ -68,7 +68,9 @@ export type CustomWorkerClientMessages = {
|
|
|
68
68
|
workerId: number;
|
|
69
69
|
} & Identify<CustomWorkerClientEvents[K] extends never ? {} : CustomWorkerClientEvents[K]>>;
|
|
70
70
|
};
|
|
71
|
-
export type
|
|
71
|
+
export type ClientHeartbeaterMessages = ACKHeartbeat;
|
|
72
|
+
export type ACKHeartbeat = CreateWorkerMessage<'ACK_HEARTBEAT'>;
|
|
73
|
+
export type WorkerMessages = ClientHeartbeaterMessages | {
|
|
72
74
|
[K in BaseWorkerMessage['type']]: Identify<Extract<BaseWorkerMessage, {
|
|
73
75
|
type: K;
|
|
74
76
|
}>>;
|
|
@@ -5,6 +5,7 @@ import { type Adapter } from '../../cache';
|
|
|
5
5
|
import { type Identify, type PickPartial } from '../../common';
|
|
6
6
|
import type { GatewayPresenceUpdateData, GatewaySendPayload } from '../../types';
|
|
7
7
|
import { ConnectQueue } from '../structures/timeout';
|
|
8
|
+
import { Heartbeater, type WorkerHeartbeaterMessages } from './heartbeater';
|
|
8
9
|
import type { ShardOptions, WorkerData, WorkerManagerOptions } from './shared';
|
|
9
10
|
import type { WorkerInfo, WorkerMessages, WorkerShardInfo } from './worker';
|
|
10
11
|
export declare class WorkerManager extends Map<number, (ClusterWorker | WorkerThreadsWorker | {
|
|
@@ -31,6 +32,7 @@ export declare class WorkerManager extends Map<number, (ClusterWorker | WorkerTh
|
|
|
31
32
|
rest: ApiHandler;
|
|
32
33
|
reshardingWorkerQueue: (() => void)[];
|
|
33
34
|
private _info?;
|
|
35
|
+
heartbeater: Heartbeater;
|
|
34
36
|
constructor(options: Omit<PickPartial<WorkerManagerOptions, 'token' | 'intents' | 'info' | 'handlePayload' | 'handleWorkerMessage'>, 'resharding'> & {
|
|
35
37
|
resharding?: PickPartial<NonNullable<WorkerManagerOptions['resharding']>, 'getInfo'>;
|
|
36
38
|
});
|
|
@@ -52,8 +54,8 @@ export declare class WorkerManager extends Map<number, (ClusterWorker | WorkerTh
|
|
|
52
54
|
}): Promise<number>;
|
|
53
55
|
calculateShardId(guildId: string): number;
|
|
54
56
|
calculateWorkerId(shardId: number): number;
|
|
55
|
-
postMessage(id: number, body: ManagerMessages): void;
|
|
56
|
-
prepareWorkers(shards: number[][],
|
|
57
|
+
postMessage(id: number, body: ManagerMessages | WorkerHeartbeaterMessages): void;
|
|
58
|
+
prepareWorkers(shards: number[][], rawResharding?: boolean): void;
|
|
57
59
|
createWorker(workerData: WorkerData): ClusterWorker | WorkerThreadsWorker | ({
|
|
58
60
|
ready?: boolean;
|
|
59
61
|
} & {
|
|
@@ -13,6 +13,7 @@ const common_1 = require("../../common");
|
|
|
13
13
|
const constants_1 = require("../constants");
|
|
14
14
|
const structures_1 = require("../structures");
|
|
15
15
|
const timeout_1 = require("../structures/timeout");
|
|
16
|
+
const heartbeater_1 = require("./heartbeater");
|
|
16
17
|
class WorkerManager extends Map {
|
|
17
18
|
static prepareSpaces(options, logger) {
|
|
18
19
|
logger?.info('Preparing buckets');
|
|
@@ -35,6 +36,7 @@ class WorkerManager extends Map {
|
|
|
35
36
|
rest;
|
|
36
37
|
reshardingWorkerQueue = [];
|
|
37
38
|
_info;
|
|
39
|
+
heartbeater;
|
|
38
40
|
constructor(options) {
|
|
39
41
|
super();
|
|
40
42
|
this.options = options;
|
|
@@ -46,6 +48,7 @@ class WorkerManager extends Map {
|
|
|
46
48
|
return oldFn(message);
|
|
47
49
|
};
|
|
48
50
|
}
|
|
51
|
+
this.heartbeater = new heartbeater_1.Heartbeater(this.postMessage.bind(this), options.heartbeaterInterval ?? 15e3);
|
|
49
52
|
}
|
|
50
53
|
setCache(adapter) {
|
|
51
54
|
this.cacheAdapter = adapter;
|
|
@@ -101,7 +104,8 @@ class WorkerManager extends Map {
|
|
|
101
104
|
return this.debugger?.error(`Worker ${id} does not exists.`);
|
|
102
105
|
switch (this.options.mode) {
|
|
103
106
|
case 'clusters':
|
|
104
|
-
worker.
|
|
107
|
+
if (worker.isConnected())
|
|
108
|
+
worker.send(body);
|
|
105
109
|
break;
|
|
106
110
|
case 'threads':
|
|
107
111
|
worker.postMessage(body);
|
|
@@ -111,33 +115,40 @@ class WorkerManager extends Map {
|
|
|
111
115
|
break;
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
|
-
prepareWorkers(shards,
|
|
118
|
+
prepareWorkers(shards, rawResharding = false) {
|
|
115
119
|
const worker_threads = (0, common_1.lazyLoadPackage)('node:worker_threads');
|
|
116
120
|
if (!worker_threads)
|
|
117
121
|
throw new Error('Cannot prepare workers without worker_threads.');
|
|
118
122
|
for (let i = 0; i < shards.length; i++) {
|
|
123
|
+
const registerWorker = (resharding) => {
|
|
124
|
+
const worker = this.createWorker({
|
|
125
|
+
path: this.options.path,
|
|
126
|
+
debug: this.options.debug,
|
|
127
|
+
token: this.options.token,
|
|
128
|
+
shards: shards[i],
|
|
129
|
+
intents: this.options.intents,
|
|
130
|
+
workerId: i,
|
|
131
|
+
workerProxy: this.options.workerProxy,
|
|
132
|
+
totalShards: resharding ? this._info.shards : this.totalShards,
|
|
133
|
+
mode: this.options.mode,
|
|
134
|
+
resharding,
|
|
135
|
+
totalWorkers: shards.length,
|
|
136
|
+
info: {
|
|
137
|
+
...this.options.info,
|
|
138
|
+
shards: this.totalShards,
|
|
139
|
+
},
|
|
140
|
+
compress: this.options.compress,
|
|
141
|
+
});
|
|
142
|
+
this.set(i, worker);
|
|
143
|
+
};
|
|
119
144
|
const workerExists = this.has(i);
|
|
120
|
-
if (
|
|
121
|
-
this[
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
shards: shards[i],
|
|
127
|
-
intents: this.options.intents,
|
|
128
|
-
workerId: i,
|
|
129
|
-
workerProxy: this.options.workerProxy,
|
|
130
|
-
totalShards: resharding ? this._info.shards : this.totalShards,
|
|
131
|
-
mode: this.options.mode,
|
|
132
|
-
resharding,
|
|
133
|
-
totalWorkers: shards.length,
|
|
134
|
-
info: {
|
|
135
|
-
...this.options.info,
|
|
136
|
-
shards: this.totalShards,
|
|
137
|
-
},
|
|
138
|
-
compress: this.options.compress,
|
|
145
|
+
if (rawResharding || !workerExists) {
|
|
146
|
+
this[rawResharding ? 'reshardingWorkerQueue' : 'workerQueue'].push(() => {
|
|
147
|
+
registerWorker(rawResharding);
|
|
148
|
+
this.heartbeater.register(i, () => {
|
|
149
|
+
this.delete(i);
|
|
150
|
+
registerWorker(false);
|
|
139
151
|
});
|
|
140
|
-
this.set(i, worker);
|
|
141
152
|
});
|
|
142
153
|
}
|
|
143
154
|
}
|
|
@@ -170,6 +181,9 @@ class WorkerManager extends Map {
|
|
|
170
181
|
env,
|
|
171
182
|
});
|
|
172
183
|
worker.on('message', data => this.handleWorkerMessage(data));
|
|
184
|
+
worker.on('error', err => {
|
|
185
|
+
this.debugger?.error(`[Worker #${workerData.workerId}]`, err);
|
|
186
|
+
});
|
|
173
187
|
return worker;
|
|
174
188
|
}
|
|
175
189
|
case 'clusters': {
|
|
@@ -204,6 +218,9 @@ class WorkerManager extends Map {
|
|
|
204
218
|
}
|
|
205
219
|
async handleWorkerMessage(message) {
|
|
206
220
|
switch (message.type) {
|
|
221
|
+
case 'ACK_HEARTBEAT':
|
|
222
|
+
this.heartbeater.acknowledge(message.workerId);
|
|
223
|
+
break;
|
|
207
224
|
case 'WORKER_READY_RESHARDING':
|
|
208
225
|
{
|
|
209
226
|
this.get(message.workerId).resharded = true;
|