@@ -21,7 +21,7 @@ import type { NodeManager } from "./NodeManager";
2121import type {
2222 BaseNodeStats , LavalinkInfo , LavalinkNodeOptions , LyricsResult , ModifyRequest , NodeLinkConnectionMetrics , NodeStats , SponsorBlockSegment
2323} from "./Types/Node" ;
24- import { NodeLinkEventPayload , NodeLinkEventTypes , HealthStatusThreshold , HealthStatusKeys , HealthPerformanceKeys , NodeMetricSummary } from "./Types/NodeLink" ;
24+ import { NodeLinkEventPayload , NodeLinkEventTypes , HealthStatusThreshold , HealthStatusKeys , HealthPerformanceKeys , NodeMetricSummary , HealthStatusObject , HealthStatusThresholdOptions } from "./Types/NodeLink" ;
2525/**
2626 * Lavalink Node creator class
2727 */
@@ -181,14 +181,6 @@ export class LavalinkNode {
181181 ...options
182182 } ;
183183
184- // Allow custom health/capacity thresholds via options.healthThresholds
185- this . healthThresholds = options . healthThresholds || {
186- cpu : { excellent : 0.3 , good : 0.5 , fair : 0.7 , poor : 0.85 } ,
187- memory : { excellent : 60 , good : 75 , fair : 85 , poor : 95 } ,
188- ping : { excellent : 50 , good : 100 , fair : 200 , poor : 300 } ,
189- frameDeficit : { overload : 100 , critical : 500 }
190- } ;
191-
192184 this . NodeManager = manager ;
193185 this . validate ( ) ;
194186 if ( this . options . secure && this . options . port !== 443 ) throw new SyntaxError ( "If secure is true, then the port must be 443" ) ;
@@ -933,25 +925,29 @@ export class LavalinkNode {
933925 return await this . request ( `/info` ) as LavalinkInfo ;
934926 }
935927
936-
928+ /**
929+ * Returns the metric summary of the node
930+ * @returns the metric summary of the node
931+ */
937932 public nodeMetricSummary ( ) : NodeMetricSummary {
938933 if ( ! this . connected || ! this . isAlive ) return { systemLoad : 0 , cpuLoad : 0 , memoryUsage : 0 , players : 0 , playingPlayers : 0 , uptime : 0 , ping : 0 , frameDeficit : 0 }
939- const cpuLoad = this . stats . cpu . lavalinkLoad ;
940- const systemLoad = this . stats . cpu . systemLoad ;
941934 const _memoryUsed = this . stats . memory . used ;
942935 const _memoryAllocated = this . stats . memory . allocated ;
943- const memoryUsage = _memoryAllocated > 0 ? ( _memoryUsed / _memoryAllocated ) * 100 : 0 ;
944- const players = this . stats . players ;
945- const playingPlayers = this . stats . playingPlayers ;
946- const frameDeficit = this . stats . frameStats ?. deficit || 0 ;
947- const ping = this . heartBeatPing ;
948- const uptime = this . stats . uptime ;
949- return { systemLoad, cpuLoad, memoryUsage, players, playingPlayers, uptime, ping, frameDeficit }
936+ return {
937+ systemLoad : this . stats . cpu . systemLoad ,
938+ cpuLoad : this . stats . cpu . lavalinkLoad ,
939+ memoryUsage : _memoryAllocated > 0 ? ( _memoryUsed / _memoryAllocated ) * 100 : 0 ,
940+ players : this . stats . players ,
941+ playingPlayers : this . stats . playingPlayers ,
942+ uptime : this . stats . uptime ,
943+ ping : this . heartBeatPing ,
944+ frameDeficit : this . stats . frameStats ?. deficit || 0
945+ }
950946 }
951947 /**
952948 * Get the node's health status with performance assessment.
953949 * @returns Object containing health status, performance rating, load balancing info, and recommendations
954- *
950+ *
955951 * @example
956952 * ```ts
957953 * const health = node.getHealthStatus();
@@ -966,31 +962,13 @@ export class LavalinkNode {
966962 * }
967963 * ```
968964 */
969- public getHealthStatus ( thresholds ?: { cpu : Partial < HealthStatusThreshold > , memory : Partial < HealthStatusThreshold > , ping : Partial < HealthStatusThreshold > } ) : {
970- status : HealthStatusKeys ;
971- performance : HealthPerformanceKeys ;
972- isOverloaded : boolean ;
973- needsRestart : boolean ;
974- penaltyScore : number ;
975- estimatedRemainingCapacity : number ;
976- recommendations : string [ ] ;
977- metrics : {
978- cpuLoad : number ;
979- memoryUsage : number ;
980- players : number ;
981- playingPlayers : number ;
982- uptime : number ;
983- ping : number ;
984- frameDeficit : number ;
985- } ;
986- } {
965+ public getHealthStatus ( thresholds ?: HealthStatusThresholdOptions ) : HealthStatusObject {
987966 const cpuThresholds : HealthStatusThreshold = { excellent : 0.3 , good : 0.5 , fair : 0.7 , poor : 0.85 , ...thresholds ?. cpu } ;
988967 const memoryThresholds : HealthStatusThreshold = { excellent : 60 , good : 75 , fair : 85 , poor : 95 , ...thresholds ?. memory } ;
989968 const pingThresholds : HealthStatusThreshold = { excellent : 50 , good : 100 , fair : 200 , poor : 300 , ...thresholds ?. ping } ;
990969 const recommendations : string [ ] = [ ] ;
991970 const metrics = this . nodeMetricSummary ( ) ;
992- const { systemLoad, cpuLoad, memoryUsage, players, playingPlayers, uptime, ping, frameDeficit } = metrics ;
993-
971+
994972 // Check if node is offline
995973 if ( ! this . connected || ! this . isAlive ) {
996974 return {
@@ -1000,32 +978,32 @@ export class LavalinkNode {
1000978 needsRestart : true ,
1001979 penaltyScore : 999999 , // Maximum penalty for offline nodes
1002980 estimatedRemainingCapacity : 0 ,
1003- recommendations : [ RecommendationsStrings . nodeOffline , RecommendationsStrings . checkConnectivity ] ,
981+ recommendations : [ RecommendationsStrings . nodeOffline , RecommendationsStrings . checkConnectivity ] ,
1004982 metrics
1005983 } ;
1006984 }
1007985
1008986
1009987 // Assess CPU performance
1010988 let cpuScore = 0 ;
1011- if ( cpuLoad < cpuThresholds . excellent ) cpuScore = 4 ;
1012- else if ( cpuLoad < cpuThresholds . good ) cpuScore = 3 ;
1013- else if ( cpuLoad < cpuThresholds . fair ) cpuScore = 2 ;
1014- else if ( cpuLoad < cpuThresholds . poor ) cpuScore = 1 ;
989+ if ( metrics . cpuLoad < cpuThresholds . excellent ) cpuScore = 4 ;
990+ else if ( metrics . cpuLoad < cpuThresholds . good ) cpuScore = 3 ;
991+ else if ( metrics . cpuLoad < cpuThresholds . fair ) cpuScore = 2 ;
992+ else if ( metrics . cpuLoad < cpuThresholds . poor ) cpuScore = 1 ;
1015993
1016994 // Assess memory performance
1017995 let memoryScore = 0 ;
1018- if ( memoryUsage < memoryThresholds . excellent ) memoryScore = 4 ;
1019- else if ( memoryUsage < memoryThresholds . good ) memoryScore = 3 ;
1020- else if ( memoryUsage < memoryThresholds . fair ) memoryScore = 2 ;
1021- else if ( memoryUsage < memoryThresholds . poor ) memoryScore = 1 ;
996+ if ( metrics . memoryUsage < memoryThresholds . excellent ) memoryScore = 4 ;
997+ else if ( metrics . memoryUsage < memoryThresholds . good ) memoryScore = 3 ;
998+ else if ( metrics . memoryUsage < memoryThresholds . fair ) memoryScore = 2 ;
999+ else if ( metrics . memoryUsage < memoryThresholds . poor ) memoryScore = 1 ;
10221000
10231001 // Assess ping performance
10241002 let pingScore = 0 ;
1025- if ( ping < pingThresholds . excellent ) pingScore = 4 ;
1026- else if ( ping < pingThresholds . good ) pingScore = 3 ;
1027- else if ( ping < pingThresholds . fair ) pingScore = 2 ;
1028- else if ( ping < pingThresholds . poor ) pingScore = 1 ;
1003+ if ( metrics . ping < pingThresholds . excellent ) pingScore = 4 ;
1004+ else if ( metrics . ping < pingThresholds . good ) pingScore = 3 ;
1005+ else if ( metrics . ping < pingThresholds . fair ) pingScore = 2 ;
1006+ else if ( metrics . ping < pingThresholds . poor ) pingScore = 1 ;
10291007
10301008 // Overall performance rating (average of scores)
10311009 const avgScore = ( cpuScore + memoryScore + pingScore ) / 3 ;
@@ -1035,36 +1013,36 @@ export class LavalinkNode {
10351013 else if ( avgScore >= 1.5 ) performance = "fair" ;
10361014
10371015 // Check if overloaded
1038- const isOverloaded = cpuLoad > cpuThresholds . fair || memoryUsage > memoryThresholds . fair || frameDeficit > 100 ;
1039- const isCritical = cpuLoad > cpuThresholds . poor || memoryUsage > memoryThresholds . poor || frameDeficit > 500 ;
1016+ const isOverloaded = metrics . cpuLoad > cpuThresholds . fair || metrics . memoryUsage > memoryThresholds . fair || metrics . frameDeficit > 100 ;
1017+ const isCritical = metrics . cpuLoad > cpuThresholds . poor || metrics . memoryUsage > memoryThresholds . poor || metrics . frameDeficit > 500 ;
10401018 // Determine status
10411019 const status : HealthStatusKeys = isCritical ? "critical" : isOverloaded ? "degraded" : "healthy" ;
10421020
10431021 // Check if restart is needed
1044- const needsRestart = status === "critical" ||
1045- ( isOverloaded && memoryUsage > 90 ) ||
1046- frameDeficit > 1000 ||
1047- ( this . reconnectionAttemptCount > 0 && this . reconnectionAttemptCount >= this . options . retryAmount / 2 ) ;
1022+ const needsRestart = status === "critical" ||
1023+ ( isOverloaded && metrics . memoryUsage > 90 ) ||
1024+ metrics . frameDeficit > 1000 ||
1025+ ( this . reconnectionAttemptCount > 0 && this . reconnectionAttemptCount >= this . options . retryAmount / 2 ) ;
10481026
10491027 // Generate recommendations
1050- if ( cpuLoad > cpuThresholds . fair ) recommendations . push ( RecommendationsStrings . highCPULoad ( cpuLoad ) ) ;
1051- if ( systemLoad > 0.8 ) recommendations . push ( RecommendationsStrings . highSystemLoad ( systemLoad ) ) ;
1052- if ( memoryUsage > memoryThresholds . fair ) recommendations . push ( RecommendationsStrings . highMemoryUsage ( memoryUsage ) ) ;
1053- if ( frameDeficit > 100 ) recommendations . push ( RecommendationsStrings . frameDeficit ( frameDeficit ) ) ;
1054- if ( ping > pingThresholds . fair ) recommendations . push ( RecommendationsStrings . highLatency ( ping ) ) ;
1028+ if ( metrics . cpuLoad > cpuThresholds . fair ) recommendations . push ( RecommendationsStrings . highCPULoad ( metrics . cpuLoad ) ) ;
1029+ if ( metrics . systemLoad > 0.8 ) recommendations . push ( RecommendationsStrings . highSystemLoad ( metrics . systemLoad ) ) ;
1030+ if ( metrics . memoryUsage > memoryThresholds . fair ) recommendations . push ( RecommendationsStrings . highMemoryUsage ( metrics . memoryUsage ) ) ;
1031+ if ( metrics . frameDeficit > 100 ) recommendations . push ( RecommendationsStrings . frameDeficit ( metrics . frameDeficit ) ) ;
1032+ if ( metrics . ping > pingThresholds . fair ) recommendations . push ( RecommendationsStrings . highLatency ( metrics . ping ) ) ;
10551033 if ( needsRestart ) recommendations . push ( RecommendationsStrings . nodeRestart ) ;
1056- if ( players > 500 ) recommendations . push ( RecommendationsStrings . highPlayercount ( players ) ) ;
1034+ if ( metrics . players > 500 ) recommendations . push ( RecommendationsStrings . highPlayercount ( metrics . players ) ) ;
10571035
10581036 // Calculate penalty score for load balancing (lower is better)
10591037 // Based on Lavalink's penalty system but customized for health
10601038 const nullFrames = this . stats . frameStats ?. nulled || 0 ;
1061- const penaltyScore = players // Player count penalty (each player adds base penalty)
1062- + Math . pow ( cpuLoad * 100 , 2 ) // CPU penalty (exponential - heavily penalize high CPU)
1063- + Math . pow ( memoryUsage , 1.5 ) // Memory penalty (exponential - heavily penalize high memory)
1064- + ping * 2 // Latency penalty
1065- + frameDeficit * 10 // Frame deficit penalty (critical for audio quality)
1039+ let penaltyScore = metrics . players // Player count penalty (each player adds base penalty)
1040+ + Math . pow ( metrics . cpuLoad * 100 , 2 ) // CPU penalty (exponential - heavily penalize high CPU)
1041+ + Math . pow ( metrics . memoryUsage , 1.5 ) // Memory penalty (exponential - heavily penalize high memory)
1042+ + metrics . ping * 2 // Latency penalty
1043+ + metrics . frameDeficit * 10 // Frame deficit penalty (critical for audio quality)
10661044 + nullFrames * 5 ; // Null frame penalty (if available)
1067-
1045+
10681046 // Status penalties
10691047 if ( status === "critical" ) penaltyScore += 10000 ;
10701048 else if ( status === "degraded" ) penaltyScore += 5000 ;
@@ -1076,19 +1054,19 @@ export class LavalinkNode {
10761054
10771055 // Estimate remaining capacity
10781056 let estimatedRemainingCapacity = 0 ;
1079-
1057+
10801058 // Base capacity estimation on current resource usage
10811059 // Assume a healthy node can handle ~100 players at 50% CPU, ~200 at 70% CPU
1082- if ( status !== "critical" && status !== "offline" ) {
1083- const cpuCapacity = players === 0
1060+ if ( status !== "critical" ) {
1061+ const cpuCapacity = metrics . players === 0
10841062 ? 200
1085- : cpuLoad > 0
1086- ? Math . max ( 0 , Math . floor ( ( cpuThresholds . fair - cpuLoad ) / cpuLoad * players ) )
1063+ : metrics . cpuLoad > 0
1064+ ? Math . max ( 0 , Math . floor ( ( cpuThresholds . fair - metrics . cpuLoad ) / metrics . cpuLoad * metrics . players ) )
10871065 : 200 ;
1088- const memoryCapacity = players === 0
1066+ const memoryCapacity = metrics . players === 0
10891067 ? 200
1090- : memoryUsage > 0
1091- ? Math . max ( 0 , Math . floor ( ( memoryThresholds . fair - memoryUsage ) / memoryUsage * players ) )
1068+ : metrics . memoryUsage > 0
1069+ ? Math . max ( 0 , Math . floor ( ( memoryThresholds . fair - metrics . memoryUsage ) / metrics . memoryUsage * metrics . players ) )
10921070 : 200 ;
10931071
10941072 // Use the more conservative estimate, capped at a reasonable maximum
@@ -1364,7 +1342,7 @@ export class LavalinkNode {
13641342 if ( this . options . enablePingOnStatsCheck ) this . heartBeat ( ) ;
13651343
13661344 if ( this . heartBeatInterval ) clearInterval ( this . heartBeatInterval ) ;
1367-
1345+
13681346 if ( this . options . heartBeatInterval > 0 ) {
13691347 // everytime a pong happens, set this.isAlive to true
13701348 this . socket . on ( "pong" , ( ) => {
0 commit comments