- Enclosing class:
Tuner
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final RouterContextThe router being scored.private doubleThe computed health score, NaN before the first computation.private final longRouter uptime when this score was created.(package private) static final doubleShare of the fast or high-capacity tier that ghost exclusions may consume before the factor starts to bite, and the share at which it bottoms out.(package private) static final doubleShare of the tier at which the ghost-exclusion factor bottoms out at 0.0.(package private) static final doubleFloor applied to each factor before the composite is computed.Constructor Summary
ConstructorsMethod Summary
Modifier and TypeMethodDescriptionprivate static doubleclamp(double v) Limits a score to the valid range.(package private) static doublecompositeScore(double[] factors, double[] weights) Weighted geometric mean of the available factors.private voidcompute()Scores every subsystem and combines them into the health score.Compute per-subsystem health scores for the ring chart dashboard.private doublegetEventCount(String statName) Fetch the event count for a stat using the 60s period.private doublegetEventCount(String statName, long period) Fetch the event count for a stat using the given period (ms).private doublegetLifetimeEventCount(String statName) Return lifetime event count for a stat.(package private) doublegetScore()Computed score.private doublegetStatValue(String statName) Read a stat's average value using the 60s period.private doublegetStatValue(String statName, long period) Read a stat's average value using the given period (ms).private booleanTrue if any transport reports firewalled status.private doubleScores the average UDP retransmit timeout.private doubleScores how often peers are banned on contact.private doubleScores the tunnel build handler queue depth.private doubleBuild storms: 0 concurrent = 1.0, linear decay to 0 at 100.private doubleBuild success rate from 60-second event counts.private doubleCongestion: fraction of message send outcomes that triggered a window-reduction (congestion) event.private doubleCrypto: DH queue empty events vs used events.private doubleDropped jobs per interval — any dropped job is a severe overload signal.private doubleGhost exclusions as a fraction of the fast or high-capacity tier — the pool tunnel builds actually draw from.private doubleScores I2CP client session health.private doubleJob queue lag: 0ms = 1.0, >50ms = degraded, >200ms = 0.0 Also considers jobRunSlow (jobs >1s) as sustained overload signal.private doubleHow long jobs wait in the queue before execution.private doubleMessage send latency: how long an outbound message waits in the transport send queue (transport.sendProcessingTime).private doubleScores network database lookup success and speed.private doubleScores peer connectivity and profile quality.private doubleFraction of registered tunnel pools that are alive.private doubleFraction of alive pools meeting their target tunnel count.private doubleReady (queued) jobs as a fraction of pool capacity.private doubleTransport send failure rate: failed sends vs total sends.private doubleScores the UDP send pool saturation.private doubleStreaming health: multi-signal score combining connection success rate, RTT at close, retransmission overhead, and inbound reset rate.private doubleScores transit capacity against the tunnels offered.
Field Details
MIN_FACTOR_SCORE
static final double MIN_FACTOR_SCOREFloor applied to each factor before the composite is computed.The composite is a weighted geometric mean, so its factors multiply: one factor that saturates at 0.0 drives the product — and therefore the health shown on the tuning page — to exactly 0% no matter how healthy the rest of the router is. That 0% is also below
BaseParam.DEGRADED_THRESHOLD, so a score pinned there silently freezes every auto-tuned param at its factory default, which makes the page read "Degraded (0%)" and stops tuning without any hint why. Flooring keeps one saturated factor worth only its own weight share while the all-bad worst case still sits well under the degraded threshold, so the safety behaviour is preserved.- See Also:
_ctx
The router being scored._score
private double _scoreThe computed health score, NaN before the first computation._uptime
private final long _uptimeRouter uptime when this score was created.GHOST_TIER_FREE
static final double GHOST_TIER_FREEShare of the fast or high-capacity tier that ghost exclusions may consume before the factor starts to bite, and the share at which it bottoms out.- See Also:
GHOST_TIER_ZERO
static final double GHOST_TIER_ZEROShare of the tier at which the ghost-exclusion factor bottoms out at 0.0.- See Also:
Constructor Details
SystemHealth
SystemHealth(RouterContext ctx) System health.- Parameters:
ctx- router context supplying uptime and the stats the score reads
Method Details
getScore
double getScore()Computed score.- Returns:
- the health score, where 1.0 is healthy and lower is worse
compositeScore
static double compositeScore(double[] factors, double[] weights) Weighted geometric mean of the available factors.NaN factors mean "insufficient data" and are skipped along with their weights, so the remaining weights are renormalized to 1.0. Every participating factor is floored at
MIN_FACTOR_SCOREfirst — without that floor a single saturated factor zeroes the aggregate and the score stops discriminating between bad and catastrophic.- Parameters:
factors- per-factor scores in [0,1]; NaN for no dataweights- matching per-factor weights, summing to 1.0- Returns:
- the composite score in [0,1], or 1.0 if no factor has data
- See Also:
compute
private void compute()Scores every subsystem and combines them into the health score.scoreJobLag
private double scoreJobLag()Job queue lag: 0ms = 1.0, >50ms = degraded, >200ms = 0.0 Also considers jobRunSlow (jobs >1s) as sustained overload signal.- Returns:
- the job-lag health factor in [0,1], or NaN if the period holds fewer than three job-lag events
scoreReadyJobs
private double scoreReadyJobs()Ready (queued) jobs as a fraction of pool capacity. >50% of pool capacity queued = overload indicator.- Returns:
- the ready-jobs health factor in [0,1], or NaN if the period holds fewer than three samples
scoreDroppedJobs
private double scoreDroppedJobs()Dropped jobs per interval — any dropped job is a severe overload signal.- Returns:
- the dropped-jobs health factor in [0,1], 1.0 when nothing was dropped, or NaN if the stat is unavailable
scoreJobWait
private double scoreJobWait()How long jobs wait in the queue before execution. 0ms→1.0, 500ms→0.5, 2000ms→0.0.- Returns:
- the job-wait health factor in [0,1], or NaN if the period holds fewer than three samples
scoreBuildSuccess
private double scoreBuildSuccess()Build success rate from 60-second event counts. More responsive than SystemVersion's 10-minute window and avoids NaN when that window hasn't accumulated events yet. Requires 10+ total events (exploratory + client) for stability. When firewalled, lower thresholds since inbound builds are rejected.- Returns:
- the build-success health factor in [0,1], or NaN if fewer than 10 build events landed in the period
scorePoolAlive
private double scorePoolAlive()Fraction of registered tunnel pools that are alive. 1.0 = all alive, 0.0 = half or more dead.- Returns:
- the pool-alive health factor in [0,1], or NaN when no tunnel pools are configured
scorePoolDeficit
private double scorePoolDeficit()Fraction of alive pools meeting their target tunnel count. 1.0 = all pools meet target, 0.0 = none do. Linear ramp: 0%→0.0, 70%→1.0. Pools still building at startup won't penalize until sufficient time has passed to meet targets.- Returns:
- the pool-target health factor in [0,1], or NaN when no tunnel pools are configured
isFirewalled
private boolean isFirewalled()True if any transport reports firewalled status.- Returns:
- true if a transport is firewalled
getEventCount
Fetch the event count for a stat using the given period (ms).- Parameters:
statName- the statManager rate name to readperiod- the rate period in ms, e.g.RateConstants.ONE_HOUR- Returns:
- the event count in that period, 0 if the stat or rate has no data
getEventCount
Fetch the event count for a stat using the 60s period.- Parameters:
statName- the statManager rate name to read- Returns:
- the event count in the 60s period, 0 if the stat has no data
getLifetimeEventCount
Return lifetime event count for a stat. Use for low-frequency stats where 60s window is too narrow.- Parameters:
statName- the statManager rate name to read- Returns:
- the number of events recorded since the stat was created, 0 if the stat is unavailable
scoreBuildStorms
private double scoreBuildStorms()Build storms: 0 concurrent = 1.0, linear decay to 0 at 100. Startup bursts (30-50 concurrent) are normal; only penalize sustained high concurrency.- Returns:
- the build-storm health factor in [0,1], or NaN if the period holds fewer than five samples
scoreSendFailure
private double scoreSendFailure()Transport send failure rate: failed sends vs total sends. Uses event counts (each send = 1 event, each failure = 1 event). Requires 500+ sends to skip noisy startup window. <1% = 1.0, >10% = 0.0.- Returns:
- the send-failure health factor in [0,1], or NaN if fewer than 500 sends were recorded in the period
scoreLatency
private double scoreLatency()Message send latency: how long an outbound message waits in the transport send queue (transport.sendProcessingTime). ≤1s → 1.0, 4s → 0.5, ≥10s → 0.0.Anchored on what the router itself treats as normal versus congested:
RouterThrottleImpldocuments ~25ms for NTCP and ~1000ms for SSU as ordinary (SSU includes ack time and can be much more than RTT), and starts refusing participating tunnel requests once the smoothed average passesmaxProcessingTime— 2000ms, 3000ms on slow systems.BuildExecutorlikewise backs builds off above a 2000ms average. The old mapping reached 0.0 at 5000ms, only 2.5× the router's own congestion gate, so a plain SSU router sitting at its documented average scored 0.5 and one with slow acks scored 0.0.- Returns:
- the send-latency health factor in [0,1], or NaN if the period holds fewer than three samples
scoreGhostPeers
private double scoreGhostPeers()Ghost exclusions as a fraction of the fast or high-capacity tier — the pool tunnel builds actually draw from. Up toGHOST_TIER_FREEof the tier excluded is free; the factor then ramps to 0.0 atGHOST_TIER_ZERO.Fraction, not absolute count: marks accrue routinely (a few per minute against multi-minute cooldowns), expire on their own, and clear the moment the peer answers, so any fixed count threshold fires on every healthy router — which is what pinned the composite to 0%. Scaling by the tier keeps a large router free while still catching the case that matters, where exclusions consume enough of the preferred pool to starve selection.
Numerator and denominator must cover the same population: the count is tier-matched, because dividing an all-tier ghost count by a tier size would overstate whenever the tier is smaller than the ghost population.
- Returns:
- the ghost health factor in [0,1], or NaN if the tier is unknown
scoreStreaming
private double scoreStreaming()Streaming health: multi-signal score combining connection success rate, RTT at close, retransmission overhead, and inbound reset rate. Connection success rate dominates for client-heavy routers.- Returns:
- the weighted streaming health factor in [0,1], or NaN when no signal has enough events to score
clamp
private static double clamp(double v) Limits a score to the valid range.- Parameters:
v- the raw score- Returns:
- the score, clamped to 0 to 1
computeSubsystemScores
scoreTransit
private double scoreTransit()Scores transit capacity against the tunnels offered.- Returns:
- the score, 0 to 1
scoreBuildQueue
private double scoreBuildQueue()Scores the tunnel build handler queue depth.- Returns:
- the score, 0 to 1
scoreBanHit
private double scoreBanHit()Scores how often peers are banned on contact.- Returns:
- the score, 0 to 1
scoreSendPool
private double scoreSendPool()Scores the UDP send pool saturation.- Returns:
- the score, 0 to 1
scoreAvgRTO
private double scoreAvgRTO()Scores the average UDP retransmit timeout.- Returns:
- the score, 0 to 1
scoreNetDbHealth
private double scoreNetDbHealth()Scores network database lookup success and speed.- Returns:
- the score, 0 to 1
scoreI2cpHealth
private double scoreI2cpHealth()Scores I2CP client session health.- Returns:
- the score, 0 to 1
scorePeerHealth
private double scorePeerHealth()Scores peer connectivity and profile quality.- Returns:
- the score, 0 to 1
scoreCongestion
private double scoreCongestion()Congestion: fraction of message send outcomes that triggered a window-reduction (congestion) event.The denominator is
udp.sendConfirmTime, which fires once per ACKed message, so this is a true per-message congestion rate rather than a per-packet count.udp.congestionOccurredfires per RTO-timeout or fast-retransmit window reduction.Thresholds are calibrated for UDP/SSU over a lossy anonymizing overlay, where normal AIMD keeps a busy router in the 5-15% band. A wired-TCP-style 5% cap would flag healthy routers as congested. <=5% congestion → 1.0, 25% → 0.0.
Requires at least 100 confirmed sends in the period for a stable ratio.
- Returns:
- the worst of the congestion, retransmit, expiry and inbound-drop health factors, each in [0,1]
scoreCryptoPressure
private double scoreCryptoPressure()Crypto: DH queue empty events vs used events. Frequent empty queue = key generation bottleneck. Only evaluates pools with actual activity; returns NaN when no crypto ops.- Returns:
- the worst crypto-pool health factor in [0,1], or NaN when no pool did any work in the window
getStatValue
Read a stat's average value using the given period (ms). Returns NaN if stat unavailable.- Parameters:
statName- the statManager rate name to readperiod- the rate period in ms, e.g.STAT_PERIOD_LONG- Returns:
- the stat's average value over that period, NaN if unavailable
getStatValue
Read a stat's average value using the 60s period. Returns NaN if stat unavailable.- Parameters:
statName- the statManager rate name to read- Returns:
- the stat's average value over 60s, NaN if unavailable