PeerProfile. Requires periodic reorganize() calls to update peer classifications for optimal tunnel selection. Tier assignment during reorganize(): 1. All profiles are iterated in capacity order (via InverseCapacityComparator) 2. Expired/unreachable/low-bandwidth peers are filtered out 3. Speed, capacity, and integration thresholds are recalculated from the active set 4. Each surviving profile is evaluated via lockedPromoteProfileToTiers() 5. If fast/high-cap tiers fall below minimum thresholds, fallback passes fill gaps (with selectability checks to prevent selecting unusable peers)
A promotion whose RouterInfo has aged past PROP_ROUTERINFO_REFRESH_AGE_MS and which has no proof of life is held back: the peer is left out of the tier until a newer publication arrives on its own. Nothing is fetched on the peer's behalf, unless holding the promotion would starve the tier it would have joined — see shouldDeferPromotionOnStaleRouterInfo(int, int).
Concurrency: ReentrantReadWriteLock protects all tier maps. reorganize() acquires the write lock; selection methods use the read lock. getOrCreateProfileNonblocking(Hash) uses try-lock escalation so a read path never blocks on the write lock.
Nested Class Summary
Modifier and TypeClassDescriptionprivate static final classResult ofbuildCandidateProfiles(long, long, long, long, long): the candidate set plus the totals and counts consumed by the threshold and logging sections.(package private) static enumWhy a peer was evicted from a tier.static enumDefines peer selection slicing modes for tier-based peer organization.Field Summary
Modifier and TypeFieldDescriptionprivate intProfiles currently in use by peers.private final List<Map<Hash, PeerProfile>> Every tier this object retains, in a fixed order, so a pass that must read the whole retained population walks one collection instead of repeating itself per map.private floatCalibrated cohort latency floor across fast peers with data, computed during each reorganize bycomputeAverageLowLatencyRTT()and read throughgetAverageLowLatencyRTT().private final AtomicIntegerBanned-candidate rejections seen since the lastflushSelectionCounters().private doubleFirst-measured fast-tier RTT boundary, captured during the initial reorganize pass.private doubleShort-lived cache forgetTunnelBuildSuccess().private longWhen the build success ratio was last read.private longCachedgetMaxRouterInfoAgeMs(), and the timestamp of the fetch.private longWhen_cachedMaxRouterInfoAgewas fetched.private final InverseCapacityComparatorComparator ordering profiles by inverse capacity.private final RouterContextThe router context.static intFast tier ceiling, in peers.static intHigh-capacity tier ceiling, in peers.static intRuntime-adjustable default max profile count.static intFast tier refill target, in peers, as last configured or autotuned.static intHigh-capacity tier refill target, in peers, as last configured or autotuned.private final ConcurrentHashMap<Hash, Long> Peers demoted via demoteIfUnreachable — excluded from promotion for TUNNEL_DEMOTION_COOLDOWN_MS.private final ConcurrentHashMap<Hash, Integer> Strike count per peer for demoteIfUnreachable — only demote after DEMOTE_STRIKE_THRESHOLD strikes within the decay window.private final ConcurrentHashMap<Hash, Long> Last strike time per peer, so strikes expire after DEMOTE_STRIKE_DECAY_MS.private final Map<Hash, PeerProfile> Profiles of peers fast enough for tunnels.private intCount of fast-tier peers that passed all tests (proven throughput or high-cap bypass).private static final DecimalFormatShared formatter for the statistics display.private final Map<Hash, PeerProfile> Profiles of peers with high capacity.private longTimestamp of the last starved-tier warning; selections hold only the read lock, so this is read and written concurrently.private longWhenpromoteToFillTiers()last ran a full scan, in router time.private final LogLogger for profile reorganization.private final intHow many times the calibrated floor a peer's latency may sit under and still count as low latency, read once fromPROP_LOW_LATENCY_MULTIPLE.private static intMinimum candidate sample size for threshold math.private final Map<Hash, PeerProfile> Profiles of peers not currently failing.Cached keys of the not-failing peers map.private final ProfilePersistenceHelperSaves and restores the peer profiles.private intLast values published byreorganize(boolean), sampled by CoalesceStatsEvent on the short cycle.Skip reasons from the promotion scan in flight, reported bydrainPromotionSkipTally().private intProfiles rated by the last organize run.private final RouterInfoRefresherPer-cycle bounds and counters for the promotion deferral on a stale RouterInfo.private final ReentrantReadWriteLockLock guarding the profile maps during reorganization.Calibrated latency floor per transport style, in ms, published bysampleFirstHopRtts()once a style has enough samples.private Set<PeerProfile> All profiles ordered by inverse capacity.private doubleCapacity value a peer needs for the high-capacity pool.private doubleIntegration value a peer needs for the well-integrated pool.private doubleRound trip time peers are held to, in ms.private doubleSpeed value a peer needs for the fast pool.private booleanWhether either tier was under its target at the last tier-fill scan.private static floatTuned lossy threshold, -1.0 = use router config.private HashHash of this router.private final Map<Hash, PeerProfile> Profiles of peers well integrated into the netdb.static final intCeiling on profiles held in memory, in profiles.private static final longWindow used bygetActiveProfileCount(), in ms.private static final longTimeout for a RouterInfo lookup issued on behalf of a held peer.static final doubleThreshold below which the network is considered under attack.private static final longAbsolute cap: the ceiling never rises above this even on a fully slow pool.private static final longFloor: never exclude peers below this RTT, matching the demoteIfHighRTT bar.private static final doubleAdaptive absolute-RTT ceiling for fast-tier selection.private static final longHow long the cached build success ratio stays fresh.private static final Comparator<PeerProfile> Sorts profiles by capacity, highest first.static final intDefault minimum packet sample size (20 = full 5% bucket resolution).private static final floatDefault packet loss marking a peer moderately lossy.private static final floatDefault loss ratio threshold (20% of packets retransmitted).private static final longDefault freshness window in ms (10 minutes).static final intDefault RouterInfo age past which a RouterInfo is treated as stale and must show proof of life to be selected, in hours.(package private) static final intDefault floor for candidates examined per selection.static final intDefault high-capacity tier refill target, in peers.private static final floatLatency used as a peer's selection weight when it has no measured peer-test average.static final longOne hour, i.e.static final longA strike older than this no longer counts, so a peer that recovered is not demoted by one later failure.static final intUnreachable first-hop failures needed inside the decay window before demoteIfUnreachable demotes.private static final intProfile count considered enough for full selection.(package private) static final booleanWhether acceptance-based eviction applies to both tiers.(package private) static final booleanWhether fast-tier admission requires acceptable tunnel acceptance.(package private) static final booleanWhether high-capacity admission requires acceptable tunnel acceptance.private static final floatFast-track readmission: a fresh loss report below this ratio clears the minimum age even when the tiers are full.private static final longMinimum time after a loss demotion before a peer may be re-admitted, even with fresh clean evidence.private static final floatSelection penalty for moderately-lossy peers: their random priority range is multiplied by this factor, so they are this many times less likely to be picked than an equal-latency clean peer.private static final intHow many times the calibrated floor a peer's latency may sit under and still count as low latency.private static final intBad netdb replies per hour before a peer is penalized.(package private) static final intUpper bound, so a typo cannot turn selection into a full tier scan.private static final intDemoted peers remembered across reorganizations.private static final doubleMaximum lifetime failure ratio (failed / (agreed + failed)) before a peer is excluded.private static final longExclude peers from tunnel selection after this many cumulative failures OR when the lifetime failure ratio exceedsMAX_LIFETIME_FAILURE_RATIO, whichever is stricter.(package private) static final intUpper bound on the soft-purge tier size, in peers.static final intProfile files retained on disk before surplus ones are trimmed.private static final intWhen fast tier has at least this many peers, require all tests passing — peer test (low latency), active, no recent failures, AND proven tunnel throughput.private static final intWhen high-cap tier has at least this many peers, require actual capacity threshold — stop bucket-filling with marginal peers.static final intFloor for the in-memory profile cap, in profiles.private static final doubleMinimum tunnel acceptance ratio (40%) to remain in high-capacity/fast tiers.(package private) static final intLifetime requests a peer needs before its acceptance ratio may exclude it.private static final longPeers with zero tunnel history are allowed into fast/high-cap tiers if they were heard from within this window.private static final longWindow for trusting a stale router info as proof of life.static final StringConfig property for the minimum number of transmitted packets before a loss ratio is reported.static final StringConfig property for the decayed loss score above which a peer is de-prioritized in tier selection (soft signal, below the demotion threshold).static final StringConfig property for the loss ratio above which a peer is demoted from fast/high-cap tiers.static final StringConfig property for how long a reported loss ratio stays fresh.static final StringProperty key for the calibrated low-latency multiple.static final StringConfig property for the maximum number of peer profiles.static final StringProperty key naming the RouterInfo age past which a RouterInfo is treated as stale and must show proof of life to be selected, in hours.static final StringProperty key naming the fast tier's ceiling.static final StringProperty key naming the high-capacity tier's ceiling.static final StringProperty for the floor; seegetMinCandidateSample(RouterContext).static final StringProperty key naming the fast tier's refill target.static final StringProperty key naming the high-capacity tier's refill target.static final StringProperty key naming the RouterInfo age past which a tier promotion is held back, in ms.private static final long[]Rate stat periods for the profile statistics.(package private) static final intNetwork size above which tier thresholds scale with the network instead of staying at their configured default.private static final longFailure count above which peers receive a selection priority penalty but are not excluded.private static final Comparator<PeerProfile> Eviction order: least recently active first, then lowest capacity.private static final longGrace period after startup before peers are penalized.private static final longMinimum delay between "tier returned no candidates" warnings.(package private) static final longShortest gap between two full tier-refill scans.private static final longCooldown period (ms) after demotion before peer can be re-promoted.Constructor Summary
ConstructorsConstructorDescriptionProfileOrganizer(RouterContext context) Creates a profile organizer with empty tier maps and registers performance tracking statistics for peer classification.Method Summary
Modifier and TypeMethodDescription(package private) static booleanaboveRttCeiling(PeerProfile profile, long rttCeilingMs) True if the peer's measured tunnel-test RTT exceeds the adaptive selection ceiling, indicating it should be excluded from this fast-tier pick while still remaining in the tier (soft signal.addProfile(PeerProfile profile) Insert a fully-constructed profile into the organizer.private longapplyBaselineMultiple(long baselineMs) Scales a baseline timeout by the configured multiple.(package private) static longapplyBaselineMultiple(long baselineMs, int multiple) Scale a calibrated baseline into the latency a peer may sit under and still count as low latency.private floataverageLossRatio(Map<Hash, PeerProfile>... tiers) Mean loss ratio of the union of the given tiers, restricted to profiles with a measurement fresh withinPROP_LOSSY_WINDOW.private ProfileOrganizer.CandidateSetbuildCandidateProfiles(long now, long expireActive, long expirePassive, long expireGossip, long expireUntracked) Builds the candidate profile set for this reorganize round, filtering expired, unreachable, and excluded profiles.private doublecalculateCapacityThreshold(double meanCapacity, int numExceedingMean, int totalPeers, double thresholdAtMedian, double thresholdAtMinHighCap, double thresholdAtLowest) Interpolates the capacity threshold for selection.private doublecalculateCapacityThresholdFromSet(Set<PeerProfile> activeProfiles, int totalPeers) Helper to calculate capacity threshold from a pre-filtered set of active profiles.private doublecalculateSpeedThreshold(Set<PeerProfile> reordered, long now, double capacityThreshold) Computes the speed threshold over the high-capacity peers.(package private) static intclampMaxProfiles(int maxProfiles) Clamps the configured max-profiles value into [100, ABSOLUTE_MAX_PROFILES].private voidclearLossIfReadmitted(PeerProfile profile) Clear the loss demotion flag once a peer has been re-admitted, so the cleared state is persisted and re-admission is not re-evaluated on every reorganize.(package private) voidRemove all profiles and clear all tier maps.(package private) longcomputeAdaptiveRttCeiling(double boundaryRttMs, double buildSuccess) Compute the adaptive absolute-RTT ceiling for fast-tier selection, floored at 2× the startup baseline when build success is degraded.private floatCompute average peer test response time across fast peers with data.(package private) static longcomputeFastRttCeiling(double boundaryRttMs) Compute the adaptive absolute-RTT ceiling for fast-tier selection.private intcount(Map<Hash, PeerProfile> m) Counts the profiles in one pool map.private static intcount(Map<ProfileOrganizer.PurgeReason, Integer> reasons, ProfileOrganizer.PurgeReason reason) Count of one eviction reason in this cycle's tally.intCount peers with any send or hear activity in the last 4 hours.intCount peers with activity (send, receive, or failure) within the last hour.private intcountActiveProfiles(long now) Counts profiles with recent activity, over the same set ascountActivePeersInLastHour()so the published gauge and the on-demand scan agree.(package private) static intcountExceedingMean(double[] capacities, double meanCapacity) How many of the given capacities are strictly above the mean.intNumber of peers classified as fast tier.intNumber of peers classified as high-capacity tier.intTotal number of peer profiles in memory (all tiers).(package private) static intcountQualifying(Collection<PeerProfile> profiles, long now, double capacityThreshold) How many of the given profiles qualify for the speed-threshold ranking.private intcountQualityPeers(long now, double buildSuccess) Counts quality peers (fast/high-cap with good acceptance plus recent activity).(package private) static intdefaultTierSize(int known, int base, int divisor) Fallback tier size for a threshold that scales with network size.voiddemoteBatch(Set<Hash> peers) Batch-demote peers from fast/high-cap tiers under a single write lock.voiddemoteFastOnlyBatch(Set<Hash> peers) Batch-remove peers from fast tier only, keeping them in high-cap.voiddemoteIfBanned(Hash peer) Immediately demote a peer from fast/high-cap tiers.voiddemoteIfCongested(Hash peer) Immediately demote a peer from fast/high-cap tiers.voiddemoteIfHighLatency(Hash peer) Immediately demote a peer from fast/high-cap tiers.(package private) voiddemoteIfHighRTT(Hash peer, long responseTimeMs) Demote a peer from fast/high-cap tiers when its direct-link latency is poor.voiddemoteIfLossy(Hash peer) Instantly remove a peer from the fast/high-capacity tiers when its decayed loss score exceeds the threshold, and mark it for loss probation.voiddemoteIfNotLowLatency(Hash peer) Immediately demote a peer from the fast tier when a peer test shows it is no longer low-latency.voiddemoteIfNoTunnel(Hash peer) Immediately demote a peer from fast/high-cap tiers.(package private) voiddemoteIfSlowFirstHop(Hash peer, int firstHopRttMs) Remove a peer from the fast/high-cap tiers if its measured direct-link RTT reaches the low-latency threshold.voiddemoteIfStale(Hash peer) Immediately demote a peer from fast/high-cap tiers if its RouterInfo is stale and it has no recent proof of life (fails isSelectable).voiddemoteIfUnreachable(Hash peer) Immediately demote a peer from fast/high-cap tiers when our transport connection to it failed as a tunnel first hop (unreachable).voiddemoteIfUnreachableNow(Hash peer) Immediately demote a peer from fast/high-cap tiers on a single unreachable first hop, skipping theDEMOTE_STRIKE_THRESHOLDstrikes and the cooldown thatdemoteIfUnreachable(Hash)requires.(package private) static booleandemotesFastTier(boolean inFast, boolean lowLatency) Whether losing the low-latency flag revokes membership of the fast tier.(package private) static StringdescribeTierFill(String where, int fast, int fastTarget, int highCap, int highCapTarget, int examined, int alreadyTiered, int heldNoAddress, String skips) Summarise why the selection tiers could not be filled.private StringFormats the promotion skip tally and clears it.private voidEnforces the global profile cap by evicting the least valuable peers when over capacity.(package private) voidEvict profiles for peers no longer in the network database.(package private) static booleanexcludedOnAcceptance(long agreed, long rejected, int minRequests, boolean highStress) Whether a peer's tunnel acceptance is bad enough to exclude it, given its counts and the sample floor the tier in question applies.booleanexportProfile(Hash profile, OutputStream out) Write a peer profile to the given output stream for persistence.(package private) static intfastActiveCap(int active, int base) Ceiling on the fast tier from the number of reachable peers.(package private) intPeers in either the fast or the high-capacity tier, counting each peer once.private voidfeedBaseline(Map<String, ProtocolRttBaseline> baselines, PeerProfile profile, Collection<Transport> transports) Add this peer's best measured latency to its winning transport's baseline.private intfillFastTierFallbacks(long now, Set<PeerProfile> activeProfiles, double buildSuccess) Fills the fast tier to the minimum via up to three fallback passes.private intfillHighCapFallback(long now, Set<PeerProfile> activeProfiles, double buildSuccess) Fills the high-capacity tier to the minimum with recently-active selectable peers.fingerprints(RouterContext ctx, Hash peer, int mask) One peer's fingerprints: the masked IP the comm system recorded, the masked IP and port of every published address, and the family option.(package private) static booleanfirstHopRttIsSlow(int firstHopRttMs, int thresholdMs) Whether a measured first-hop RTT warrants removal from the fast tiers.private voidRecord the banned-candidate rejections accumulated since the last flush as a singletunnel.peerBannedAtSelectionsample.freshLossRatios(Map<Hash, PeerProfile>... tiers) Fresh loss ratios (positive and withinPROP_LOSSY_WINDOW) of the union of the given tiers, sorted ascending; empty if none.intProfiles with recent activity, as of the last completed reorganisation.floatAverage loss ratio across the fast and high-capacity tiers with a fresh measurement, as a fraction (0.0 - 1.0).floatCalibrated latency floor across fast peers with data, computed during eachreorganize()as the mean of the fastest samples rather than of the whole population.(package private) longThe calibrated latency floor for a transport style, or -1 when that transport has not gathered enough samples to have an opinion.(package private) longThe calibrated latency floor for a peer, choosing the style of the transport that measured it.(package private) longThe qualifying latency threshold for a peer, from the calibrated baseline of the transport that measured it.doubleCurrent minimum capacity value for the high-capacity peer tier.(package private) longThe qualifying latency threshold for the fast cohort, from the cohort baseline of its fastest members.static intCeiling on the fast tier.static intCeiling on the high-capacity tier.private static intReturns the default profile count from the config.static intProfile count the reorganizer targets by default.static intFast tier size the router refills toward.intThe candidate-sample floor currently in effect, for logging.private static intDerives the enough-profiles count from the config.(package private) static doublegetExcessiveFailurePenalty(Hash peer, PeerProfile prof) Selection penalty multiplier for peers between the soft and hard failure thresholds.floatAverage loss ratio across the fast tier with a fresh measurement, as a fraction (0.0 - 1.0); 0.0 if none.intLive size of the fast or high-capacity union, a peer in both tiers counted once.intCurrent number of peers in the fast tier.intThe number of quality peers currently in the fast tier.doubleAverage tunnel test time (RTT in ms) of the peer at the fast-tier speed boundary from the last reorganize, or 0.0 if never reorganized.floatAverage loss ratio across the high-capacity tier with a fresh measurement, as a fraction (0.0 - 1.0); 0.0 if none.intCurrent high-capacity tier population.doubleGet the high-capacity RTT ceiling for display and diagnostics.doubleCurrent minimum integration value for the well-integrated peer tier.static floatThe lossy demotion threshold in effect, tuned value if set else config.protected intMaximum number of peers to keep in the fast tier, capped by active peers.protected intgetMaximumFastPeers(int known, int active) Variant taking both expensive inputs, for callers that already have them.protected intMaximum number of peers to keep in the high-capacity tier, capped by active peers.protected intgetMaximumHighCapPeers(int known, int active) Variant taking both expensive inputs, for callers that already have them.private longMaximum RouterInfo age in ms, cached alongsidegetTunnelBuildSuccess()on the sameBUILD_SUCCESS_CACHE_MScadence: it is a constant read per candidate, and 15s of staleness on a proof-of-life bar is immaterial.floatMedian loss ratio of the union of the fast and high-capacity tiers with a fresh measurement, as a fraction (0.0 - 1.0).static intRead the candidate-sample floor, honouring the configured override.static intHigh-capacity tier size the router refills toward.protected intMinimum number of peers to keep in the fast tier.protected intgetMinimumFastPeers(int known) Variant taking the network size, for callers that already know it.protected intMinimum number of peers to keep in the high-capacity tier.protected intgetMinimumHighCapacityPeers(int known) Variant taking the network size, for callers that already know it.(package private) intPeers not recorded because the recovery list was full.(package private) intPeers still waiting for their RouterInfo to be requested.static floatThe moderate de-prioritization threshold in effect, read straight from config (there is no Tuner override, unlikegetLossyThreshold(RouterContext)).Retrieve an existing profile or create a new one, without blocking on contention.getProfile(Hash peer) Retrieve the profile for a peer, or null if unknown.intProfiles held in RAM, as of the last completed reorganisation.getProfileNonblocking(Hash peer) Retrieve the profile for a peer without blocking.intPeers passing the quality gates, as of the last completed reorganisation.private voidTakes the reorganization read lock.private longRouterInfo age past which a promotion is held back, in ms.doubleCurrent minimum speed value for the fast peer tier.intLast known number of profiles stored on disk, from the most recent load, cleanup, or purge in the persistence helper.private intgetSubTier(Hash peer, long k0, long k1) Sub-tier slice index for a peer, keyed off the session key so the same key always yields the same slice.doubleTunnel build success ratio across the whole router, cached forBUILD_SUCCESS_CACHE_MSbecause a single lookup costs several rate reads.getUs()Self-exclusion hash for this router instance.private booleanTakes the reorganization write lock, waiting if needed.private booleanhasHighLoss(PeerProfile profile, long now) True if the peer's decayed loss score is at or abovePROP_LOSSY_THRESHOLD.private booleanhasNoUsableTransport(Hash peer) Does this peer's RouterInfo carry no address we could still send to?(package private) static booleanhasRecentProofOfLife(PeerProfile profile, long now) True if the peer has shown proof of life withinPROOF_OF_LIFE_WINDOW_MS: a successful send, heard-from, or heard-about within the last hour.(package private) static booleanhasRecentTierActivity(PeerProfile profile, long activeCutoff) True if the peer showed recent activity within the given cutoff: a heard-from or successful send at or after the cutoff.private booleanhasRecentTunnelFailures(PeerProfile profile) Check if a peer has had recent tunnel failures (failed tests or builds).private booleanReturns true when the RouterInfo has a reachable NTCP/NTCP2/SSU/SSU2 address.private booleanhasValidRouterInfo(Hash peer, RouterInfo info, double buildSuccess, PeerProfile prof) Checks the RouterInfo for hidden flag, stale proof of life, usable transport address, bandwidth tier, and tunnel exclusion.(package private) static inthighCapActiveCap(int active, int base) Ceiling on the high-capacity tier from the number of reachable peers.private booleanholdPromotionForRefresh(Hash peer, boolean staleRouterInfo, int tierSize, int safeFloor) Apply the hold to one tier insert: leave the peer out of the tier, or let it in.private booleaninLossProbation(PeerProfile profile, long now) True if the peer is in loss probation: it was demoted from the fast/ high-cap tiers for packet loss and has not yet produced fresh clean evidence of recovery (seeshouldReadmitLossy(PeerProfile, long)).(package private) static booleanisActiveInWindow(PeerProfile profile, long hideBefore) Whether a profile shows activity within the active window: a peer test counted as active, a successful or failed send, or any contact.private booleanisCongestedPeer(Hash peer) Check if a peer has a congestion capability cap (D or E).private booleanisEligibleForTierFill(PeerProfile profile, double buildSuccess, long now, long activeCutoff) Full eligibility gate for filling the fast/high-cap tiers from the active profile set: not ourselves, passes the tier gates, and active within the cutoff window.private booleanisEligibleForTierFill(PeerProfile profile, double buildSuccess, long now, long activeCutoff, boolean requireAcceptance) Whether a peer may be admitted to a tier by a fill pass.(package private) static booleanisEvictable(PeerProfile profile, Map<Hash, PeerProfile> fastPeers, Map<Hash, PeerProfile> highCapPeers, long activeThreshold, int fastPeerLimit, int highCapacityLimit) Whether a profile is eligible for eviction under the profile cap: not protected by a fast/high-cap tier slot that still has room below its limit, and no send or heard-from activity within the active window.(package private) static booleanWhether the given profile carries excessive cumulative tunnel failures.(package private) static booleanisExcludedBuildTier(String rawTier) Whether a RouterInfo's advertised bandwidth tier is one that cannot host a tunnel: L, M, or N.(package private) static booleanisExcludedFromProfiling(String capabilities, String bandwidthTier) Should a peer advertising these capabilities and bandwidth tier be profiled?(package private) booleanisExcludedFromProfiling(Hash peer) Check if a peer should be excluded from profiling.(package private) static booleanisExpiredProfile(PeerProfile profile, long now, long expireActive, long expirePassive, long expireGossip, long expireUntracked) Whether a profile has expired from its activity tier and should be purged: no activity (send/heard/heard-about) within the tier's expiration window.booleanCheck whether a peer is classified in the fast tier.private booleanisFastTierCapable(Hash peer) Whether the peer qualifies for the fast tier based on advertised bandwidth tier and capabilities.private booleanCheck if current router is firewalled to adjust peer selection thresholds.private booleanisHighBandwidthCapable(Hash peer) Whether the peer qualifies for the high-capacity tier based on advertised bandwidth tier and capabilities.(package private) static booleanWhether a bandwidth tier is one of the high-capacity tiers (O, P, X).booleanisHighCapacity(Hash peer) Check whether a peer is classified in the high-capacity tier.private booleanisLowAcceptanceLowSample(PeerProfile profile, double ratio, boolean highStress) Small-sample decision: belowMIN_TUNNEL_REQUESTSlifetime requests, apply the loose startup thresholds.private booleanisLowAcceptanceMature(PeerProfile profile, TunnelHistory th, double ratio, long agreed, long rejected, boolean highStress) Mature decision: effective minimum ratio, with a cooldown for recent bandwidth rejections.(package private) static booleanisLowBandwidthTierName(String tier) Whether a bandwidth tier name is one of the tiers excluded from profiling.booleanWhether recent tunnel build success is below the attack threshold.private booleanisLowLatency(Hash peer) Check whether the peer's profile has low-latency status.private booleanisLowTunnelAcceptance(PeerProfile profile, double buildSuccess, long now, int minRequestsForJudgement) Check if peer has low tunnel acceptance ratio, decided from its lifetime accept/reject counts viaexcludedOnAcceptance(long, long, int, boolean), then refined by the low-sample or mature path.(package private) static booleanisModeratelyLossy(float score, float moderateThreshold, float demoteThreshold) Whether a decayed loss score falls in the moderate band: at or above the moderate threshold but below the demotion threshold.private static booleanisQualityFastPeer(PeerProfile profile, Map<Hash, PeerProfile> highCap) True when a peer in the fast tier has proven throughput or is in the high-cap tier.private booleanisQualityPeer(PeerProfile profile, long recentCutoff, double buildSuccess) Check if peer is a quality tunnel candidate: good acceptance ratio, selectable, and recently active.(package private) static booleanisReliableBandwidthPeer(PeerProfile profile, long now, boolean established) Reliability gate for high-bandwidth (O/P/X tier) selection: the peer must show a tunnel acceptance ratio of at least 0.3 AND evidence of recent activity.private booleanisRestorableTierPeer(Hash peer, PeerProfile profile, Map<Hash, PeerProfile> tier, double buildSuccess, long now, boolean highCap) Whether a preserved pre-reorganize tier entry may be re-added: not already present in the tier, selectable, no recent tunnel failures, not in loss probation (fast tier) or below the loss demotion threshold (high-cap tier), and acceptable tunnel acceptance.booleanisSelectable(Hash peer) Whether the given peer is eligible for selection.private booleanisSelectable(Hash peer, double buildSuccess) Whether the given peer is eligible for selection.(package private) static booleanisStaleAbsentPeer(PeerProfile profile, long now, long absentThreshold) Whether the peer's last activity (send, heard-from, or heard-about) is older than the absent threshold.booleanisWellIntegrated(Hash peer) Check whether a peer is classified in the well-integrated tier.private booleanisX(Map<Hash, PeerProfile> m, Hash peer) Checks whether a peer is in one pool map.(package private) static booleanlatencySampleIsBroadEnough(int measured, int population) Fraction of a tier population that must carry a usable first-hop RTT before latency is allowed to influence tier membership.private PeerProfilelockedGetProfile(Hash peer) Returns a peer's profile with the read lock held.private voidlockedPickLowestPriority(List<Map.Entry<Hash, PeerProfile>> candidates, int howMany, Set<Hash> matches) Selects the lowest-priority candidates, penalizing moderately-lossy peers.private voidlockedPlaceProfile(PeerProfile profile, double buildSuccess) Puts a profile into the pool maps with the write lock held.private voidlockedPlaceProfile(PeerProfile profile, double buildSuccess, int minHighCap, int maxFast, int maxHighCap) Variant taking the tier thresholds, which the caller has already fetched for the whole scan.private voidlockedPromoteProfileToTiers(PeerProfile profile, double buildSuccess) Evaluate a profile for promotion to fast/high-cap tiers without touching the notFailing structures.private booleanlockedPromoteProfileToTiers(PeerProfile profile, double buildSuccess, int minHighCap, int maxFast, int maxHighCap) Evaluate a profile for promotion to the tiers, reporting whether the only thing that stopped it was an unusable transport address.private voidlockedRebuildTiers(Set<PeerProfile> candidates, double buildSuccess) Reinserts all active profiles into the tier maps.private voidlockedSelectActive(List<Hash> connected, int howMany, Set<Hash> toExclude, Set<Hash> matches, int mask, MaskedIPSet ipSet, double buildSuccess) Scan up tomaxCandidateSample(int, int)established peers for the active tier, inRandomIteratororder so the peers examined differ from call to call.private voidlockedSelectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches, int mask, MaskedIPSet ipSet, double buildSuccess, long rttCeiling) Selects peers while holding the lock.private voidlockedSelectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches, SessionKey randomKey, ProfileOrganizer.Slice subTierMode, int mask, MaskedIPSet ipSet, double buildSuccess, long rttCeiling) Collects up tomaxCandidateSample(int, int)candidates from a tier and hands them tolockedPickLowestPriority(List, int, Set).private RouterInfoFetch a peer's local RouterInfo without re-running the network database'svalidate().(package private) static RouterInfolookupRouterInfoUnvalidated(RouterContext ctx, Hash peer) lookupRouterInfoUnvalidated(Hash)with the context passed in, so the fingerprint builder below is reachable from tests.(package private) static booleanlossyReadmitAllowed(float lossRatio, long ratioLastUpdate, long lossySince, long now, long freshWindowMs, long minAgeMs, float threshold, float fastTrackRatio, boolean tiersShort) Pure decision behindshouldReadmitLossy(PeerProfile, long), pinned by tests.static voidCommand-line entry point; reads profile dump files and prints thresholds.(package private) static intmaxCandidateSample(int howMany, int peerCount) Cap on how many tier peers to gate-check when selectinghowManytunnels.(package private) static intmaxCandidateSample(int howMany, int peerCount, int floor) Candidate-sample cap for a configurable floor.(package private) booleanmayDrainMissingRouterInfo(long now) Whether enough time has passed to request the next batch of missing RouterInfos.private floatmedianLossRatio(Map<Hash, PeerProfile>... tiers) Median loss ratio of the union of the given tiers, restricted to profiles with a measurement fresh withinPROP_LOSSY_WINDOW.(package private) static booleanneedsRouterInfoRefresh(RouterInfo info, PeerProfile profile, long now, long refreshAgeMs) Whether a RouterInfo is old enough that promoting on it risks a build failure: published before the threshold and no proof of life.(package private) static booleanneedsTierRestore(int currentSize, int oldSize) Whether a shrunken tier needs restoration: below half its old size and the old size was substantial.(package private) static doubleneutraliseUnknownBuildSuccess(double ratio) Maps an unknown build-success reading onto a neutral 1.0.(package private) static intnextStrikeCount(int previousStrikes, long lastStrikeTime, long now) Compute the next strike count after a failure, applying time-window decay.voidnoteFirstHopRtt(Hash peer, int rtt, long when) Record a measured direct-link RTT against a peer, by hash.(package private) booleannoteMissingRouterInfo(Hash peer) Record that a stored profile was loaded for a peer the netdb cannot resolve.private booleannotRestricted(Hash peer, MaskedIPSet ipSet, int mask) Subnet-diversity gate for the running router.(package private) static booleannotRestricted(RouterContext ctx, Hash peer, MaskedIPSet ipSet, int mask) Subnet-diversity gate: does this peer pass the masked-subnet, port and family check, and if it does, claim its fingerprints so that no later peer sharing any one of them passes as well.private static final Stringnum(double num) Formats one number for the statistics display.private booleanpassesBasicGates(Hash peer) Returns false when the peer is us, banlisted, or recently failed as first hop.(package private) booleanpassesTierGates(PeerProfile profile, double buildSuccess, long now) Common gate for the fast-tier fill passes, which always require acceptable tunnel acceptance.(package private) booleanpassesTierGates(PeerProfile profile, double buildSuccess, long now, boolean requireAcceptance) Common gate for fast/high-cap tier fill passes: the peer must be selectable, have acceptable tunnel acceptance, no recent tunnel failures, and not be in loss probation.booleanpeerSendsBadReplies(Hash peer) Check whether a peer sends an excessive rate of invalid or failed netDb lookup replies (potential misbehavior indicator).static intpickLowestPriority(List<Hash> candidates, float[] priority, int howMany, Set<Hash> matches) Choose up tohowManycandidates in ascending priority order.(package private) static PeerProfileprofileToPromote(PeerProfile live, PeerProfile fallback) Which profile a promotion pass should act on: the live one when the map has it, the caller's otherwise.private voidAfter a demotion vacates slots in fast/high-cap tiers, scan the capacity-ordered peer list and promote the next best candidates to fill the openings.(package private) static StringpromotionEvidence(int bonus, int raw, boolean promotedIsLive) Evidence line for a promotion log: only the values that explain something.private booleanpromotionNeedsRouterInfoRefresh(PeerProfile profile, long now) Whether this peer's stored RouterInfo is stale enough that promoting on it risks a build failure.(package private) static StringpromotionSkipLabel(String reason) Display form of a promotion skip reason: prose words with the first letter capitalised, so the tally reads as a list of reasons rather than as code identifiers.(package private) StringpromotionSkipReason(PeerProfile profile, Hash peer, double buildSuccess, long now) WhyskipsPromotion(PeerProfile, Hash, double)would skip this profile, or null when it would not.(package private) static StringpromotionTransition(boolean wasFast, boolean wasHighCap, boolean nowFast, boolean nowHighCap) Describe a tier promotion as afrom -> totransition, naming only the tiers actually gained.private voidpublishBaselines(Map<String, ProtocolRttBaseline> baselines) Publish this cycle's baselines and report them as stats.private voidReport how much of each tier holds a current first-hop RTT.private voidpublishPurgeReasonStats(Map<ProfileOrganizer.PurgeReason, Integer> reasons, boolean fastSoft, boolean highCapSoft, int threshold) Publish per-reason eviction counts for this reorganize cycle.(package private) static ProfileOrganizer.PurgeReasonpurgeReasonFor(boolean banned, boolean unreachable, boolean tierCapable, boolean softEvictionAllowed, boolean hasRecentFailures, boolean inLossProbation, boolean lowAcceptance) Why a peer listed in a tier should be evicted, or null to keep it.voidRemove profile files that are no longer worth keeping: those whose peer is genuinely stale at any size, and those beyond the disk retention floor.private voidpurgeUnusableFromMap(Map<Hash, PeerProfile> tier, String tierName, long now, double buildSuccess, boolean allowSoftEviction, Map<ProfileOrganizer.PurgeReason, Integer> reasons) Evicts every peer in one tier map thatpurgeReasonFor(boolean, boolean, boolean, boolean, boolean, boolean, boolean)rejects.private voidpurgeUnusableFromTiers(long now, double buildSuccess, int profileCount) Evicts peers that stopped qualifying since the tiers were last rebuilt.private voidputFastPeer(Hash peer, PeerProfile profile) Add a peer to the fast tier, maintaining the quality counter.private static booleanqualifiesForSpeedRank(PeerProfile profile, long now, double capacityThreshold) Whether a profile qualifies for the speed-threshold ranking: enough capacity to count and recently active.(package private) static booleanqualifiesHighBandwidthTier(String tier, String caps) Whether a bandwidth tier and capability string together qualify the peer for the high-capacity / fast tiers: X/P/O tier and none of the D (moderate congestion), E (severe congestion), G (no tunnels) or U (unreachable) capability flags.private voidrecordFirstHopRtt(PeerProfile profile, int rtt, long when) The single recorder for a measured direct-link RTT.private voidrecordPromotionSkip(String reason) Counts one promotion skip against its reason.private voidPublish the promotion-deferral counters for the cycle just finished.private voidIssue RouterInfo lookups for peers held out of the tiers for want of a usable address.private voidReleases the reorganization read lock.private voidReleases the reorganization write lock.private voidRelease the reorganize write lock, then refresh peers held out of the tiers.(package private) static intremainingToSelect(int howMany, Set<Hash> matches) How many slots are still free in a cascade's destination set.private PeerProfileremoveFastPeer(Hash peer) Remove a peer from the fast tier, maintaining the quality counter.(package private) voidRun a standard reorganize round without coalescing.(package private) voidreorganize(boolean shouldCoalesce) Reorganizes peer profiles into performance-based tiers (fast, high-capacity, etc.) and expires stale entries.private voidrestorePreservedPeers(long now, Set<Hash> oldFastPeers, Set<Hash> oldHighCapPeers, double buildSuccess) Re-adds preserved pre-reorganize tier entries when the rebuild shrank the tiers too much.private intsampleFirstHopRtt(PeerProfile profile, Collection<Transport> transports, long now) Record the best RTT any transport reports for this peer, if any.(package private) static intsampleFirstHopRttLimit(int total) How many tier peers to sample for first-hop RTT in one reorganize.(package private) intRefresh direct-link RTT for tier peers that already have a transport session, and re-judge their low-latency standing.private List<PeerProfile> Bounded random sample of the retained peers for RTT measurement, capped atDEFAULT_MIN_CANDIDATE_SAMPLEwith a rotated start so successive cycles do not always read the same prefix.voidselectActiveNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches) Select from currently connected (established) not-failing peers.voidselectActiveNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask, MaskedIPSet ipSet) Select from currently connected peers with IP-subnet diversity.voidselectAllNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing) Select from all not-failing peers randomly, falling through to general peers if the not-failing set is insufficient.voidselectAllNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet) Select from all not-failing peers randomly, honouring a /n subnet diversity restriction.private voidselectAllNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet, double buildSuccess, boolean preferUnproven) Random iterator over not-failing list with optional tier exclusion.Gather the union of all peers from fast, high-capacity, and not-failing tiers.Return the current fast tier membership directly.voidselectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches) Select up to howMany fast-tier peers, filling shortfalls from high-capacity.voidselectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask, MaskedIPSet ipSet) Select fast-tier peers with IP-subnet diversity constraints.voidselectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, SessionKey randomKey, ProfileOrganizer.Slice subTierMode, int mask, MaskedIPSet ipSet) Select fast-tier peers deterministically sliced by a random key.voidselectHighBandwidthPeers(int howMany, Set<Hash> exclude, Set<Hash> matches) Select up to howMany peers from O/P/X bandwidth tiers (high shared bandwidth) that are not failing.voidselectHighBandwidthPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet) Select high-bandwidth peers with tier-exclusion and IP-subnet diversity.private voidselectHighBandwidthPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet, double buildSuccess) Iterates not-failing peers randomly, filtering for O/P/X bandwidth tiers with reliability checks (acceptance ratio, recent activity, peer tests).Return the current high-capacity tier membership directly.voidselectHighCapacityPeers(int howMany, Set<Hash> exclude, Set<Hash> matches) Select high-capacity peers, filling shortfalls from not-failing peers.voidselectHighCapacityPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask, MaskedIPSet ipSet) Select high-capacity peers with IP-subnet diversity.voidselectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches) Select from not-failing peers, excluding high-capacity tier if requested.voidselectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing) Select from not-failing peers, optionally excluding the high-capacity tier.voidselectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet) Full-parameter select from not-failing peers.voidselectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet, boolean preferUnproven) Full-parameter select from not-failing peers.voidselectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask, MaskedIPSet ipSet) Select from not-failing peers with IP-subnet diversity.(package private) static PeerProfileselectNthFastest(Collection<PeerProfile> profiles, long now, double capacityThreshold, int k) Select thek-th fastest qualifying profile (1-based) without sorting the whole candidate set.private voidselectRemainderFromAllPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, double buildSuccess) Fallback selection: fill the remaining slots from the union of all peers (fast + high-capacity + not-failing).(package private) static PeerProfileselectSpeedBoundary(Collection<PeerProfile> profiles, long now, double capacityThreshold) The profile whose speed becomes the speed threshold: the one at rankspeedThresholdCutoff(int)of the qualifying set, ranked fastest first.booleanselectTestPeersNonBlocking(int howMany, Set<Hash> exclude, Set<Hash> matches) Non-blocking test peer selection: tries the full fast → high-cap → active chain under a singletryReadLock().Return the current well-integrated tier membership directly.static voidsetDefaultMaxFastPeers(int val) Override the fast tier ceiling, clamped to 200-3000 peers.static voidsetDefaultMaxHighCapPeers(int val) Override the high-capacity tier ceiling, clamped to 200-6000 peers.static voidsetDefaultMaxProfiles(int val) Override the default profile count, clamped toMIN_MAX_PROFILES-ABSOLUTE_MAX_PROFILES.static voidsetDefaultMinFastPeers(int val) Override the fast tier refill target, clamped to 50-2000 peers.static voidsetLossyThreshold(float ratio) Set the lossy demotion threshold (called by Tuner).static voidsetMinCandidateSample(int val) Override the candidate-sample floor at runtime.static voidsetMinHighCapacityPeers(int val) Override the high-capacity tier refill target, clamped to 50-3000 peers.voidStore the local router hash for self-exclusion from peer selection.(package private) static booleanshouldDeferPromotionOnStaleRouterInfo(int tierSize, int safeFloor) Whether a promotion may be held back on a stale RouterInfo without risking the tier it would join.private booleanshouldReadmitLossy(PeerProfile profile, long now) True if a demoted peer may be re-admitted to the tiers: at leastLOSS_READMIT_MIN_AGEhas passed since the demotion AND the transport has reported a loss ratio below the demotion threshold withinPROP_LOSSY_WINDOW.(package private) booleanskipsPromotion(PeerProfile profile, Hash peer, double buildSuccess) Whether the peer should be skipped for tier promotion: not selectable, in a strict country, low tunnel acceptance, high-latency penalty, congested, or in loss probation.(package private) static intsoftPurgeThreshold(int knownProfiles) The smallest tier size at which peers may be evicted for a soft reason.(package private) static intspeedThresholdCutoff(int qualifyingCount) Rank boundary for the speed threshold: the top 30% of qualifying peers, capped at 50.(package private) static booleanstillSelecting(int howMany, Set<Hash> matches) Whether a cascade still has slots left to fill.takeMissingRouterInfoBatch(long now) Hand over the next batch of peers whose RouterInfo should be requested.(package private) static booleantierScanStarved(int candidates, int howMany, int examined, int skipExcluded, int skipGated, int skipSliced, int skipIp, int skipRtt) Whether a tier scan starved badly enough to be worth reporting.(package private) static booleantryClaimKeys(Set<String> claimed, Set<String> candidateKeys) The diversity gate itself: report whether any of this peer's fingerprints is already claimed, and claim them all either way.private booleanTakes the read lock if it is free.private booleanTakes the write lock if it is free.private voidwarnTierStarved(Map<Hash, PeerProfile> peers, int examined, int skipExcluded, int skipGated, int skipSliced, int skipIp, int skipRtt) Rate-limited warning naming the gate that emptied a tier scan.voidwriteProfile(PeerProfile profile) Persist the given profile to disk.
Field Details
ATTACK_THRESHOLD
public static final double ATTACK_THRESHOLDThreshold below which the network is considered under attack.- See Also:
CAPACITY_COMPARATOR
Sorts profiles by capacity, highest first.STALENESS_THEN_CAPACITY
Eviction order: least recently active first, then lowest capacity.Activity is the newest of last-sent-successfully, last-heard-from and last-heard-about, matching
isStaleAbsentPeer(PeerProfile, long, long). Ties fall through toCAPACITY_COMPARATORso two equally idle peers are separated on what else is known about them rather than on map order._log
Logger for profile reorganization._context
The router context._fastPeers
Profiles of peers fast enough for tunnels._fastQualityCount
private int _fastQualityCountCount of fast-tier peers that passed all tests (proven throughput or high-cap bypass). Maintained exclusively viaputFastPeer(Hash, PeerProfile)andremoveFastPeer(Hash)._profileCount
private volatile int _profileCountLast values published byreorganize(boolean), sampled by CoalesceStatsEvent on the short cycle. Plain reads are enough: these are gauges that hold their last level, and a torn pair would at worst report a count from the previous reorganisation._qualityCount
_activeProfileCount
ACTIVE_WINDOW_MS
_refresher
Per-cycle bounds and counters for the promotion deferral on a stale RouterInfo. A cycle is opened bylockedRebuildTiers(Set, double)and its counters are published byrecordRouterInfoRefreshStats()before the reorg write lock is released._highCapacityPeers
Profiles of peers with high capacity._promotionSkipTally
Skip reasons from the promotion scan in flight, reported bydrainPromotionSkipTally().Guarded by the write lock every caller already holds; see
recordPromotionSkip(String)._lastTierFillScan
private long _lastTierFillScanWhenpromoteToFillTiers()last ran a full scan, in router time._tiersShort
private volatile boolean _tiersShortWhether either tier was under its target at the last tier-fill scan. Widens the loss-readmit fast-track while short (seelossyReadmitAllowed(float, long, long, long, long, long, float, float, boolean)); recomputed on every scan, so it lags a demotion by at most the fill debounce.TIER_FILL_DEBOUNCE_MS
static final long TIER_FILL_DEBOUNCE_MSShortest gap between two full tier-refill scans.Well under the reorganise interval, so the debounce can never delay a refill past the next cycle that would have done it anyway.
- See Also:
_wellIntegratedPeers
Profiles of peers well integrated into the netdb._notFailingPeers
Profiles of peers not currently failing._allTiers
Every tier this object retains, in a fixed order, so a pass that must read the whole retained population walks one collection instead of repeating itself per map.Assigned in the constructor once all four maps exist. The maps are themselves final and are never replaced, so this never needs rebuilding.
_notFailingPeersList
_demotedPeers
Peers demoted via demoteIfUnreachable — excluded from promotion for TUNNEL_DEMOTION_COOLDOWN_MS._demoteStrikes
Strike count per peer for demoteIfUnreachable — only demote after DEMOTE_STRIKE_THRESHOLD strikes within the decay window._demoteStrikeTimes
Last strike time per peer, so strikes expire after DEMOTE_STRIKE_DECAY_MS.DEMOTE_STRIKE_THRESHOLD
public static final int DEMOTE_STRIKE_THRESHOLDUnreachable first-hop failures needed inside the decay window before demoteIfUnreachable demotes.- See Also:
DEMOTE_STRIKE_DECAY_MS
public static final long DEMOTE_STRIKE_DECAY_MSA strike older than this no longer counts, so a peer that recovered is not demoted by one later failure.- See Also:
MAX_DEMOTED_PEERS
private static final int MAX_DEMOTED_PEERSDemoted peers remembered across reorganizations.- See Also:
_us
Hash of this router._persistenceHelper
Saves and restores the peer profiles._strictCapacityOrder
All profiles ordered by inverse capacity._thresholdSpeedValue
private double _thresholdSpeedValueSpeed value a peer needs for the fast pool._thresholdCapacityValue
private double _thresholdCapacityValueCapacity value a peer needs for the high-capacity pool._thresholdIntegrationValue
private double _thresholdIntegrationValueIntegration value a peer needs for the well-integrated pool._thresholdRTT
private double _thresholdRTTRound trip time peers are held to, in ms._baselineRTT
private volatile double _baselineRTTFirst-measured fast-tier RTT boundary, captured during the initial reorganize pass. Used bycomputeAdaptiveRttCeiling(double, double)to floor the adaptive ceiling at 2× the startup baseline when build success is degraded, preventing RTT ceiling amplification during cascade failure._averageLowLatencyRTT
private volatile float _averageLowLatencyRTTCalibrated cohort latency floor across fast peers with data, computed during each reorganize bycomputeAverageLowLatencyRTT()and read throughgetAverageLowLatencyRTT().Previously documented as tightening the low-latency threshold to 1.5x the cohort average, but no caller ever consulted it: the value was recomputed every cycle and discarded. It is now calibrated as the mean of the fastest samples and applied as a second bound alongside the per-transport floor.
_comp
Comparator ordering profiles by inverse capacity._rttBaselines
Calibrated latency floor per transport style, in ms, published bysampleFirstHopRtts()once a style has enough samples. Absent means that style has no calibrated opinion yet and the absolute rule applies unchanged. Immutable after publication, so reads are lock-free.LOW_LATENCY_BASELINE_MULTIPLE_DEFAULT
private static final int LOW_LATENCY_BASELINE_MULTIPLE_DEFAULTHow many times the calibrated floor a peer's latency may sit under and still count as low latency. Three is deliberately permissive: roughly half of all peers fall within two times a top-50 floor, so this admits the fast end of each transport's population and rejects the tail.- See Also:
PROP_LOW_LATENCY_MULTIPLE
Property key for the calibrated low-latency multiple. @since 0.9.71+.- See Also:
_lowLatencyBaselineMultiple
private final int _lowLatencyBaselineMultipleHow many times the calibrated floor a peer's latency may sit under and still count as low latency, read once fromPROP_LOW_LATENCY_MULTIPLE. Three is deliberately permissive: roughly half of all peers fall within two times a top-50 floor, so this admits the fast end of each transport's population and rejects the tail.Read at construction rather than per decision so the bar cannot shift underneath a cycle, and held at a sane minimum so a misconfigured value cannot make the floor vacuous.
PROP_MINIMUM_FAST_PEERS
Property key naming the fast tier's refill target.- See Also:
SCALING_THRESHOLD
static final int SCALING_THRESHOLDNetwork size above which tier thresholds scale with the network instead of staying at their configured default.- See Also:
_defaultMinFastPeers
public static volatile int _defaultMinFastPeersFast tier refill target, in peers, as last configured or autotuned.PROP_MAX_ROUTERINFO_AGE_HOURS
Property key naming the RouterInfo age past which a RouterInfo is treated as stale and must show proof of life to be selected, in hours.- See Also:
DEFAULT_MAX_ROUTERINFO_AGE_HOURS
public static final int DEFAULT_MAX_ROUTERINFO_AGE_HOURSDefault RouterInfo age past which a RouterInfo is treated as stale and must show proof of life to be selected, in hours.- See Also:
PROP_ROUTERINFO_REFRESH_AGE_MS
Property key naming the RouterInfo age past which a tier promotion is held back, in ms.Set to 0 or less to disable the deferral entirely, which restores plain promotion on whatever the netdb holds.
- See Also:
DEFAULT_ROUTERINFO_REFRESH_AGE_MS
public static final long DEFAULT_ROUTERINFO_REFRESH_AGE_MSOne hour, i.e. one republish interval.- See Also:
STARTUP_GRACE_PERIOD_MS
private static final long STARTUP_GRACE_PERIOD_MSGrace period after startup before peers are penalized.- See Also:
PROOF_OF_LIFE_WINDOW_MS
private static final long PROOF_OF_LIFE_WINDOW_MSWindow for trusting a stale router info as proof of life.- See Also:
PROP_MAXIMUM_FAST_PEERS
Property key naming the fast tier's ceiling.- See Also:
_defaultMaxFastPeers
public static volatile int _defaultMaxFastPeersFast tier ceiling, in peers.PROP_MINIMUM_HIGH_CAPACITY_PEERS
Property key naming the high-capacity tier's refill target.- See Also:
DEFAULT_MINIMUM_HIGH_CAPACITY_PEERS
public static final int DEFAULT_MINIMUM_HIGH_CAPACITY_PEERSDefault high-capacity tier refill target, in peers.- See Also:
_defaultMinHighCapPeers
public static volatile int _defaultMinHighCapPeersHigh-capacity tier refill target, in peers, as last configured or autotuned.PROP_MAXIMUM_HIGH_CAPACITY_PEERS
Property key naming the high-capacity tier's ceiling.- See Also:
_defaultMaxHighCapPeers
public static volatile int _defaultMaxHighCapPeersHigh-capacity tier ceiling, in peers.MIN_TUNNEL_ACCEPTANCE_RATIO
private static final double MIN_TUNNEL_ACCEPTANCE_RATIOMinimum tunnel acceptance ratio (40%) to remain in high-capacity/fast tiers.- See Also:
MIN_TUNNEL_REQUESTS
static final int MIN_TUNNEL_REQUESTSLifetime requests a peer needs before its acceptance ratio may exclude it.Package-visible because the tiers apply different floors to it and the boundary between "judged" and "not judged" is exactly the difference. The high-capacity tier passes this value and the fast tier passes one, so the same counts give opposite answers on the two.
TierAdmissionFloorTestpins that boundary.- See Also:
TUNNEL_DEMOTION_COOLDOWN_MS
private static final long TUNNEL_DEMOTION_COOLDOWN_MSCooldown period (ms) after demotion before peer can be re-promoted.- See Also:
MAX_LIFETIME_TUNNEL_FAILURES
private static final long MAX_LIFETIME_TUNNEL_FAILURESExclude peers from tunnel selection after this many cumulative failures OR when the lifetime failure ratio exceedsMAX_LIFETIME_FAILURE_RATIO, whichever is stricter. The hard cap is high (50) to avoid permanently excluding long-lived peers that accumulated failures over days but are currently healthy. BetweenSOFT_FAILURE_PENALTY_THRESHOLD(20) and this cap, peers are penalized (lower selection priority) but not excluded — a lightweight exponential backoff that still allows recovery.- See Also:
SOFT_FAILURE_PENALTY_THRESHOLD
private static final long SOFT_FAILURE_PENALTY_THRESHOLDFailure count above which peers receive a selection priority penalty but are not excluded. Between this andMAX_LIFETIME_TUNNEL_FAILURES, peers are deprioritized rather than banned.- See Also:
MAX_LIFETIME_FAILURE_RATIO
private static final double MAX_LIFETIME_FAILURE_RATIOMaximum lifetime failure ratio (failed / (agreed + failed)) before a peer is excluded. A peer with 50%+ failure rate is unreliable regardless of absolute count. Combined withMAX_LIFETIME_TUNNEL_FAILURES, this prevents long-lived peers with terrible records from staying selectable.- See Also:
MIN_HC_TIGHT_COUNT
private static final int MIN_HC_TIGHT_COUNTWhen high-cap tier has at least this many peers, require actual capacity threshold — stop bucket-filling with marginal peers.- See Also:
MIN_FAST_QUALITY_COUNT
private static final int MIN_FAST_QUALITY_COUNTWhen fast tier has at least this many peers, require all tests passing — peer test (low latency), active, no recent failures, AND proven tunnel throughput. Peers that are low-latency but have never participated in a real tunnel are excluded.- See Also:
MAX_SOFT_PURGE_FLOOR
static final int MAX_SOFT_PURGE_FLOORUpper bound on the soft-purge tier size, in peers.Caps
softPurgeThreshold(int)so that on a large network the threshold stays reachable rather than scaling without limit.- See Also:
HIGH_CAP_ADMISSION_REQUIRES_ACCEPTANCE
static final boolean HIGH_CAP_ADMISSION_REQUIRES_ACCEPTANCEWhether high-capacity admission requires acceptable tunnel acceptance. False: high capacity is the reservoir the fast tier is seeded from, so requiring acceptance here would filter it by the same criterion that empties the fast tier, leaving nothing to seed from.- See Also:
FAST_ADMISSION_REQUIRES_ACCEPTANCE
static final boolean FAST_ADMISSION_REQUIRES_ACCEPTANCEWhether fast-tier admission requires acceptable tunnel acceptance. True: the fast tier is the selective tier, and a peer that declines most of our build requests is wasted work wherever it is listed.- See Also:
EVICTION_REQUIRES_ACCEPTANCE_BOTH_TIERS
static final boolean EVICTION_REQUIRES_ACCEPTANCE_BOTH_TIERSWhether acceptance-based eviction applies to both tiers. True: admission is permissive and eviction is what keeps poor accepters from accumulating, so the two must disagree on admission and agree on eviction.- See Also:
PROP_LOSSY_THRESHOLD
Config property for the loss ratio above which a peer is demoted from fast/high-cap tiers.- See Also:
DEFAULT_LOSSY_THRESHOLD
private static final float DEFAULT_LOSSY_THRESHOLDDefault loss ratio threshold (20% of packets retransmitted).- See Also:
_tunedLossyThreshold
private static volatile float _tunedLossyThresholdTuned lossy threshold, -1.0 = use router config. Set by the Tuner when autotuning profileOrganizer.lossyThreshold.PROP_LOSSY_WINDOW
Config property for how long a reported loss ratio stays fresh.- See Also:
DEFAULT_LOSSY_WINDOW
private static final long DEFAULT_LOSSY_WINDOWDefault freshness window in ms (10 minutes).- See Also:
PROP_LOSSY_MIN_PACKETS
Config property for the minimum number of transmitted packets before a loss ratio is reported.- See Also:
DEFAULT_LOSSY_MIN_PACKETS
public static final int DEFAULT_LOSSY_MIN_PACKETSDefault minimum packet sample size (20 = full 5% bucket resolution).- See Also:
PROP_LOSSY_MODERATE_THRESHOLD
Config property for the decayed loss score above which a peer is de-prioritized in tier selection (soft signal, below the demotion threshold). Default 10%: a peer can be consistently lossy without ever crossing the hard demotion bar, and this keeps such peers out of the preferred picks without evicting them.- See Also:
DEFAULT_LOSSY_MODERATE_THRESHOLD
private static final float DEFAULT_LOSSY_MODERATE_THRESHOLDDefault packet loss marking a peer moderately lossy.- See Also:
LOSS_READMIT_MIN_AGE
private static final long LOSS_READMIT_MIN_AGEMinimum time after a loss demotion before a peer may be re-admitted, even with fresh clean evidence. Reduced from 30 min to 10 min to accelerate tier recovery after congestion clears. Asymmetric hysteresis: demotion is instant on weak evidence, re-admission requires both this age and a fresh clean measurement (seeshouldReadmitLossy(PeerProfile, long)).- See Also:
LOSS_FAST_TRACK_RATIO
private static final float LOSS_FAST_TRACK_RATIOFast-track readmission: a fresh loss report below this ratio clears the minimum age even when the tiers are full. Near-spotless evidence is proof enough of recovery. When either tier is under target the fast-track widens to any fresh below-threshold report (seelossyReadmitAllowed(float, long, long, long, long, long, float, float, boolean)): a short tier cannot afford to hold a peer out for a clean-but-not-spotless measurement.- See Also:
LOSSY_SELECTION_PENALTY
private static final float LOSSY_SELECTION_PENALTYSelection penalty for moderately-lossy peers: their random priority range is multiplied by this factor, so they are this many times less likely to be picked than an equal-latency clean peer. Lossiness becomes one of several selection signals instead of a hard gate.- See Also:
DEFAULT_PRIORITY_LATENCY_MS
private static final float DEFAULT_PRIORITY_LATENCY_MSLatency used as a peer's selection weight when it has no measured peer-test average. The widest weight means the widest priority range, so an unmeasured peer is drawn last.- See Also:
AUTO_RTT_MULTIPLIER
private static final double AUTO_RTT_MULTIPLIERAdaptive absolute-RTT ceiling for fast-tier selection. The fast tier is a relative ranking (top N% by speed), so on a degraded network its members can still sit at high absolute latency and intermittently drop build messages. The existingdemoteIfHighRTT(Hash, long)uses a fixed bar and only fires on individual peer-test events. These constants define a selection-time ceiling derived from the measured fast-tier RTT boundary (_thresholdRTT): the ceiling is the boundary times AUTO_RTT_MULTIPLIER, clamped to at least AUTO_RTT_FLOOR_MS and at most AUTO_RTT_CAP_MS. Peers whose measured tunnel-test RTT exceeds the ceiling are excluded from fast-tier picks (soft signal, not a tier eviction — consistent with the lossiness-is-one-signal philosophy).- See Also:
AUTO_RTT_FLOOR_MS
private static final long AUTO_RTT_FLOOR_MSFloor: never exclude peers below this RTT, matching the demoteIfHighRTT bar.- See Also:
AUTO_RTT_CAP_MS
private static final long AUTO_RTT_CAP_MSAbsolute cap: the ceiling never rises above this even on a fully slow pool.- See Also:
PROP_MAX_PROFILES
Config property for the maximum number of peer profiles.- See Also:
_defaultMaxProfiles
public static volatile int _defaultMaxProfilesRuntime-adjustable default max profile count.ABSOLUTE_MAX_PROFILES
public static final int ABSOLUTE_MAX_PROFILESCeiling on profiles held in memory, in profiles.Bounds the RAM store only. What is kept on disk is governed separately by
MAX_STORED_PROFILE_FILES, so raising this to let the router use more of its persisted knowledge does not shorten the retention of that knowledge across a restart.- See Also:
MAX_STORED_PROFILE_FILES
public static final int MAX_STORED_PROFILE_FILESProfile files retained on disk before surplus ones are trimmed.Deliberately a different number, and a different concern, from
ABSOLUTE_MAX_PROFILES: that one bounds profiles held in memory, this one bounds what is kept on disk so it survives a restart. Using one constant for both meant that lowering the disk floor would silently lower the memory ceiling too.Below this count no file is deleted unless the peer is genuinely stale, so the floor costs nothing while the store is smaller than it and only starts trimming once the store exceeds it.
- See Also:
MIN_MAX_PROFILES
public static final int MIN_MAX_PROFILESFloor for the in-memory profile cap, in profiles.Low enough that a small-heap router still runs;
getDefaultMaxProfiles()returns this instead ofABSOLUTE_MAX_PROFILESon a slow system.- See Also:
RATES
private static final long[] RATESRate stat periods for the profile statistics._reorganizeLock
Lock guarding the profile maps during reorganization._lastStarveWarn
private volatile long _lastStarveWarnTimestamp of the last starved-tier warning; selections hold only the read lock, so this is read and written concurrently.MAX_BAD_REPLIES_PER_HOUR
private static final int MAX_BAD_REPLIES_PER_HOURBad netdb replies per hour before a peer is penalized.- See Also:
ENOUGH_PROFILES
private static final int ENOUGH_PROFILESProfile count considered enough for full selection.DEFAULT_MIN_CANDIDATE_SAMPLE
static final int DEFAULT_MIN_CANDIDATE_SAMPLEDefault floor for candidates examined per selection. @since 0.9.71+.- See Also:
MAX_CANDIDATE_SAMPLE
static final int MAX_CANDIDATE_SAMPLEUpper bound, so a typo cannot turn selection into a full tier scan. @since 0.9.71+.- See Also:
PROP_MIN_CANDIDATE_SAMPLE
Property for the floor; seegetMinCandidateSample(RouterContext).- See Also:
_minCandidateSample
private static volatile int _minCandidateSampleMinimum candidate sample size for threshold math.STARVE_WARN_INTERVAL_MS
private static final long STARVE_WARN_INTERVAL_MSMinimum delay between "tier returned no candidates" warnings. A starved tier is the signal that one gate is rejecting everything, so the warning carries the per-gate counts. It must not flood the log while starvation persists across build passes.- See Also:
_bannedAtSelection
Banned-candidate rejections seen since the lastflushSelectionCounters()._cachedMaxRouterInfoAge
private volatile long _cachedMaxRouterInfoAgeCachedgetMaxRouterInfoAgeMs(), and the timestamp of the fetch. @since 0.9.71+._cachedMaxRouterInfoAgeTime
private volatile long _cachedMaxRouterInfoAgeTimeWhen_cachedMaxRouterInfoAgewas fetched. @since 0.9.71+.ADDRESS_REFRESH_TIMEOUT_MS
private static final long ADDRESS_REFRESH_TIMEOUT_MSTimeout for a RouterInfo lookup issued on behalf of a held peer.- See Also:
_fmt
Shared formatter for the statistics display._cachedBuildSuccess
private volatile double _cachedBuildSuccessShort-lived cache forgetTunnelBuildSuccess(). The 6 RateStat lookups are expensive when called per-candidate in peer selection (up to 400 peers × 6 lookups). The ratio is a 10-minute rolling average, so a 15s cache (<2.5% staleness) eliminates ~40x redundant lookups with negligible impact on decision accuracy._cachedBuildSuccessTime
private volatile long _cachedBuildSuccessTimeWhen the build success ratio was last read.BUILD_SUCCESS_CACHE_MS
private static final long BUILD_SUCCESS_CACHE_MSHow long the cached build success ratio stays fresh.- See Also:
NO_HISTORY_ACTIVITY_WINDOW_MS
private static final long NO_HISTORY_ACTIVITY_WINDOW_MSPeers with zero tunnel history are allowed into fast/high-cap tiers if they were heard from within this window. Alive peers that haven't yet built tunnels get a chance. Dead peers are excluded. Under-performers are evicted by existing mechanisms (latency, acceptance ratio, loss probation) within minutes.- See Also:
Constructor Details
ProfileOrganizer
Creates a profile organizer with empty tier maps and registers performance tracking statistics for peer classification.- Parameters:
context- the router context providing config, stats, and log access
Method Details
getProfileCount
public int getProfileCount()Profiles held in RAM, as of the last completed reorganisation.- Returns:
- the in-RAM profile count
getQualityCount
public int getQualityCount()Peers passing the quality gates, as of the last completed reorganisation.- Returns:
- the quality peer count
getActiveProfileCount
public int getActiveProfileCount()Profiles with recent activity, as of the last completed reorganisation.Computed during the reorganisation walk rather than on demand:
countActivePeersInLastHour()iterates every profile while holding _reorganizeLock, which is the lock tunnel peer selection contends on, and CoalesceStatsEvent runs every 50s. Publishing it here costs one comparison per profile in a walk that already holds the lock.- Returns:
- the recently active profile count
isActiveInWindow
Whether a profile shows activity within the active window: a peer test counted as active, a successful or failed send, or any contact. Split out so the rule is testable without a reorganise cycle or a live profile set.- Parameters:
profile- profile whose send and heard-from timestamps are compared against the cutoffhideBefore- cutoff time, i.e. now minusACTIVE_WINDOW_MS- Returns:
- true if the profile is recently active
isQualityFastPeer
True when a peer in the fast tier has proven throughput or is in the high-cap tier.- Parameters:
profile- the fast-tier profile to testhighCap- the high-capacity tier, for a membership test by peer hash- Returns:
- true if peak 1m throughput is above 0 KBps or the peer is in highCap
putFastPeer
Add a peer to the fast tier, maintaining the quality counter.- Parameters:
peer- the peer's hash, the tier map keyprofile- the profile to store for that peer, replacing any earlier entry
removeFastPeer
Remove a peer from the fast tier, maintaining the quality counter.- Parameters:
peer- the peer's hash, the tier map key- Returns:
- the profile stored for that peer, or null if it was not in the tier
getDefaultMinFastPeers
public static int getDefaultMinFastPeers()Fast tier size the router refills toward.- Returns:
- the current minimum number of fast-tier peers
setDefaultMinFastPeers
public static void setDefaultMinFastPeers(int val) Override the fast tier refill target, clamped to 50-2000 peers.- Parameters:
val- requested minimum fast tier size, in peers
getDefaultMaxFastPeers
public static int getDefaultMaxFastPeers()Ceiling on the fast tier.- Returns:
- the current maximum number of fast-tier peers
setDefaultMaxFastPeers
public static void setDefaultMaxFastPeers(int val) Override the fast tier ceiling, clamped to 200-3000 peers.- Parameters:
val- requested maximum fast tier size, in peers
getMinHighCapacityPeers
public static int getMinHighCapacityPeers()High-capacity tier size the router refills toward.- Returns:
- the current minimum number of high-capacity-tier peers
setMinHighCapacityPeers
public static void setMinHighCapacityPeers(int val) Override the high-capacity tier refill target, clamped to 50-3000 peers.- Parameters:
val- requested minimum high-capacity tier size, in peers
getDefaultMaxHighCapPeers
public static int getDefaultMaxHighCapPeers()Ceiling on the high-capacity tier.- Returns:
- the current maximum number of high-capacity-tier peers
setDefaultMaxHighCapPeers
public static void setDefaultMaxHighCapPeers(int val) Override the high-capacity tier ceiling, clamped to 200-6000 peers.- Parameters:
val- requested maximum high-capacity tier size, in peers
setLossyThreshold
public static void setLossyThreshold(float ratio) Set the lossy demotion threshold (called by Tuner).- Parameters:
ratio- the loss ratio threshold
getLossyThreshold
The lossy demotion threshold in effect, tuned value if set else config.- Parameters:
ctx- the router context- Returns:
- the loss ratio threshold
getDefaultMaxProfilesValue
public static int getDefaultMaxProfilesValue()Profile count the reorganizer targets by default.- Returns:
- the current default maximum number of stored profiles
setDefaultMaxProfiles
public static void setDefaultMaxProfiles(int val) Override the default profile count, clamped toMIN_MAX_PROFILES-ABSOLUTE_MAX_PROFILES.- Parameters:
val- requested maximum profile count
getDefaultMaxProfiles
private static int getDefaultMaxProfiles()Returns the default profile count from the config.- Returns:
- the maximum number of profiles
isFirewalled
private boolean isFirewalled()Check if current router is firewalled to adjust peer selection thresholds.- Returns:
- true if the comm system reports FIREWALLED or REJECT_UNSOLICITED
getReadLock
private void getReadLock()Takes the reorganization read lock.tryReadLock
private boolean tryReadLock()Takes the read lock if it is free.- Returns:
- true if the lock was taken
releaseReadLock
private void releaseReadLock()Releases the reorganization read lock.tryWriteLock
private boolean tryWriteLock()Takes the write lock if it is free.- Returns:
- true if the lock was taken
getWriteLock
private boolean getWriteLock()Takes the reorganization write lock, waiting if needed.- Returns:
- true if the lock was taken
releaseWriteLock
private void releaseWriteLock()Releases the reorganization write lock.setUs
Store the local router hash for self-exclusion from peer selection.- Parameters:
us- our own router hash
getUs
Self-exclusion hash for this router instance.- Returns:
- the local router hash, or null if not set
getSpeedThreshold
public double getSpeedThreshold()Current minimum speed value for the fast peer tier.- Returns:
- the speed threshold computed during the last reorganize
getFastRTTThreshold
public double getFastRTTThreshold()Average tunnel test time (RTT in ms) of the peer at the fast-tier speed boundary from the last reorganize, or 0.0 if never reorganized.- Returns:
- the boundary RTT in ms
getCapacityThreshold
public double getCapacityThreshold()Current minimum capacity value for the high-capacity peer tier.- Returns:
- the capacity threshold computed during the last reorganize
getIntegrationThreshold
public double getIntegrationThreshold()Current minimum integration value for the well-integrated peer tier.- Returns:
- the integration threshold computed during the last reorganize
getProfile
Retrieve the profile for a peer, or null if unknown. Blocks on the reorganize read lock.- Parameters:
peer- the router hash to look up- Returns:
- the existing profile, or null if none exists
getProfileNonblocking
Retrieve the profile for a peer without blocking. Returns null immediately if the lock is contended.- Parameters:
peer- the router hash to look up- Returns:
- the existing profile, or null if none exists or lock unavailable
getOrCreateProfileNonblocking
Retrieve an existing profile or create a new one, without blocking on contention. Uses try-lock escalation to avoid blocking the caller.- Parameters:
peer- the router hash to look up or create a profile for- Returns:
- the existing or new profile, or null if the peer is excluded or lock contention
addProfile
Insert a fully-constructed profile into the organizer. The profile is added to the not-failing set and high-capacity tier if it meets the current capacity threshold.- Parameters:
profile- the profile to add- Returns:
- the previous profile for the same peer, or null if none existed
count
Counts the profiles in one pool map.- Parameters:
m- the pool map- Returns:
- the number of profiles
countFastPeers
public int countFastPeers()Number of peers classified as fast tier.- Returns:
- fast peer count (read-lock protected)
countHighCapacityPeers
public int countHighCapacityPeers()Number of peers classified as high-capacity tier.- Returns:
- high-capacity peer count (read-lock protected)
countNotFailingPeers
public int countNotFailingPeers()Total number of peer profiles in memory (all tiers).- Returns:
- profile count (read-lock protected)
countActivePeers
public int countActivePeers()Count peers with any send or hear activity in the last 4 hours.- Returns:
- active peer count (read-lock protected)
countActivePeersInLastHour
public int countActivePeersInLastHour()Count peers with activity (send, receive, or failure) within the last hour.- Returns:
- recently active peer count (read-lock protected)
isX
Checks whether a peer is in one pool map.- Parameters:
m- the pool mappeer- the peer hash- Returns:
- true if the peer is in the map
isFast
Check whether a peer is classified in the fast tier.- Parameters:
peer- the router hash to check- Returns:
- true if the peer is in the fast tier set
getFastQualityCount
public int getFastQualityCount()The number of quality peers currently in the fast tier. A quality peer is fast and either high-capacity or has proven throughput. The count gates fast-tier promotion internally: at or above MIN_FAST_QUALITY_COUNT (500) the tier is treated as healthy.- Returns:
- the count of quality fast peers
isHighCapacity
Check whether a peer is classified in the high-capacity tier.- Parameters:
peer- the router hash to check- Returns:
- true if the peer is in the high-capacity tier set
isWellIntegrated
Check whether a peer is classified in the well-integrated tier.- Parameters:
peer- the router hash to check- Returns:
- true if the peer is in the well-integrated tier set
clearProfiles
void clearProfiles()Remove all profiles and clear all tier maps. Package-private: only called from tests and router shutdown paths.peerSendsBadReplies
Check whether a peer sends an excessive rate of invalid or failed netDb lookup replies (potential misbehavior indicator).- Parameters:
peer- the router hash to check- Returns:
- true if the peer exceeds the bad-reply threshold in the last hour
exportProfile
Write a peer profile to the given output stream for persistence.- Parameters:
profile- the router hash identifying the profile to exportout- the destination stream for the serialized profile- Returns:
- true if the profile was found and written, false if no profile exists
- Throws:
IOException- on write errors
remainingToSelect
How many slots are still free in a cascade's destination set.Every tier selector takes
howManyas the total the caller wants held inmatcheswhen the cascade returns, and every tier — the first one included — asks for only the shortfall this reports. That single convention is what keeps the arithmetic in one place: a call site that pre-subtractsmatches.size()itself is a second copy of this computation, and the two copies disagree the moment one of them drifts.The failure mode that makes it worth centralising: a caller that sized an
ArraySetto exactlyhowManyhas already filled part of it by the time a later tier runs, so a tier that wrotehowManyagain would overflow it andArraySetwould throwSetFullException— surfaced aspickLowestPrioritycrashing, which names the symptom rather than the cascade arithmetic behind it.- Parameters:
howMany- total peers the caller wants held in matches across the whole cascadematches- peers picked so far, by the caller and by the tiers that already ran- Returns:
- slots still needed, never negative
stillSelecting
Whether a cascade still has slots left to fill.A guard, not a computation the callers should repeat: every tier-selector shortfall branch needs the same
remaining > 0question, and writing it asmatches.size() < howManyin ten places is what let one site drift.- Parameters:
howMany- peers the caller asked for in totalmatches- peers picked by the tiers that ran before this one- Returns:
- true if the set still needs more peers
selectFastPeers
selectFastPeers
public void selectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask, MaskedIPSet ipSet) Select fast-tier peers with IP-subnet diversity constraints. Falls through to high-capacity tier on shortfall.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesmask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnets
selectFastPeers
public void selectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, SessionKey randomKey, ProfileOrganizer.Slice subTierMode, int mask, MaskedIPSet ipSet) Select fast-tier peers deterministically sliced by a random key. This enables different peers per tunnel to improve path diversity.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesrandomKey- key for deterministic sub-tier assignmentsubTierMode- slicing mode (SLICE_ALL to skip slicing)mask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnets
selectHighCapacityPeers
selectHighCapacityPeers
public void selectHighCapacityPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask, MaskedIPSet ipSet) Select high-capacity peers with IP-subnet diversity.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesmask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnets
selectNotFailingPeers
selectNotFailingPeers
public void selectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask, MaskedIPSet ipSet) Select from not-failing peers with IP-subnet diversity.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesmask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnets
selectNotFailingPeers
public void selectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing) Select from not-failing peers, optionally excluding the high-capacity tier.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesonlyNotFailing- if true, exclude peers already in high-capacity tier
selectNotFailingPeers
public void selectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet) Full-parameter select from not-failing peers.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesonlyNotFailing- if true, exclude peers already in high-capacity tiermask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnets
selectNotFailingPeers
public void selectNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet, boolean preferUnproven) Full-parameter select from not-failing peers.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesonlyNotFailing- if true, exclude peers already in high-capacity tiermask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnetspreferUnproven- if true, prioritize peers with no tunnel test history so they accumulate profiling data
selectActiveNotFailingPeers
selectActiveNotFailingPeers
public void selectActiveNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask, MaskedIPSet ipSet) Select from currently connected peers with IP-subnet diversity.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesmask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnets
selectTestPeersNonBlocking
Non-blocking test peer selection: tries the full fast → high-cap → active chain under a singletryReadLock(). Returns false (leavingmatchesunchanged) if the read lock cannot be acquired. The caller should skip this round and requeue. Used exclusively byPeerTestJobto avoid blocking on reorganize()'s write lock during startup profiling.- Parameters:
howMany- target number of peersexclude- peers to excludematches- output set populated with selected peer hashes- Returns:
- true if selection ran, false if lock was not acquired
selectHighBandwidthPeers
Select up to howMany peers from O/P/X bandwidth tiers (high shared bandwidth) that are not failing. Falls through to selectAllNotFailingPeers on shortfall.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashes
selectHighBandwidthPeers
public void selectHighBandwidthPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet) Select high-bandwidth peers with tier-exclusion and IP-subnet diversity.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesonlyNotFailing- if true, exclude peers already in high-capacity tiermask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnets
selectHighBandwidthPeers
private void selectHighBandwidthPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet, double buildSuccess) Iterates not-failing peers randomly, filtering for O/P/X bandwidth tiers with reliability checks (acceptance ratio, recent activity, peer tests).- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesonlyNotFailing- if true, exclude peers already in high-capacity tiermask- bitmask length for /n diversity restriction (0 to disable)ipSet- mutable set tracking already-selected subnets, or null when mask is 0buildSuccess- the build success ratio, fetched once per scan
isReliableBandwidthPeer
Reliability gate for high-bandwidth (O/P/X tier) selection: the peer must show a tunnel acceptance ratio of at least 0.3 AND evidence of recent activity. A successful peer test within 10 minutes, a heard- from/send-success within 30 minutes, or an established commSystem connection. Peers with no profile are not gated (no data yet).Pure decision — no context access, safe for unit tests.
- Parameters:
profile- the peer profile (non-null)now- current time in msestablished- whether commSystem has an established connection- Returns:
- whether the peer passes the reliability gate
selectAllNotFailingPeers
public void selectAllNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing) Select from all not-failing peers randomly, falling through to general peers if the not-failing set is insufficient.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesonlyNotFailing- if true, exclude peers already in high-capacity tier
selectAllNotFailingPeers
public void selectAllNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet) Select from all not-failing peers randomly, honouring a /n subnet diversity restriction.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesonlyNotFailing- if true, exclude peers already in high-capacity tiermask- bitmask length for /n diversity restriction (0 to disable)ipSet- subnets already represented in this tunnel
selectAllNotFailingPeers
private void selectAllNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask, MaskedIPSet ipSet, double buildSuccess, boolean preferUnproven) Random iterator over not-failing list with optional tier exclusion. Falls through toselectAllPeers()if shortfall remains.- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesonlyNotFailing- if true, exclude peers already in high-capacity tiermask- bitmask length for /n diversity restriction (0 to disable)ipSet- subnets already represented in this tunnel, consulted only when mask is non-zerobuildSuccess- the build success ratio, fetched once per scan and consumed by the selectability gatepreferUnproven- if true, prioritize peers with no tunnel test history so they accumulate profiling data through exploratory builds
selectRemainderFromAllPeers
private void selectRemainderFromAllPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, double buildSuccess) Fallback selection: fill the remaining slots from the union of all peers (fast + high-capacity + not-failing).- Parameters:
howMany- target number of peersexclude- peers to exclude (may be null)matches- output set populated with selected peer hashesbuildSuccess- the build success ratio, fetched once per scan
selectAllPeers
selectFastPeers
selectHighCapacityPeers
selectWellIntegratedPeers
getStoredProfileCount
public int getStoredProfileCount()Last known number of profiles stored on disk, from the most recent load, cleanup, or purge in the persistence helper.- Returns:
- the stored profile count, 0 if the store has never been scanned
getEnoughProfiles
private static int getEnoughProfiles()Derives the enough-profiles count from the config.- Returns:
- the profile count
reorganize
void reorganize()Run a standard reorganize round without coalescing. Equivalent to reorganize(false).latencySampleIsBroadEnough
static boolean latencySampleIsBroadEnough(int measured, int population) Fraction of a tier population that must carry a usable first-hop RTT before latency is allowed to influence tier membership.A low-latency decision taken over a small measured subset describes that subset, not the tier: the peers we happen to have sessions with are the ones we have been talking to, so an early sample is drawn from whoever was contacted most recently rather than from the population. Requiring a majority of the tier to be measured keeps the decision anchored to the tier it is applied to.
- Parameters:
measured- peers in the tier with a usable first-hop RTTpopulation- peers in the tier- Returns:
- true when latency may be used to judge this tier
sampleFirstHopRtts
int sampleFirstHopRtts()Refresh direct-link RTT for tier peers that already have a transport session, and re-judge their low-latency standing.Decoupled from the build pre-connect on purpose. The pre-connect is a one-shot probe fired to force establishment, so it reads the transport at the least informative moment: a session that was just created has not completed a round trip and reports zero. This samples instead from sessions that have been up long enough to have measured, which is the value the latency tiers actually want.
Peers with no session are skipped rather than probed. Probing here would re-create the problem above: establishing a session to measure it is what makes the measurement unavailable.
- Returns:
- number of peers whose RTT was refreshed
feedBaseline
private void feedBaseline(Map<String, ProtocolRttBaseline> baselines, PeerProfile profile, Collection<Transport> transports) Add this peer's best measured latency to its winning transport's baseline.The same transport whose value
sampleFirstHopRtt(PeerProfile, Collection, long)recorded: the fastest of the peer's sessions. A peer with no readable latency contributes nothing, which is correct - a baseline describes measured links.- Parameters:
baselines- per-style accumulators for this cycleprofile- the sampled peertransports- live transports to query
publishBaselines
Publish this cycle's baselines and report them as stats.The stat is what makes the calibration observable rather than assumed: a baseline of 0 means the transport has not yet gathered enough samples, and the low-latency judgement is running on the absolute floor unchanged. Watching the sample count alongside the value is the check that a transport is actually supplying latency.
- Parameters:
baselines- per-style accumulators built this cycle
getCalibratedLatencyFloorMs
The calibrated latency floor for a transport style, or -1 when that transport has not gathered enough samples to have an opinion.Callers must treat -1 as "no calibrated opinion" and fall back to the absolute rule, never as "this peer qualifies".
- Parameters:
style- the transport style, e.g. "NTCP2" or "SSU2"- Returns:
- the calibrated floor in ms, or -1 if unknown
getCalibratedLatencyFloorMs
The calibrated latency floor for a peer, choosing the style of the transport that measured it.- Parameters:
peer- the peer to look up- Returns:
- the calibrated floor in ms, or -1 if unknown
publishLatencyCoverage
private void publishLatencyCoverage()Report how much of each tier holds a current first-hop RTT.This is the question the other stats cannot answer. Tier size says how many peers exist, and the baseline stats say how many samples fed the floor, but neither says how many peers hold a reading that is still inside the validity window - a tier can be fully counted and largely unmeasured while looking identical to a healthy one from outside.
Reported as a count against nothing rather than a ratio, because a ratio computed over a shrinking tier flatters itself.
applyBaselineMultiple
private long applyBaselineMultiple(long baselineMs) Scales a baseline timeout by the configured multiple.- Parameters:
baselineMs- the baseline timeout- Returns:
- the adjusted timeout
applyBaselineMultiple
static long applyBaselineMultiple(long baselineMs, int multiple) Scale a calibrated baseline into the latency a peer may sit under and still count as low latency.Pure arithmetic, extracted so the overflow and sentinel rules can be exercised without a router context.
- Parameters:
baselineMs- the calibrated baseline in msmultiple- how many times it may be exceeded; values of 1 or less return the baseline unscaled- Returns:
- the qualifying threshold in ms, or
baselineMsunchanged when it carries no opinion
getCalibratedLatencyThresholdMs
The qualifying latency threshold for a peer, from the calibrated baseline of the transport that measured it.This is what the profile actually compares against - the baseline scaled by
PROP_LOW_LATENCY_MULTIPLE- as opposed togetCalibratedLatencyFloorMs(Hash), which returns the unscaled baseline thatpeer.latencyBaseline.*reports.- Parameters:
peer- the peer to look up- Returns:
- the qualifying threshold in ms, or -1 if unknown
getCohortLatencyThresholdMs
long getCohortLatencyThresholdMs()The qualifying latency threshold for the fast cohort, from the cohort baseline of its fastest members.Scaled on the same terms as
getCalibratedLatencyThresholdMs(Hash)so the two bounds stay comparable; a cohort mean and a transport baseline are the same kind of quantity and neither is a cutoff on its own.- Returns:
- the qualifying threshold in ms, or a non-positive value when the cohort has too few measured peers to hold an opinion
sampleTierPeers
Bounded random sample of the retained peers for RTT measurement, capped atDEFAULT_MIN_CANDIDATE_SAMPLEwith a rotated start so successive cycles do not always read the same prefix.Sampling is safe here because a peer's RTT is a property of its transport session, not an accumulator: a peer missed this cycle is measured in a later one.
Covers every tier the Organizer retains, not just the two the fast tier is drawn from. A profile outside the fast and high-capacity tiers can still hold the only current measurement of a peer that later qualifies, and reading the tiers it can currently be selected from would leave its latency unmeasured until something promoted it - at which point the first hop is chosen on no evidence at all. There is no larger population to read: profiles are retained only in these maps, so this is the whole of what exists.
- Returns:
- the sampled profiles, at most
sampleFirstHopRttLimit(int)
sampleFirstHopRttLimit
static int sampleFirstHopRttLimit(int total) How many tier peers to sample for first-hop RTT in one reorganize.Pure decision — no context access, safe for unit tests.
- Parameters:
total- combined size of the two tiers- Returns:
- the sample size, never negative
sampleFirstHopRtt
Record the best RTT any transport reports for this peer, if any.- Parameters:
profile- the peer to sample; nothing is recorded when it is nulltransports- the peer's transports, each asked for its estimated RTT — the largest reported value winsnow- router time in ms, stamped onto the sample when the winning transport reports no measurement time- Returns:
- the recorded RTT in ms, or 0 if there was no measurement
fastOrHighCapCount
int fastOrHighCapCount()Peers in either the fast or the high-capacity tier, counting each peer once.A peer can be both, so neither tier contains the other and adding the two counts over-reports. This is the number the profile-tier graph needs for its high-capacity line to be a true subset of the fast line, rather than merely a smaller number that happens to be plotted beside it.
Walks the smaller tier and probes the larger, so the cost is
O(min(fast, highCap))once per coalesce rather than a full merge.- Returns:
- size of the union of the two tiers
getFastOrHighCapCount
public int getFastOrHighCapCount()Live size of the fast or high-capacity union, a peer in both tiers counted once. Read-locked, so it is safe to call outside a coalesce pass.Callers that already hold the write lock —
reorganize(). Must call the package-visiblefastOrHighCapCount()directly; going through this accessor would re-enter the lock.- Returns:
- size of the union of the two tiers
- See Also:
reorganize
void reorganize(boolean shouldCoalesce) Reorganizes peer profiles into performance-based tiers (fast, high-capacity, etc.) and expires stale entries.This method:
- Coalesces stats if requested and uptime conditions are met. Peak throughput values are never decayed.
- Filters out unreachable, inactive, or low-tier peers.
- Recalculates dynamic thresholds for speed, capacity, and integration.
- Rebuilds internal tier maps and the global profile ordering.
- Expires profiles that haven't been active recently to bound memory usage.
Memory Safety: To prevent unbounded memory growth (e.g., OOM after 8+ hours), this method ensures that expired profiles are removed from all data structures—even if the full reorganization is skipped due to lock contention. A best-effort expiration pass runs outside the write lock to mitigate leaks during high contention.
- Parameters:
shouldCoalesce- iftrue, coalesce statistics for active profiles
isExpiredProfile
static boolean isExpiredProfile(PeerProfile profile, long now, long expireActive, long expirePassive, long expireGossip, long expireUntracked) Whether a profile has expired from its activity tier and should be purged: no activity (send/heard/heard-about) within the tier's expiration window. Tiered expiration: Active > Passive > Gossip; profiles with no tunnel history are purged on the tighter untracked window regardless of tier. A profile with zero timestamps sits in the gossip tier.Pure decision. No context access, safe for unit tests.
- Parameters:
profile- profile to test; its expiration window is picked from its tier, the tier of the newest activity timestamp it carriesnow- current time in msexpireActive- active-tier window in msexpirePassive- passive-tier window in msexpireGossip- gossip-tier window in msexpireUntracked- untracked window in ms- Returns:
- whether the profile is expired
buildCandidateProfiles
private ProfileOrganizer.CandidateSet buildCandidateProfiles(long now, long expireActive, long expirePassive, long expireGossip, long expireUntracked) Builds the candidate profile set for this reorganize round, filtering expired, unreachable, and excluded profiles.- Parameters:
now- current time in ms, the reference point for the windows belowexpireActive- active-tier window in msexpirePassive- passive-tier window in msexpireGossip- gossip-tier window in msexpireUntracked- untracked window in ms, the tighter cap applied to profiles with no tunnel history- Returns:
- the surviving profiles, with the integration total and the expired and kept counts
lockedRebuildTiers
Reinserts all active profiles into the tier maps. Must be called with the write lock held.Also opens the promotion-deferral cycle, which the tier rebuild is the only place that can judge: a promotion is the moment a stale RouterInfo starts costing something, and this loop is the sole promotion path that sees the whole profile population at once.
- Parameters:
candidates- the surviving profile setbuildSuccess- cached tunnel build success ratio
passesTierGates
boolean passesTierGates(PeerProfile profile, double buildSuccess, long now, boolean requireAcceptance) Common gate for fast/high-cap tier fill passes: the peer must be selectable, have acceptable tunnel acceptance, no recent tunnel failures, and not be in loss probation. Evaluation order preserves the original short-circuit chain (cheap pure checks before the netDb-backed selectability check is already handled by callers that test other cheap conditions first).No side effects. Safe to evaluate without holding locks.
Tunnel acceptance is required by the fast-tier passes and not by the high-capacity pass. The two tiers do different jobs: high capacity is the reservoir the fast tier is seeded from, so gating it on acceptance leaves no deep supply to draw on — when a fast peer demotes, the peers beside it in high capacity have already been filtered by the same criterion. High capacity therefore admits any provably alive peer of the right bandwidth class, and acceptance decides fast-tier membership only. A poor accepter is still evicted from high capacity, so the reservoir turns over rather than accumulating them.
- Parameters:
profile- the candidate profilebuildSuccess- the build success ratio in [0.0, 1.0]now- current time in msrequireAcceptance- whether the peer must also have acceptable tunnel acceptance; false admits by capacity and liveness alone- Returns:
- whether the peer passes the tier gates
passesTierGates
Common gate for the fast-tier fill passes, which always require acceptable tunnel acceptance.- Parameters:
profile- the candidate profilebuildSuccess- the build success ratio in [0.0, 1.0]now- current time in ms- Returns:
- whether the peer passes the fast-tier gates
hasRecentTierActivity
True if the peer showed recent activity within the given cutoff: a heard-from or successful send at or after the cutoff. Used to keep stale peers out of the fast/high-cap tiers.Pure decision. No context access, safe for unit tests.
- Parameters:
profile- profile whose heard-from and last-successful-send timestamps are checkedactiveCutoff- earliest allowed activity timestamp in ms- Returns:
- whether the peer has recent activity
computeFastRttCeiling
static long computeFastRttCeiling(double boundaryRttMs) Compute the adaptive absolute-RTT ceiling for fast-tier selection.The ceiling is the measured fast-tier RTT boundary times a multiplier, clamped to at least
AUTO_RTT_FLOOR_MSand at mostAUTO_RTT_CAP_MS. Flooring keeps the bar at least as tight as the existing fixed high-RTT demotion; capping prevents a uniformly-slow pool from raising the ceiling without bound, so clearly-slow outliers are still excluded. Pure decision — no context access, safe for unit tests.- Parameters:
boundaryRttMs- the fast-tier RTT boundary from the last reorganize (_thresholdRTT); its own scaling amplifies the typical pool latency so the ceiling tracks the network- Returns:
- the selection ceiling in ms; never below
AUTO_RTT_FLOOR_MSand never aboveAUTO_RTT_CAP_MS. A boundary at or below 0 (no measurement yet) returns the floor so nothing is over-trimmed.
computeAdaptiveRttCeiling
long computeAdaptiveRttCeiling(double boundaryRttMs, double buildSuccess) Compute the adaptive absolute-RTT ceiling for fast-tier selection, floored at 2× the startup baseline when build success is degraded. Prevents RTT ceiling amplification during cascade failure: when the entire network slows, the ceiling rises with it and peers that would cause failures are allowed through. The baseline floor keeps the ceiling tight during degradation while allowing natural growth when builds are healthy.- Parameters:
boundaryRttMs- the fast-tier RTT boundary from the last reorganizebuildSuccess- current tunnel build success ratio [0.0, 1.0]- Returns:
- the selection ceiling in ms
aboveRttCeiling
True if the peer's measured tunnel-test RTT exceeds the adaptive selection ceiling, indicating it should be excluded from this fast-tier pick while still remaining in the tier (soft signal. The ceiling applies at selection time, not tier membership). A zero/unknown RTT never trips the ceiling.- Parameters:
profile- the candidate profilerttCeilingMs- the adaptive ceiling fromcomputeFastRttCeiling(double)- Returns:
- whether the peer is above the ceiling
isEligibleForTierFill
private boolean isEligibleForTierFill(PeerProfile profile, double buildSuccess, long now, long activeCutoff) Full eligibility gate for filling the fast/high-cap tiers from the active profile set: not ourselves, passes the tier gates, and active within the cutoff window.- Parameters:
profile- the candidate profilebuildSuccess- the build success ratio in [0.0, 1.0]now- current time in msactiveCutoff- earliest allowed activity timestamp in ms- Returns:
- whether the peer may be added to the tier
isEligibleForTierFill
private boolean isEligibleForTierFill(PeerProfile profile, double buildSuccess, long now, long activeCutoff, boolean requireAcceptance) Whether a peer may be admitted to a tier by a fill pass.- Parameters:
profile- the candidate profilebuildSuccess- the build success ratio in [0.0, 1.0]now- current time in msactiveCutoff- peers without activity at or after this are skippedrequireAcceptance- whether acceptable tunnel acceptance is required; false for the high-capacity reservoir, which admits by capacity and liveness alone- Returns:
- whether the peer may be admitted
fillFastTierFallbacks
Fills the fast tier to the minimum via up to three fallback passes. Must be called with the write lock held. Returns the number added.- Parameters:
now- current time in ms, the reference for the tier gates and the activity cutoff of the last passactiveProfiles- profiles surviving this reorganize round; the candidate pool when the high-capacity tier is emptybuildSuccess- the build success ratio in [0.0, 1.0], fetched once per cycle- Returns:
- how many profiles were added to the fast tier, 0 if it already met its minimum
fillHighCapFallback
Fills the high-capacity tier to the minimum with recently-active selectable peers. Must be called with the write lock held. Returns the number added.- Parameters:
now- current time in ms, the reference for the activity cutoffactiveProfiles- profiles surviving this reorganize round, scanned in iteration order until the minimum is reachedbuildSuccess- the build success ratio in [0.0, 1.0], also used to size the activity window during normal operation- Returns:
- how many profiles were added to the high-capacity tier, 0 if it already met its minimum
purgeUnusableFromTiers
private void purgeUnusableFromTiers(long now, double buildSuccess, int profileCount) Evicts peers that stopped qualifying since the tiers were last rebuilt.This is a within-window catch, not the mechanism that turns the tiers over. The rebuild at step 4 clears both tiers and repopulates them through
lockedPromoteProfileToTiers(PeerProfile, double), which applies acceptance, congestion, loss-probation and excessive-failure gates to every peer it admits. A peer that is already disqualified therefore never survives the rebuild, and the only thing left for this pass to remove is a peer that qualified at rebuild time and stopped qualifying during the reorg window that followed.The consequence for anyone reading the stats: with the rebuild doing the filtering, every reason counts near zero in steady state, and that is the expected reading rather than evidence that a gate is inert. An all-zero
peer.tierEvicted.*series over many cycles means "nothing degraded mid-window", not "eviction is disabled". The gates this pass duplicates are still load-bearing, but they live in the promotion path.Two reasons have no promotion-path counterpart and so are this pass's own:
ProfileOrganizer.PurgeReason.UNREACHABLE, which tests a momentary transport flag that promotion never reads, andProfileOrganizer.PurgeReason.BANNED, which promotion covers only throughpassesBasicGates(Hash). Those two are the ones to watch when diagnosing.Must be called with the write lock held.
- Parameters:
now- current time in msbuildSuccess- the build success ratio, which relaxes the acceptance threshold under stressprofileCount- this cycle's profile count, which sets the soft-purge threshold
publishPurgeReasonStats
private void publishPurgeReasonStats(Map<ProfileOrganizer.PurgeReason, Integer> reasons, boolean fastSoft, boolean highCapSoft, int threshold) Publish per-reason eviction counts for this reorganize cycle.Registered at the point of use, as with the other tier stats: StatManager drops a name it has never been told about, so a name registered only in the constructor would publish nothing at all. All five are registered every cycle whether or not that reason fired, so a zero can be told apart from a stat that is not reporting.
- Parameters:
reasons- counts accumulated by both tier passes, keyed by reasonfastSoft- whether the fast tier was above the soft-purge thresholdhighCapSoft- whether the high-capacity tier was above itthreshold- the threshold in force, so a sudden stop is explicable
count
private static int count(Map<ProfileOrganizer.PurgeReason, Integer> reasons, ProfileOrganizer.PurgeReason reason) Count of one eviction reason in this cycle's tally.- Parameters:
reasons- this cycle's eviction counts, keyed by purge reasonreason- the reason whose count is wanted- Returns:
- the count, zero if that reason never fired
softPurgeThreshold
static int softPurgeThreshold(int knownProfiles) The smallest tier size at which peers may be evicted for a soft reason.Scaled to the number of known profiles rather than fixed, because a fixed count means different things at different network sizes: demanding 500 peers of a 5,000-profile network is a real constraint, while demanding the same of an 800-profile network is simply unreachable, which silently disables the eviction entirely and lets the tier decay one-way.
One tenth of the known profiles, capped at
MAX_SOFT_PURGE_FLOOR: the fraction keeps the requirement proportionate on small networks, and the cap stops it becoming so large on a big one that no tier ever qualifies. Below roughlyMAX_SOFT_PURGE_FLOOR * 10profiles the cap is not reached, so a small router is held to the proportionate bar.Pure decision — no context access, safe for unit tests.
- Parameters:
knownProfiles- the number of profiles the router knows about- Returns:
- the minimum tier size at which soft eviction applies; no size qualifies when the population is unknown
purgeReasonFor
static ProfileOrganizer.PurgeReason purgeReasonFor(boolean banned, boolean unreachable, boolean tierCapable, boolean softEvictionAllowed, boolean hasRecentFailures, boolean inLossProbation, boolean lowAcceptance) Why a peer listed in a tier should be evicted, or null to keep it.Extracted from
purgeUnusableFromMap(Map, String, long, double, boolean, Map)so the precedence and the gating are testable without a router context. The order is a precedence, not a set of independent checks: a banned peer is reported as banned even if it is also unreachable, because that is the most actionable fact about it and the one an operator greps for.The three categorical reasons apply at any tier size. The two soft ones require
softEvictionAllowed, which the caller derives from the population-scaled threshold, so a starved tier keeps peers that are merely underperforming while still losing the ones that must not be there at all.Pure decision — no context access, safe for unit tests.
- Parameters:
banned- the peer is banlistedunreachable- the transport reports the peer unreachabletierCapable- the peer still meets its tier's bandwidth classsoftEvictionAllowed- whether the tier is above the soft-purge thresholdhasRecentFailures- the peer has sustained recent build/test failuresinLossProbation- the peer is inside its loss-probation windowlowAcceptance- the peer's tunnel acceptance is below the bar- Returns:
- the reason to evict for, or null to leave the peer in place
purgeUnusableFromMap
private void purgeUnusableFromMap(Map<Hash, PeerProfile> tier, String tierName, long now, double buildSuccess, boolean allowSoftEviction, Map<ProfileOrganizer.PurgeReason, Integer> reasons) Evicts every peer in one tier map thatpurgeReasonFor(boolean, boolean, boolean, boolean, boolean, boolean, boolean)rejects.Acceptance is an eviction criterion for both tiers, even where high-capacity admission ignores it. A peer that declines too many of our build requests is wasted work whichever tier lists it, and leaving such a peer listed in the fast tier in particular means it keeps being offered as a first hop. Admission is the permissive side of that trade; this is what stops the permissiveness from accumulating poor accepters.
Only
PurgeReason.BANNEDandPurgeReason.UNREACHABLEcan be first to fire here for a peer the rebuild had already admitted; the other three are re-checks of gates the rebuild applied. SeepurgeUnusableFromTiers(long, double, int)for why that makes the steady-state counts near zero.- Parameters:
tier- the tier map to purge, mutated in placetierName- "fast" or "high-cap" for the log messagenow- current time in msbuildSuccess- the build success ratio in [0.0, 1.0], which relaxes the acceptance threshold under stress so the tiers cannot be emptied by a network-wide eventallowSoftEviction- whether this tier is abovesoftPurgeThreshold(int), gating the soft reasonsreasons- tally shared with the other tier's pass, accumulated in place
needsTierRestore
static boolean needsTierRestore(int currentSize, int oldSize) Whether a shrunken tier needs restoration: below half its old size and the old size was substantial. Prevents starvation when thresholds shift unfavorably.Pure decision — no context access, safe for unit tests.
- Parameters:
currentSize- current tier sizeoldSize- pre-reorganize tier size- Returns:
- whether the tier should be restored
isRestorableTierPeer
private boolean isRestorableTierPeer(Hash peer, PeerProfile profile, Map<Hash, PeerProfile> tier, double buildSuccess, long now, boolean highCap) Whether a preserved pre-reorganize tier entry may be re-added: not already present in the tier, selectable, no recent tunnel failures, not in loss probation (fast tier) or below the loss demotion threshold (high-cap tier), and acceptable tunnel acceptance. Evaluation order matches the original restore loops.- Parameters:
peer- the peer hashprofile- the preserved profiletier- the tier map being restored intobuildSuccess- the build success ratio in [0.0, 1.0]now- current time in mshighCap- whether restoring the high-cap tier (loss-demotion gate instead of loss probation)- Returns:
- whether the peer may be restored
restorePreservedPeers
private void restorePreservedPeers(long now, Set<Hash> oldFastPeers, Set<Hash> oldHighCapPeers, double buildSuccess) Re-adds preserved pre-reorganize tier entries when the rebuild shrank the tiers too much. Must be called with the write lock held.- Parameters:
now- current time in ms, the reference for the loss and acceptance checksoldFastPeers- the fast tier's peer hashes as they were before the rebuildoldHighCapPeers- the high-capacity tier's hashes as they were before the rebuildbuildSuccess- the build success ratio in [0.0, 1.0], used by the selectability and acceptance gates
countActiveProfiles
private int countActiveProfiles(long now) Counts profiles with recent activity, over the same set ascountActivePeersInLastHour()so the published gauge and the on-demand scan agree. Called during the reorganise, which already holds the write lock.- Parameters:
now- current time in ms; activity is anything at or after now minusACTIVE_WINDOW_MS- Returns:
- how many not-failing profiles are active inside the window
countQualityPeers
private int countQualityPeers(long now, double buildSuccess) Counts quality peers (fast/high-cap with good acceptance plus recent activity).- Parameters:
now- current time in ms; the activity cutoff is 24 hours before itbuildSuccess- the build success ratio in [0.0, 1.0], which scales the acceptance requirement- Returns:
- how many distinct tier peers pass the quality test, each peer counted once
purgeStaleProfileFiles
public void purgeStaleProfileFiles()Remove profile files that are no longer worth keeping: those whose peer is genuinely stale at any size, and those beyond the disk retention floor.See
ProfilePersistenceHelper.purgeExcessProfiles(Set, int)for the two rules and why neither lets a recently active peer be deleted for its bandwidth tier.calculateCapacityThresholdFromSet
Helper to calculate capacity threshold from a pre-filtered set of active profiles. Avoids re-iterating the full set multiple times.- Parameters:
activeProfiles- the pre-filtered profiles whose capacity values are sampledtotalPeers- the size of that set, used as the mean divisor and as the highest index read; 0 skips the sampling entirely- Returns:
- the capacity threshold,
CapacityCalculator.GROWTH_FACTORwhen there are no peers to sample
countExceedingMean
static int countExceedingMean(double[] capacities, double meanCapacity) How many of the given capacities are strictly above the mean.Scanned rather than derived from an index into the sorted array: the mean is a repeated value whenever capacities cluster, and
Arrays.binarySearchreturns only some index among equal entries, so an index-derived count silently includes peers whose capacity merely equals the mean and can flip the>= minHighCapbranch. The scan is over a primitive array the sort above already walked.- Parameters:
capacities- the capacities; the caller holds them sorted, but the count does not depend on their ordermeanCapacity- the mean to compare against- Returns:
- the number of entries greater than the mean, never negative
enforceProfileCap
private void enforceProfileCap()Enforces the global profile cap by evicting the least valuable peers when over capacity.Eviction priority (from most to least likely to be kept):
- Peers in _fastPeers (fast + reliable)
- Peers in _highCapacityPeers
- Peers with recent activity (last 48 hours)
- Peers with higher capacity value
This method assumes the write lock is held.
clampMaxProfiles
static int clampMaxProfiles(int maxProfiles) Clamps the configured max-profiles value into [100, ABSOLUTE_MAX_PROFILES].Pure decision — no context access, safe for unit tests.
- Parameters:
maxProfiles- the configured or default value- Returns:
- the clamped value
isEvictable
static boolean isEvictable(PeerProfile profile, Map<Hash, PeerProfile> fastPeers, Map<Hash, PeerProfile> highCapPeers, long activeThreshold, int fastPeerLimit, int highCapacityLimit) Whether a profile is eligible for eviction under the profile cap: not protected by a fast/high-cap tier slot that still has room below its limit, and no send or heard-from activity within the active window.Pure decision. No context access, safe for unit tests.
- Parameters:
profile- the profile under considerationfastPeers- the current fast tier maphighCapPeers- the current high-cap tier mapactiveThreshold- the activity window cutoff (now - 48h)fastPeerLimit- fast tier size at or below which its members keep eviction protection; above it, fast members are evictable like any otherhighCapacityLimit- high-cap tier size at or below which its members keep eviction protection; above it, high-cap members are evictable- Returns:
- whether the profile may be evicted
isStaleAbsentPeer
Whether the peer's last activity (send, heard-from, or heard-about) is older than the absent threshold. I.e. it has been absent from the netDb for at least that long. A peer that was never contacted (all-zero timestamps) counts as stale.Pure decision — no context access, safe for unit tests.
- Parameters:
profile- profile whose newest send, heard-from or heard-about timestamp sets the agenow- current time in msabsentThreshold- stale threshold in ms- Returns:
- whether the peer is stale-absent
evictProfilesNotInNetdb
void evictProfilesNotInNetdb()Evict profiles for peers no longer in the network database. Keeps profile files on disk — they'll be reloaded if the peer reappears. Acquires the write lock.calculateCapacityThreshold
private double calculateCapacityThreshold(double meanCapacity, int numExceedingMean, int totalPeers, double thresholdAtMedian, double thresholdAtMinHighCap, double thresholdAtLowest) Interpolates the capacity threshold for selection.- Parameters:
meanCapacity- the mean capacity of the peersnumExceedingMean- peers above the meantotalPeers- the peers consideredthresholdAtMedian- the threshold when half are abovethresholdAtMinHighCap- the threshold at the minimum high capthresholdAtLowest- the threshold at the lowest point- Returns:
- the capacity threshold
calculateSpeedThreshold
private double calculateSpeedThreshold(Set<PeerProfile> reordered, long now, double capacityThreshold) Computes the speed threshold over the high-capacity peers.- Parameters:
reordered- profiles ordered by capacitynow- the current timecapacityThreshold- the capacity threshold in effect- Returns:
- the speed threshold
speedThresholdCutoff
static int speedThresholdCutoff(int qualifyingCount) Rank boundary for the speed threshold: the top 30% of qualifying peers, capped at 50. 0-based, so rank 0 is the fastest.The qualifying count drives the rank, never the profile count: the profile count is an order of magnitude larger and would pin the rank at the cap, asking for a peer that does not exist.
Pure decision. No context access, safe for unit tests.
- Parameters:
qualifyingCount- number of peers that passed the capacity and activity gates- Returns:
- the rank to read the boundary peer from, or 0 for an empty set
selectSpeedBoundary
static PeerProfile selectSpeedBoundary(Collection<PeerProfile> profiles, long now, double capacityThreshold) The profile whose speed becomes the speed threshold: the one at rankspeedThresholdCutoff(int)of the qualifying set, ranked fastest first. Only that one profile is ever read, and the rank is capped at 50, so a bounded top-K selection replaces a full sort of every profile.- Parameters:
profiles- candidates, not necessarily sortednow- current time in ms, for the activity testcapacityThreshold- minimum capacity to qualify- Returns:
- the boundary profile, or null if none qualifies
qualifiesForSpeedRank
private static boolean qualifiesForSpeedRank(PeerProfile profile, long now, double capacityThreshold) Whether a profile qualifies for the speed-threshold ranking: enough capacity to count and recently active. One definition, shared by the qualifying count and the bounded selection so the two passes cannot disagree about who is in the set.- Parameters:
profile- the profile to testnow- current time in ms, for the activity testcapacityThreshold- minimum capacity to qualify- Returns:
- true if the profile takes part in the speed ranking
countQualifying
How many of the given profiles qualify for the speed-threshold ranking. The rank read from the ranked set is a fraction of this count, not of the profile count, so it has to be known before the boundary is selected.- Parameters:
profiles- candidates, not necessarily sortednow- current time in ms, for the activity testcapacityThreshold- minimum capacity to qualify- Returns:
- the qualifying count, never negative
selectNthFastest
static PeerProfile selectNthFastest(Collection<PeerProfile> profiles, long now, double capacityThreshold, int k) Select thek-th fastest qualifying profile (1-based) without sorting the whole candidate set.Holds the
kfastest profiles seen so far in an unsorted bounded array plus the index of the slowest: a slower profile is discarded in O(1), one that displaces the slowest costs ak-element rescan. Worst case O(n·k), andkis at most 51 for the only caller.Ties in speed may resolve to a different peer of equal speed than a full sort would have picked; only the boundary value is read.
- Parameters:
profiles- candidates, not necessarily sortednow- current time in ms, for the activity testcapacityThreshold- minimum capacity to qualifyk- how many of the fastest to keep, 1-based- Returns:
- the k-th fastest qualifying profile, or null if fewer than k qualify
lookupRouterInfoUnvalidated
Fetch a peer's local RouterInfo without re-running the network database'svalidate().Every caller here reads raw RouterInfo content (tier, capabilities, addresses) and applies its own gate, so nothing depends on validation rejecting an entry that is present. It already ran when the entry was stored. Callers must not use this to evict: the validating lookup's failure path calls
fail(key), which is the netDb's job.Falls back to
lookupRouterInfoLocallyon a miss, needed only forDummyNetworkDatabaseFacade: it serveslookupRouterInfoLocallyfrom its own map and returns null fromlookupLocallyWithoutValidation.- Parameters:
peer- the peer hash- Returns:
- the local RouterInfo, or null if absent or not a RouterInfo
lookupRouterInfoUnvalidated
lookupRouterInfoUnvalidated(Hash)with the context passed in, so the fingerprint builder below is reachable from tests.- Parameters:
ctx- the context holding the NetDb to look the peer up inpeer- the peer hash- Returns:
- the local RouterInfo, or null if absent or not a RouterInfo
isExcludedFromProfiling
Check if a peer should be excluded from profiling. Excludes low bandwidth tiers (K, L, M, Unknown) and G cap (no tunnels).This is the single gate in front of profile creation, and it covers all three ways one can be made:
getOrCreateProfileNonblocking(Hash),addProfile(PeerProfile)(which is how stored profiles are loaded at startup), and the periodic sweep. A peer failing it therefore has no profile at all, so it costs no RAM and cannot be written to disk.The bandwidth half of this check was documented here but never performed: the body tested only the no-tunnels capability, while the tier test sat in
isLowBandwidthTierName(String)with no caller in the codebase. Peers advertising K, L, M or Unknown were profiled, tracked and persisted despite advertising that they will not usefully host a tunnel.- Parameters:
peer- the peer hash- Returns:
- true if the peer should not be profiled
isExcludedFromProfiling
Should a peer advertising these capabilities and bandwidth tier be profiled?Pure decision form of
isExcludedFromProfiling(Hash), so the rule can be pinned without a RouterInfo or a network database.- Parameters:
capabilities- the peer's advertised capabilities, never nullbandwidthTier- the peer's advertised bandwidth tier- Returns:
- true if no profile should be created for the peer
isLowBandwidthTierName
Whether a bandwidth tier name is one of the tiers excluded from profiling. Pure decision — no context access, safe for unit tests.- Parameters:
tier- the advertised bandwidth tier- Returns:
- true for K, L, M, or Unknown
isCongestedPeer
Check if a peer has a congestion capability cap (D or E).- Parameters:
peer- the peer whose advertised capabilities are inspected; a peer with no RouterInfo in the database is not treated as congested- Returns:
- true if the peer is moderately or severely congested
isHighBandwidthCapable
Whether the peer qualifies for the high-capacity tier based on advertised bandwidth tier and capabilities. Requires X/P/O bandwidth tier and no D (congestion), E (severe congestion), G (no tunnels), or U (firewalled/unreachable) capability flags.Bandwidth tier is a fact about the peer rather than an observation that can erode, so these peers are eligible for fast-tier fast-track regardless of measured throughput.
- Parameters:
peer- the peer to check- Returns:
- true if X/P/O tier and no D/E/G/U caps
qualifiesHighBandwidthTier
Whether a bandwidth tier and capability string together qualify the peer for the high-capacity / fast tiers: X/P/O tier and none of the D (moderate congestion), E (severe congestion), G (no tunnels) or U (unreachable) capability flags.Pure decision — no context access, so a (tier, caps) pair already in hand can be tested without a router.
- Parameters:
tier- the advertised bandwidth tiercaps- the peer's capability string- Returns:
- true if X/P/O tier and no D/E/G/U caps
isHighBandwidthTierName
Whether a bandwidth tier is one of the high-capacity tiers (O, P, X).Pure decision — no context access, safe for unit tests.
- Parameters:
tier- the advertised bandwidth tier, already stripped of HTML- Returns:
- true for O, P, or X
isFastTierCapable
Whether the peer qualifies for the fast tier based on advertised bandwidth tier and capabilities. Currently identical toisHighBandwidthCapable(Hash)— both tiers reject X/P/O with D/E/G/U caps. Kept separate so fast tier can diverge (e.g. stricter RTT gate) in the future.- Parameters:
peer- the peer to check- Returns:
- true if X/P/O tier and no D/E/G/U caps
getHighCapRTTThreshold
public double getHighCapRTTThreshold()Get the high-capacity RTT ceiling for display and diagnostics. High-cap has no RTT gate — acceptance ratio and loss gates handle quality. Returns 0 (no ceiling).- Returns:
- 0 (high-cap has no RTT ceiling)
lockedGetProfile
Returns a peer's profile with the read lock held.- Parameters:
peer- the peer hash- Returns:
- the profile, or null if unknown
getMinCandidateSample
Read the candidate-sample floor, honouring the configured override. Clamped to[16,MAX_CANDIDATE_SAMPLE]so a bad value degrades to a sane range rather than scanning the whole tier or collapsing to a single candidate.- Parameters:
ctx- the router context- Returns:
- the floor to use for
maxCandidateSample
setMinCandidateSample
public static void setMinCandidateSample(int val) Override the candidate-sample floor at runtime.- Parameters:
val- the new floor, clamped to[16,MAX_CANDIDATE_SAMPLE]
maxCandidateSample
static int maxCandidateSample(int howMany, int peerCount) Cap on how many tier peers to gate-check when selectinghowManytunnels. Scanning all ~670 fast peers per selection was a CPU hot path; a 20× sample (min 20) cut work ~10× on large tiers but, because HashMap iteration order is stable, every selection examined the same prefix of the tier — the rest of it stayed unreachable while that prefix kept passing, so tier size never became choice. With the scan start rotated, the sample is the size of one attempt's lottery: 64 gives lockedPickLowestPriority a real low-latency pick while remaining an order of magnitude cheaper than a full tier scan. A 10× sample was tried before the original 20× and hurt build success, so the 20× multiplier for larger requests is kept.- Parameters:
howMany- tunnels requested (may be 0 or negative)peerCount- size of the tier map (may be 0)- Returns:
- maximum candidates to examine, never negative
maxCandidateSample
static int maxCandidateSample(int howMany, int peerCount, int floor) Candidate-sample cap for a configurable floor. Split out frommaxCandidateSample(int, int)so the arithmetic is testable without a RouterContext.- Parameters:
howMany- peers the caller wantspeerCount- size of the tier being sampledfloor- minimum candidates to examine regardless ofhowMany- Returns:
- maximum candidates to examine, never negative
lockedSelectPeers
private void lockedSelectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches, int mask, MaskedIPSet ipSet, double buildSuccess, long rttCeiling) Selects peers while holding the lock.- Parameters:
peers- the profiles to choose fromhowMany- the number of peers to selecttoExclude- the hashes to skipmatches- the set to add the selections tomask- the network bits to matchipSet- the masked ips to matchbuildSuccess- the cached build success ratiorttCeiling- the largest acceptable round trip time
lockedSelectPeers
private void lockedSelectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches, SessionKey randomKey, ProfileOrganizer.Slice subTierMode, int mask, MaskedIPSet ipSet, double buildSuccess, long rttCeiling) Collects up tomaxCandidateSample(int, int)candidates from a tier and hands them tolockedPickLowestPriority(List, int, Set).The scan start is a random offset into the tier. Tier maps are plain HashMaps, so iteration order is stable across calls; without the offset every hop of every pool examined the same prefix, which both starved the rest of the tier and piled "too many tunnels" onto the same few peers. Peers in the tier were already vetted by isSelectable at tier-entry time, so only fast-changing gates are re-checked here (banlist, ghosts, first-hop cooldown, lifetime failures) rather than full proof-of-life, which would reject too many peers on stale RouterInfo.
- Parameters:
peers- tier to scan; must be a field map so the starved warning can name ithowMany- total peers the caller wants held in matches across the whole cascade, not this tier alonetoExclude- live exclusion set, mutated with peers rejected by a hard gatematches- output set, already holding any previously selected peersrandomKey- sub-tier slice key, or null to skip slicingsubTierMode- sub-tier mask, or null to skip slicingmask- IP /n diversity restriction, 0 to disableipSet- mutable subnet set, null when mask is 0buildSuccess- build success ratio fetched once by the callerrttCeiling- soft ceiling; peers above it are skipped, never excluded
warnTierStarved
private void warnTierStarved(Map<Hash, PeerProfile> peers, int examined, int skipExcluded, int skipGated, int skipSliced, int skipIp, int skipRtt) Rate-limited warning naming the gate that emptied a tier scan. A tier reporting zero candidates is invisible upstream. The caller just falls through to the next tier — so without this a 1000-peer tier rejected by a single gate looks identical to genuine scarcity.- Parameters:
peers- the tier that produced no candidatesexamined- entries walked before giving upskipExcluded- rejected by the live exclusion setskipGated- rejected by a hard gate (ban, ghost, first-hop, lifetime failures)skipSliced- rejected by the sub-tier sliceskipIp- rejected by IP diversityskipRtt- above the adaptive RTT ceiling
profileToPromote
Which profile a promotion pass should act on: the live one when the map has it, the caller's otherwise.The TreeSet's copy may be stale, so the live profile decides. Making the choice a named decision is what lets the promotion and its log reach the same object — they used to decide separately, so the log printed the copy's capacity while the promotion ran on the live one.
- Parameters:
live- the profile from_notFailingPeers, may be nullfallback- the profile the caller already holds- Returns:
- the profile to act on, never null
tierScanStarved
static boolean tierScanStarved(int candidates, int howMany, int examined, int skipExcluded, int skipGated, int skipSliced, int skipIp, int skipRtt) Whether a tier scan starved badly enough to be worth reporting.All five skip reasons participate. The guard used to sum only four of the five it was about to pass to the logger, so a tier emptied purely by sub-tier slicing produced no warning. The one starvation cause with a dedicated counter was the one that stayed silent. Extracting the sum is what makes "every reason counts" checkable.
- Parameters:
candidates- how many candidates the scan producedhowMany- peers the caller asked forexamined- entries walked before giving upskipExcluded- rejected by the live exclusion setskipGated- rejected by a hard gateskipSliced- rejected by the sub-tier sliceskipIp- rejected by IP diversityskipRtt- above the adaptive RTT ceiling- Returns:
- true when the scan found nothing and something specific caused it
lockedPickLowestPriority
private void lockedPickLowestPriority(List<Map.Entry<Hash, PeerProfile>> candidates, int howMany, Set<Hash> matches) Selects the lowest-priority candidates, penalizing moderately-lossy peers. Must be called with the read lock held.Both lossy thresholds are resolved once for the whole list rather than per candidate. The lottery draw uses
ThreadLocalRandomrather than_context.random(): it only breaks ties among peers that have already passed every gate, so it carries no cryptographic requirement, while_context.random().nextFloat()is a monitored read on the process-wide Fortuna instance and serialized every concurrent selection.- Parameters:
candidates- the entries to choose from, each a peer hash with its profilehowMany- maximum number of peers to pick; values below one pick nothingmatches- output set receiving the chosen peer hashes
pickLowestPriority
public static int pickLowestPriority(List<Hash> candidates, float[] priority, int howMany, Set<Hash> matches) Choose up tohowManycandidates in ascending priority order.This loop is the reason the peer-selection callers may size a fixed-capacity
ArraySetto exactly the number of peers they asked for:net.i2p.router.tunnel.pool.ClientPeerSelectorbuilds four fallback sets asnew ArraySet<>(needed)and passesneededashowMany.net.i2p.router.tunnel.pool.ExploratoryPeerSelectorbuildsnew ArraySet<>(1)and asks for one peer.
ArraySetthrowsSetFullExceptiononce written past its capacity, so if this loop ever wrote more thanhowManyentries those callers would throw at runtime rather than degrade. The bounds < howMany && s < szis therefore load-bearing, and it is pinned byPeerSelectionBoundTest.picksNeverExceedHowManyand its neighbours.Distinctness falls out of the swap rather than from a membership test: the chosen entry is moved to index
sand the next inner scan starts ats + 1, so an entry already picked can never be revisited.candidatesandpriorityare reordered in place to achieve that, which is why both are taken as mutable.- Parameters:
candidates- peer hashes, reordered in placepriority- per-candidate score, parallel tocandidates, reordered in placehowMany- maximum entries to choose; values below one choose nothingmatches- receives the chosen hashes- Returns:
- how many entries were actually added to
matches
lockedSelectActive
private void lockedSelectActive(List<Hash> connected, int howMany, Set<Hash> toExclude, Set<Hash> matches, int mask, MaskedIPSet ipSet, double buildSuccess) Scan up tomaxCandidateSample(int, int)established peers for the active tier, inRandomIteratororder so the peers examined differ from call to call. Capped for the same reason aslockedSelectPeers(Map, int, Set, Set, int, MaskedIPSet, double, long): one attempt's worth of lottery, not a walk of the whole list.- Parameters:
connected- the connected peers to draw from, in random-iterator orderhowMany- peers to add; the scan stops once matches reaches this counttoExclude- peers to skip, or null; a peer failing the selectability gate is added here so later passes skip it toomatches- output set receiving the chosen hashes; its size counts against howManymask- leading address bytes to match for /n subnet diversity, 0 to disableipSet- fingerprints claimed so far, consulted only when mask is non-zerobuildSuccess- the build success ratio in [0.0, 1.0], fetched once per scan
notRestricted
Subnet-diversity gate for the running router.- Parameters:
peer- the peer whose addresses, ports and family are fingerprintedipSet- the fingerprints claimed by the peers evaluated so farmask- leading address bytes to match, as taken byMaskedIPSet.maskedIPKey(byte[], int)- Returns:
- true if the peer passed the gate and may be selected
notRestricted
Subnet-diversity gate: does this peer pass the masked-subnet, port and family check, and if it does, claim its fingerprints so that no later peer sharing any one of them passes as well. Every selection path taking amaskand anipSetcalls this, and all of them read true as "allowed".Before the peer-selection refactor this built a throwaway
MaskedIPSet, testedcontainsAnyagainst the accumulator, then added the keys. Inlining the build folded the collision test into the adds, which inverted the result and let a peer collide with itself; both are corrected here while keeping the single pass over the RouterInfo.- Parameters:
ctx- supplies the comm system IP and the NetDb lookuppeer- the peer whose addresses, ports and family are fingerprintedipSet- the fingerprints claimed by the peers evaluated so farmask- leading address bytes to match, as taken byMaskedIPSet.maskedIPKey(byte[], int)- Returns:
- true if the peer passed the gate and may be selected
fingerprints
One peer's fingerprints: the masked IP the comm system recorded, the masked IP and port of every published address, and the family option.Collected in their own set rather than added straight to the shared accumulator, because a peer normally publishes two transports on one address and its own keys overlap by design — a peer must not collide with itself.
- Parameters:
ctx- supplies the comm system IP and the NetDb lookuppeer- the peer to fingerprintmask- leading address bytes to match, 1-4- Returns:
- the peer's fingerprint keys, empty if nothing is known about it
tryClaimKeys
The diversity gate itself: report whether any of this peer's fingerprints is already claimed, and claim them all either way. A peer passes only when none of its keys was claimed, so one peer per masked subnet, per port and per family gets through.Claiming a rejected peer's keys is load-bearing: it keeps the outcome the same whichever order the candidates are evaluated in, so a peer cannot pass the gate merely because selection happened to look at it first.
- Parameters:
claimed- the fingerprints claimed by the peers evaluated so farcandidateKeys- one peer's fingerprints, already deduplicated- Returns:
- true if none of the candidate keys was claimed yet, i.e. the peer passed; an empty candidate list passes vacuously
getSubTier
Sub-tier slice index for a peer, keyed off the session key so the same key always yields the same slice.- Parameters:
peer- the peer to placek0- first half of the session key the slice is keyed offk1- second half of that session key- Returns:
- the slice index, 0-3
isSelectable
Whether the given peer is eligible for selection. Fetches the build success ratio once; per-peer callers should useisSelectable(Hash, double)with a value fetched once per scan.- Parameters:
peer- hash of the peer to judge- Returns:
- true if the peer passes every selection gate
isSelectable
Whether the given peer is eligible for selection.- Parameters:
peer- hash of the peer to judgebuildSuccess- the build success ratio, fetched once per scan- Returns:
- true if the peer passes every selection gate
passesBasicGates
Returns false when the peer is us, banlisted, or recently failed as first hop.The self check is first because this is the chokepoint every selection path funnels through, including
selectRemainderFromAllPeers(int, Set, Set, double)viaisSelectable(Hash, double). The local RouterInfo is present in our own netdb, so we are a legitimate entry in every candidate pool.Note that we are supposed to be in the tunnel we build, at one end:
ClientPeerSelector.finalizeSelectioninserts us directly, andExploratoryPeerSelectoradds us to its exclude set. Neither goes through this method, so excluding us here costs nothing. What this guard prevents is being drawn as an extra hop — a mid-hop in a tunnel we are already an end of, which can only produce a build that loops back on itself. The tier drawers (lockedSelectPeers,lockedSelectActive) each excluded self locally; the remainder path used when tiers cannot supply enough peers did not, so the two protections could disagree. Checking here rather than in each selection loop means no present or future path can reintroduce the gap.Scope, stated precisely because it was previously overstated: this guard has not been shown to fix any observed build failure. A router appearing in an expiring-build log line is expected — it is the gateway — so that log is not evidence of self-selection. Whether we were being drawn at a mid-hop has not been established.
- Parameters:
peer- the candidate's hash, tested against self, the banlist, the ghost-peer set and the first-hop failure list- Returns:
- true if the peer is not us, not banlisted, not a ghost, and not recently failing as a first hop
flushSelectionCounters
private void flushSelectionCounters()Record the banned-candidate rejections accumulated since the last flush as a singletunnel.peerBannedAtSelectionsample. The count stays exact: the atomic get-and-reset loses nothing, it only attributes a rejection to whichever scan drains it first. A count of zero writes nothing.isExcessiveLifetimeFailure
Whether the given profile carries excessive cumulative tunnel failures. Dual gate: hard exclusion atMAX_LIFETIME_TUNNEL_FAILURESfailures or when the lifetime failure ratio exceedsMAX_LIFETIME_FAILURE_RATIO. A null profile or null history is not a failure.The blame system (tunnelFailed()) only increments statistics — it never bans. Without this check, peers with 200+ failures keep getting selected because the ghost peer system clears on any success and the first-hop cooldown is only 5 minutes. Pure decision — no context access, safe for unit tests.
- Parameters:
prof- the profile, may be null- Returns:
- true when the peer should be excluded from selection
getExcessiveFailurePenalty
Selection penalty multiplier for peers between the soft and hard failure thresholds. Peers withSOFT_FAILURE_PENALTY_THRESHOLDtoMAX_LIFETIME_TUNNEL_FAILURESlifetime failures are not excluded but receive a penalty that lowers their selection priority. Returns 1.0 (no penalty) for peers below the soft threshold or above the hard threshold (they'd be excluded anyway). Pure decision — no side effects.- Parameters:
peer- the peer hashprof- the peer's profile, or null if it has none- Returns:
- a penalty multiplier in (1.0, 2.0] — higher means more penalized
hasValidRouterInfo
private boolean hasValidRouterInfo(Hash peer, RouterInfo info, double buildSuccess, PeerProfile prof) Checks the RouterInfo for hidden flag, stale proof of life, usable transport address, bandwidth tier, and tunnel exclusion.- Parameters:
peer- the peer's hash, used for the established-connection checkinfo- the peer's RouterInfo, already looked up and not nullbuildSuccess- the build success ratio in [0.0, 1.0], which scales the tunnel-exclusion checkprof- the peer's profile, or null if it was never seen; consulted only for proof of life when the RouterInfo is stale- Returns:
- true if the RouterInfo passes every age, liveness and usability check
isExcludedBuildTier
Whether a RouterInfo's advertised bandwidth tier is one that cannot host a tunnel: L, M, or N.The raw string is compared directly rather than through
DataHelper.stripHTML, which is not a weakening: stripHTML only substitutes spaces for< > " ', sostripHTML(t).equals("L")holds exactly whentis the one-character string"L".Pure decision. No context access, safe for unit tests.
- Parameters:
rawTier- the tier string as advertised, possibly HTML-obfuscated- Returns:
- whether the tier excludes the peer from tunnel hosting
getMaxRouterInfoAgeMs
private long getMaxRouterInfoAgeMs()Maximum RouterInfo age in ms, cached alongsidegetTunnelBuildSuccess()on the sameBUILD_SUCCESS_CACHE_MScadence: it is a constant read per candidate, and 15s of staleness on a proof-of-life bar is immaterial.- Returns:
- the max RouterInfo age in ms
hasUsableTransportAddress
Returns true when the RouterInfo has a reachable NTCP/NTCP2/SSU/SSU2 address.- Parameters:
info- the peer's RouterInfo whose addresses are scanned- Returns:
- true if at least one NTCP, NTCP2, SSU or SSU2 address carries a non-null IP and a valid port
hasRecentProofOfLife
True if the peer has shown proof of life withinPROOF_OF_LIFE_WINDOW_MS: a successful send, heard-from, or heard-about within the last hour. A null profile (never seen) is not evidence of life. Used to trust a RouterInfo that is older than the maximum age — the RouterInfo alone is not enough, the peer must have been active recently.Pure decision — no context access, safe for unit tests.
- Parameters:
profile- the peer profile, null if nonenow- current time in ms- Returns:
- whether the peer has recent proof of life
lockedPlaceProfile
Puts a profile into the pool maps with the write lock held.- Parameters:
profile- the profile to placebuildSuccess- the current tunnel build success ratio
lockedPlaceProfile
private void lockedPlaceProfile(PeerProfile profile, double buildSuccess, int minHighCap, int maxFast, int maxHighCap) Variant taking the tier thresholds, which the caller has already fetched for the whole scan.- Parameters:
profile- the profile to placebuildSuccess- cached tunnel build success ratiominHighCap- minimum high-capacity peers for this scanmaxFast- maximum fast peers for this scanmaxHighCap- maximum high-capacity peers for this scan
lockedPromoteProfileToTiers
Evaluate a profile for promotion to fast/high-cap tiers without touching the notFailing structures. Safe to call between reorganize cycles — does not add duplicates to _notFailingPeersList. Must be called with write lock held.- Parameters:
profile- the profile to evaluate for tier placementbuildSuccess- cached tunnel build success ratio, read once by the caller
lockedPromoteProfileToTiers
private boolean lockedPromoteProfileToTiers(PeerProfile profile, double buildSuccess, int minHighCap, int maxFast, int maxHighCap) Evaluate a profile for promotion to the tiers, reporting whether the only thing that stopped it was an unusable transport address.The return value exists for
promoteToFillTiers(), whose underfill log line wants to distinguish a candidate held back by policy from a candidate that was never examined. That line used to carry a local counter which was declared and then never incremented, so it always reported zero holds and read as "the gate never fires" for an entire diagnostic run.- Parameters:
profile- the profile to evaluatebuildSuccess- the windowed build success ratiominHighCap- minimum high-capacity peersmaxFast- maximum fast peersmaxHighCap- maximum high-capacity peers- Returns:
- true only if promotion was held because the RouterInfo has no usable transport address; false for every other outcome
hasNoUsableTransport
Does this peer's RouterInfo carry no address we could still send to?Reads the netdb unvalidated, matching what selection and the build requestor do, so the gate cannot reject a peer on stricter grounds than the path that would later use it. A missing entry counts as "no usable address": we cannot prove otherwise, and promoting an unverifiable peer is precisely the case this gate exists to stop.
- Parameters:
peer- hash of the candidate under consideration- Returns:
- true if the peer should be held out of the selection tiers
promotionNeedsRouterInfoRefresh
Whether this peer's stored RouterInfo is stale enough that promoting on it risks a build failure.Reads the same staleness notion
hasValidRouterInfo(Hash, RouterInfo, double, PeerProfile)uses — older than the threshold and no proof of life — against the shorterPROP_ROUTERINFO_REFRESH_AGE_MS. Proof of life exempts working peers, which is what keeps the affected population small: a peer we have talked to in the last hour is judged on what it just told us, not on how old its last publication is.- Parameters:
profile- the profile being promotednow- current time in ms- Returns:
- whether the stored RouterInfo is too old to promote on
needsRouterInfoRefresh
static boolean needsRouterInfoRefresh(RouterInfo info, PeerProfile profile, long now, long refreshAgeMs) Whether a RouterInfo is old enough that promoting on it risks a build failure: published before the threshold and no proof of life.An absent RouterInfo is not stale, it is missing, and is not this predicate's business:
isHighBandwidthCapable(Hash)andisFastTierCapable(Hash)already refuse to promote a peer with no RouterInfo at all, so there is no promotion here to hold.Pure decision — no context access, safe for unit tests.
- Parameters:
info- the peer's stored RouterInfo, may be nullprofile- the peer profile supplying proof of life, may be nullnow- current time in msrefreshAgeMs- staleness threshold in ms- Returns:
- whether the RouterInfo is too stale to promote on
holdPromotionForRefresh
private boolean holdPromotionForRefresh(Hash peer, boolean staleRouterInfo, int tierSize, int safeFloor) Apply the hold to one tier insert: leave the peer out of the tier, or let it in.The starvation guard is the reason this is not a plain veto. A tier that is already at its configured minimum is one the fallback passes would defend anyway, so leaving a stale peer out of it costs nothing there. Below the minimum the hold would compete with those passes for the same candidates and the tier could end up short, which is the worse of the two failures: a stale promotion risks one bad build, an empty tier sends every build back to the general pool. When the guard opens, the promotion is counted so the team can see exactly how often that happens.
- Parameters:
peer- the peer being promotedstaleRouterInfo- whether the peer carries a stale RouterInfotierSize- current size of the tier being joinedsafeFloor- size at or above which this tier can leave it out- Returns:
- whether the promotion must be held back
shouldDeferPromotionOnStaleRouterInfo
static boolean shouldDeferPromotionOnStaleRouterInfo(int tierSize, int safeFloor) Whether a promotion may be held back on a stale RouterInfo without risking the tier it would join.The floor is the tier's configured minimum, which is also what
fillFastTierFallbacks(long, Set, double)andfillHighCapFallback(long, Set, double)drive the tier back up to and whatrestorePreservedPeers(long, Set, Set, double)protects at half. At or above the minimum the tier can afford to leave the peer out; below it, it cannot.Pure decision — no context access, safe for unit tests.
- Parameters:
tierSize- current size of the tier the peer would joinsafeFloor- size at or above which the tier can leave it out- Returns:
- whether the promotion should be held back
getRouterInfoRefreshAgeMs
private long getRouterInfoRefreshAgeMs()RouterInfo age past which a promotion is held back, in ms. Read once per reorganisation cycle rather than cached per candidate, because that is the only place it is consulted.Separate from
PROP_MAX_ROUTERINFO_AGE_HOURS, which is the bar a RouterInfo must clear to be selected at all. That bar is deliberately loose (2h) because a proof of life can stand in for a fresh RouterInfo; this one is a promotion-time policy with no such fallback, so it is set where the RouterInfo itself is likely to have been superseded: routers republish hourly, so anything older than one publishing interval has a good chance of describing a peer that has since changed.Holding a promotion costs the peer a tier slot and nothing else — no lookup is issued, so the only way a held peer re-enters is by being republished to, or by showing proof of life.
- Returns:
- the staleness threshold in ms; <= 0 disables the hold
recordRouterInfoRefreshStats
private void recordRouterInfoRefreshStats()Publish the promotion-deferral counters for the cycle just finished.peer.routerInfoRefreshNeededis a population, so it is written every cycle including zero. A flat zero line is the signal that nothing is being held back. The stale-promotion count is an event count and follows the existing convention of writing nothing when it is zero.recordPromotionSkip
Counts one promotion skip against its reason.The per-peer line stays at DEBUG because a single pass walks the whole capacity-ordered profile set — about 4,300 evaluations on the live router, several passes a minute. Logging each one at INFO would bury the log; the question being answered is which gate is doing the excluding, and that is a distribution rather than a list.
Not synchronised: every caller holds the write lock, and the tally is cleared by whichever scan is about to report it.
- Parameters:
reason- the reasonpromotionSkipReason(PeerProfile, Hash, double, long)returned
promotionSkipLabel
Display form of a promotion skip reason: prose words with the first letter capitalised, so the tally reads as a list of reasons rather than as code identifiers. The identifiers themselves stay greppable in the per-peer DEBUG line and inpromotionSkipReason(PeerProfile, Hash, double, long).- Parameters:
reason- the reasonpromotionSkipReason(PeerProfile, Hash, double, long)returned- Returns:
- the tally display form; the reason unchanged when unmapped
drainPromotionSkipTally
Formats the promotion skip tally and clears it.- Returns:
\n*continuation lines ofLabel: countpairs (packed to about 80 chars per line), or the empty string when nothing was skipped since the last call
skipsPromotion
Whether the peer should be skipped for tier promotion: not selectable, in a strict country, low tunnel acceptance, high-latency penalty, congested, or in loss probation. Mirrors the eligibility gates oflockedPlaceProfile(PeerProfile, double).No side effects. Safe to evaluate without holding locks.
- Parameters:
profile- the profile under considerationpeer- the peer hashbuildSuccess- the tunnel build success ratio in [0.0, 1.0]- Returns:
- whether promotion should be skipped
promotionSkipReason
WhyskipsPromotion(PeerProfile, Hash, double)would skip this profile, or null when it would not.The one place the decision is made. The caller used to re-derive two of the five gates so it could log them, which meant a peer held back by congestion, loss probation, a strict country or low tunnel acceptance printed
notPeerSelectable=false excessiveFailures=false— a line reading "the gates passed" while the peer was in fact skipped. Returning the reason from the decision itself is what stops the log from guessing, and stops the two from drifting apart again.Short-circuits on the first gate that fires, in the order the original disjunction evaluated them. The boolean result is unchanged, because the original was a pure OR over the same five conditions.
- Parameters:
profile- the profile under considerationpeer- the peer hashbuildSuccess- the tunnel build success ratio in [0.0, 1.0]now- current time in ms- Returns:
- a short reason, or null when promotion may proceed
promoteToFillTiers
private void promoteToFillTiers()After a demotion vacates slots in fast/high-cap tiers, scan the capacity-ordered peer list and promote the next best candidates to fill the openings. Must be called with write lock held.promotionTransition
static String promotionTransition(boolean wasFast, boolean wasHighCap, boolean nowFast, boolean nowHighCap) Describe a tier promotion as afrom -> totransition, naming only the tiers actually gained. Logging unchanged membership (a tier that was and stayed true) reports no event and buries the one that happened, so a scan that changed nothing returns null and the caller logs nothing. This method only adds; a lost tier is never reported here.- Parameters:
wasFast- in the fast tier before the promotionwasHighCap- in the high-capacity tier before the promotionnowFast- in the fast tier after the promotionnowHighCap- in the high-capacity tier after the promotion- Returns:
- e.g.
"highCap -> fast","none -> highCap"; null when no tier was gained
promotionEvidence
Evidence line for a promotion log: only the values that explain something. A zero bonus or raw adds nothing to read, and a live-profile match is the normal case. Only the anomaly (the promoted object was not the live profile) earns its place. Returns null when every value is default, so the log line stays one line for a routine promotion.- Parameters:
bonus- the capacity bonus appliedraw- the unclamped bonus the bonus was derived frompromotedIsLive- whether the promoted object was the live profile- Returns:
- a
"\n* "continuation line, or null when all default
describeTierFill
static String describeTierFill(String where, int fast, int fastTarget, int highCap, int highCapTarget, int examined, int alreadyTiered, int heldNoAddress, String skips) Summarise why the selection tiers could not be filled.Short because there were no candidates left to try is a discovery problem. Short because every candidate was gated out is a policy problem. Those call for different fixes, and from outside the reorg they look identical, so the breakdown is reported rather than inferred. How to read it: held-no-address above zero is a policy gate; examined zero means no candidates were found. That guide lives here, not in the emitted line. Zero counters report the absence of an event and stay out; the skip tally rides along as evidence for this same shortfall.
- Parameters:
where- the caller, for attributionfast- the fast-tier size achievedfastTarget- the fast-tier size wantedhighCap- the high-capacity-tier size achievedhighCapTarget- the high-capacity-tier size wantedexamined- candidates walked, including those rejected by a gatealreadyTiered- candidates skipped because already in both tiers, omitted when zeroheldNoAddress- candidates that cleared every gate except having no usable address, omitted when zeroskips- the drained skip tally with its own"\n* "prefix, or empty- Returns:
- the shortfall and its causes, wrapped onto continuation lines
demoteIfStale
Immediately demote a peer from fast/high-cap tiers if its RouterInfo is stale and it has no recent proof of life (fails isSelectable). Non-blocking — only acts if the peer is currently in those tiers.- Parameters:
peer- hash of the peer to demote
demoteIfHighLatency
Immediately demote a peer from fast/high-cap tiers. The caller must have already judged the peer high-latency; this only performs the tier removal. Non-blocking.- Parameters:
peer- hash of the peer to demote
demoteIfCongested
Immediately demote a peer from fast/high-cap tiers. The caller must have already judged the peer congested (cap D/E); this only performs the tier removal. Non-blocking - only acts if the peer is currently in those tiers.- Parameters:
peer- hash of the peer to demote
demoteIfNotLowLatency
Immediately demote a peer from the fast tier when a peer test shows it is no longer low-latency. Called fromPeerTestJobwhen a test result arrives that contradicts the peer's tier membership. Non-blocking — only acts if the peer is currently in the fast tier.High-capacity membership is deliberately left alone. It is earned from capacity and throughput rather than latency, so a latency judgement does not speak to it; see the comment in the body.
- Parameters:
peer- hash of the peer to demote
demotesFastTier
static boolean demotesFastTier(boolean inFast, boolean lowLatency) Whether losing the low-latency flag revokes membership of the fast tier.Fast-tier membership is the latency promotion, so losing the flag revokes it. High-capacity membership is not revoked by this signal: it is earned from advertised capacity and sustained throughput, so a peer that has stopped being low latency has not stopped being high capacity. This method used to evict from both tiers, which with a calibrated per-transport floor meant a transport whose baseline rose during congestion could strip bandwidth-qualified peers out of the tier they had been admitted to.
- Parameters:
inFast- whether the peer is currently in the fast tierlowLatency- whether the peer still holds the low-latency flag- Returns:
- true if the peer should leave the fast tier
isLowLatency
Check whether the peer's profile has low-latency status. Non-blocking — returns the profile's flag without acquiring locks.- Parameters:
peer- the peer whose profile flag is read- Returns:
- true if a profile was found and it carries the low-latency flag
demoteIfBanned
Immediately demote a peer from fast/high-cap tiers. The caller must have already confirmed the ban; this only performs the tier removal. Non-blocking - only acts if the peer is currently in those tiers.- Parameters:
peer- hash of the peer to demote
recordFirstHopRtt
The single recorder for a measured direct-link RTT.Every path that holds a transport measurement. The reorganize sampling pass, the pre-connect warm-up, a handshake — funnels through here, so the field is written one way and re-derived one way. Callers that only need the write (no stat, because they report a cycle aggregate instead) call
recordFirstHopRtt(PeerProfile, int, long); callers that hold a peer hash instead of a profile callnoteFirstHopRtt(Hash, int, long).Owns the profile write so callers holding a transport measurement do not have to know how the value is stored or aged out. The value decays on the existing Active tier window, so a first-hop latency never outlives the evidence class it belongs to.
- Parameters:
profile- the profile to record againstrtt- the measured round trip time in mswhen- timestamp of the measurement
noteFirstHopRtt
Record a measured direct-link RTT against a peer, by hash.- Parameters:
peer- the measured peerrtt- the measured round trip time in mswhen- timestamp of the measurement
demoteIfUnreachableNow
Immediately demote a peer from fast/high-cap tiers on a single unreachable first hop, skipping theDEMOTE_STRIKE_THRESHOLDstrikes and the cooldown thatdemoteIfUnreachable(Hash)requires. Non-blocking — only acts if the peer is currently in those tiers.- Parameters:
peer- hash of the peer to demote
demoteIfNoTunnel
Immediately demote a peer from fast/high-cap tiers. The caller must have already judged the peer to have built no tunnel recently. This only performs the tier removal. Non-blocking - only acts if the peer is currently in those tiers.- Parameters:
peer- hash of the peer to demote
demoteIfUnreachable
Immediately demote a peer from fast/high-cap tiers when our transport connection to it failed as a tunnel first hop (unreachable). Uses a 3-strike threshold within the decay window to avoid premature demotion. A transient failure (e.g. pre-connect race) should not remove a peer from tiers, and strikes older than the window expire so a peer that recovered is not demoted by one later failure. Does not touch the tunnel failure counter. The caller records the failure blame (e.g. profileManager().tunnelFailed()).- Parameters:
peer- hash of the peer that failed as a first hop
releaseWriteLockAndRefresh
private void releaseWriteLockAndRefresh()Release the reorganize write lock, then refresh peers held out of the tiers.The single exit point for every path that runs a tier refill under the lock. Doing the refresh here rather than at each call site is deliberate: a call site that forgot it would leave the queue to fill and never drain, and there were ten of them. It has to be here, and not inside
promoteToFillTiers(), because a refill runs with the lock held and issuing a lookup is network work that must never run under it.refreshHeldPeers
private void refreshHeldPeers()Issue RouterInfo lookups for peers held out of the tiers for want of a usable address.Called after the reorganize write lock is released. Doubly bounded - a per-drain batch and a minimum interval between drains - so neither a large scan nor a burst of demotions can turn this into a request flood.
noteMissingRouterInfo
Record that a stored profile was loaded for a peer the netdb cannot resolve.- Parameters:
peer- the peer whose RouterInfo is missing- Returns:
- true if recorded, false if the recovery list was already full
mayDrainMissingRouterInfo
boolean mayDrainMissingRouterInfo(long now) Whether enough time has passed to request the next batch of missing RouterInfos.- Parameters:
now- current time in milliseconds- Returns:
- true if the drain interval has elapsed
takeMissingRouterInfoBatch
getMissingRouterInfoPending
int getMissingRouterInfoPending()Peers still waiting for their RouterInfo to be requested.- Returns:
- the number pending
getMissingRouterInfoDropped
int getMissingRouterInfoDropped()Peers not recorded because the recovery list was full.- Returns:
- the number dropped
demoteBatch
Batch-demote peers from fast/high-cap tiers under a single write lock. Peers not currently in any tier are silently skipped. OnepromoteToFillTiers()call after all removals.- Parameters:
peers- peers to evict — may be empty (no-op, no lock)
demoteFastOnlyBatch
Batch-remove peers from fast tier only, keeping them in high-cap. Used to demote peers with test failures from fast to high cap. Does NOT callpromoteToFillTiers()— re-promotion would undo the demotion. The next full reorganize() fills any gaps.- Parameters:
peers- peers to remove from fast tier — may be empty (no-op, no lock)
nextStrikeCount
static int nextStrikeCount(int previousStrikes, long lastStrikeTime, long now) Compute the next strike count after a failure, applying time-window decay. A previous strike older than DEMOTE_STRIKE_DECAY_MS no longer counts, so a peer that recovered is not demoted by one later failure.- Parameters:
previousStrikes- strike count before this failure (0 if none)lastStrikeTime- time of the previous strike (0 if none)now- the current clock reading in milliseconds, compared against DEMOTE_STRIKE_DECAY_MS- Returns:
- the new strike count, at least 1
demoteIfHighRTT
Demote a peer from fast/high-cap tiers when its direct-link latency is poor.responseTimeMsis the round trip through an entire multi-hop tunnel, whichTestJob.noteSuccessattributes to every peer in that tunnel. That is a reasonable profile statistic but the wrong input for first-hop selection: a peer with a fast direct link is blamed for the other hops it happened to be a member of, and the result is compared against a first-hop threshold. In production that evicted a large share of the fast tier on a latency distribution taken across the wrong hops, shrinking the candidate pool and leaving slower survivors.The tier decision therefore uses
PeerProfile.getFirstHopRtt(long), measured on the direct link. The passed-in tunnel time is not consulted. A peer with no recorded first-hop RTT is left alone: unknown is not slow.- Parameters:
peer- the peer to evaluateresponseTimeMs- full-tunnel test round trip, unused by the tier decision
firstHopRttIsSlow
static boolean firstHopRttIsSlow(int firstHopRttMs, int thresholdMs) Whether a measured first-hop RTT warrants removal from the fast tiers.Pure so the policy is testable without a populated profile. An unknown measurement (negative) is never slow: we have not probed the peer, and evicting on absent evidence would drain the tier of exactly the peers we have not looked at yet.
- Parameters:
firstHopRttMs- direct-link RTT in ms, negative when unknownthresholdMs- the latency ceiling in ms- Returns:
- true if the peer should be evicted from the fast/high-cap tiers
demoteIfSlowFirstHop
Remove a peer from the fast/high-cap tiers if its measured direct-link RTT reaches the low-latency threshold.The threshold is unchanged at
router.peerTestTimeout * 2; only the measurement changed, from a whole tunnel to one hop.- Parameters:
peer- the peer to evaluatefirstHopRttMs- direct-link RTT in ms, negative when unknown
excludedOnAcceptance
static boolean excludedOnAcceptance(long agreed, long rejected, int minRequests, boolean highStress) Whether a peer's tunnel acceptance is bad enough to exclude it, given its counts and the sample floor the tier in question applies.Pure decision. No context access, safe for unit tests. The tiers differ in the floor they pass, and that difference is the whole point: high capacity is the reservoir the fast tier seeds from and cannot afford to judge a peer on a handful of requests, while the fast tier is what build selection draws from and can. Both bars fall under stress, because a peer rejecting our requests during network-wide congestion has not demonstrated anything about itself.
- Parameters:
agreed- lifetime requests the peer acceptedrejected- lifetime requests the peer declinedminRequests- samples required before acceptance may exclude at allhighStress- whether build success is belowATTACK_THRESHOLD- Returns:
- true if the peer should be excluded on acceptance
isLowTunnelAcceptance
private boolean isLowTunnelAcceptance(PeerProfile profile, double buildSuccess, long now, int minRequestsForJudgement) Check if peer has low tunnel acceptance ratio, decided from its lifetime accept/reject counts viaexcludedOnAcceptance(long, long, int, boolean), then refined by the low-sample or mature path. Peers with no tunnel test history are judged by recent activity instead: an alive peer is allowed into the tiers to accumulate data, a stale one is excluded. The mature path also honours a recent bandwidth-rejection cooldown.Takes the build success ratio as a parameter — it is fetched once per selection and passed down;
getTunnelBuildSuccess()performs 6 stat lookups and must never run per candidate peer.- Parameters:
profile- the peer profilebuildSuccess- the tunnel build success ratio in [0.0, 1.0]now- current time in msminRequestsForJudgement- how many lifetime requests the peer needs before its acceptance ratio is allowed to exclude it at all- Returns:
- whether low tunnel acceptance (true = exclude from fast/high-cap tiers)
isLowAcceptanceLowSample
Small-sample decision: belowMIN_TUNNEL_REQUESTSlifetime requests, apply the loose startup thresholds.- Parameters:
profile- the peer profileratio- the acceptance ratio in [0.0, 1.0]highStress- whether build success is below the attack threshold- Returns:
- whether low tunnel acceptance
isLowAcceptanceMature
private boolean isLowAcceptanceMature(PeerProfile profile, TunnelHistory th, double ratio, long agreed, long rejected, boolean highStress) Mature decision: effective minimum ratio, with a cooldown for recent bandwidth rejections.- Parameters:
profile- the peer profileth- the tunnel history (non-null)ratio- the acceptance ratio in [0.0, 1.0]agreed- lifetime accepted requestsrejected- lifetime rejected requestshighStress- whether build success is below the attack threshold- Returns:
- whether low tunnel acceptance
isQualityPeer
Check if peer is a quality tunnel candidate: good acceptance ratio, selectable, and recently active. Used by tuner to adjust tier limits based on viable peers, not just raw counts.- Parameters:
profile- the tier peer's profile to judgerecentCutoff- the earliest successful send that still counts as recent activitybuildSuccess- the build success ratio in [0.0, 1.0], passed to the selectability gate- Returns:
- true if the peer is selectable, has sent successfully since recentCutoff, and clears the acceptance-ratio floor once it has MIN_TUNNEL_REQUESTS of tunnel history
hasRecentTunnelFailures
Check if a peer has had recent tunnel failures (failed tests or builds). Used to gate fast/high-cap tier admission when tier counts are healthy. Requires more than 15 failures in the last hour AND a failure ratio above 30% of agreed + rejected + failed — peers with 1-15 transient failures during a network blip stay in tiers rather than being purged. This prevents tier collapse during brief network-wide events where many peers experience isolated failures.- Parameters:
profile- the peer's profile; a peer with no tunnel history, or no failure rate for the last hour, has nothing to report- Returns:
- true if the peer has sustained recent tunnel failures
hasHighLoss
True if the peer's decayed loss score is at or abovePROP_LOSSY_THRESHOLD. The score decays continuously by 50% every 15 minutes since the last transport report, so a transient congestion episode clears the gate in tens of minutes while a peer that keeps reporting loss stays gated. Each report re-inflates the score. The decay, not a freshness window, provides the slow forgiveness.- Parameters:
profile- profile carrying the decayed loss score to test against the thresholdnow- current time in ms- Returns:
- true if the decayed score is above zero and at or above the lossy threshold
inLossProbation
True if the peer is in loss probation: it was demoted from the fast/ high-cap tiers for packet loss and has not yet produced fresh clean evidence of recovery (seeshouldReadmitLossy(PeerProfile, long)). Also covers peers whose decayed score is at or above the demotion threshold even if the demotion flag was never set, e.g. a profile loaded from disk.- Parameters:
profile- profile whose loss demotion flag and loss score are checkednow- current time in ms- Returns:
- true if the loss score is at the demotion threshold, or the peer is flagged lossy and has not yet shown fresh clean recovery
shouldReadmitLossy
True if a demoted peer may be re-admitted to the tiers: at leastLOSS_READMIT_MIN_AGEhas passed since the demotion AND the transport has reported a loss ratio below the demotion threshold withinPROP_LOSSY_WINDOW. A stale or absent ratio is not evidence of recovery. This is what breaks the avoid-then-forget loop, because a peer that recovered organically accumulates new packets (netDb traffic continues while it is demoted) and reports a fresh clean ratio, while a peer that is still lossy never does.Fast-track: peers with a fresh clean loss report (loss ratio below 5% within the lossy freshness window, default 10 min) are readmitted immediately regardless of the minimum age, accelerating tier recovery after brief congestion episodes. While either tier is under target the fast-track widens to any fresh below-threshold report.
- Parameters:
profile- profile holding the demotion timestamp and the last reported loss rationow- current time in ms- Returns:
- true if the peer may be re-admitted
lossyReadmitAllowed
static boolean lossyReadmitAllowed(float lossRatio, long ratioLastUpdate, long lossySince, long now, long freshWindowMs, long minAgeMs, float threshold, float fastTrackRatio, boolean tiersShort) Pure decision behindshouldReadmitLossy(PeerProfile, long), pinned by tests. A peer never demoted is always allowed. Otherwise the transport must have reported within the freshness window a ratio below the demotion threshold; that fresh-clean evidence admits immediately when the ratio is near-spotless (fastTrackRatio) or when the tiers are short (tiersShort) — a short tier cannot afford to hold a peer out for a clean-but-not-spotless measurement — and otherwise only after the minimum age has passed. A stale or absent report is never evidence of recovery.- Parameters:
lossRatio- last loss ratio reported by the transport, 0.0 = healthyratioLastUpdate- when that ratio was reported, in mslossySince- when the peer was demoted for loss, 0 if nevernow- current time in msfreshWindowMs- how old the last report may be and still count as freshminAgeMs- minimum time since demotion before a not-spotless report admitsthreshold- the lossy demotion threshold the ratio must fall belowfastTrackRatio- ratio below which the minimum age is skippedtiersShort- whether either tier is under target, widening the fast track- Returns:
- true if the peer may be re-admitted
clearLossIfReadmitted
Clear the loss demotion flag once a peer has been re-admitted, so the cleared state is persisted and re-admission is not re-evaluated on every reorganize. Must be called with the write lock held.- Parameters:
profile- profile whose lossy flag is cleared, so re-admission is not re-checked every cycle
demoteIfLossy
Instantly remove a peer from the fast/high-capacity tiers when its decayed loss score exceeds the threshold, and mark it for loss probation. Called from the transport path viaProfileManagerImplso a lossy peer is demoted as soon as its retransmit ratio crosses the bar, instead of waiting for the next reorganize cycle. Non-blocking: if a reorganize currently holds the write lock, the reorganize purge catches the peer instead.- Parameters:
peer- hash of the peer to demote and place into loss probation
getModerateLossyThreshold
The moderate de-prioritization threshold in effect, read straight from config (there is no Tuner override, unlikegetLossyThreshold(RouterContext)). Separate so a caller walking a whole candidate list can resolve both thresholds once instead of paying two config lookups per candidate.- Parameters:
ctx- the router context- Returns:
- the loss score at or above which a peer is de-prioritized at selection
isModeratelyLossy
static boolean isModeratelyLossy(float score, float moderateThreshold, float demoteThreshold) Whether a decayed loss score falls in the moderate band: at or above the moderate threshold but below the demotion threshold. Such peers remain eligible for the tiers. They never crossed the hard bar — but are de-prioritized at selection time (see LOSSY_SELECTION_PENALTY).Pure decision — the score and both thresholds are passed in so a caller scanning a candidate list resolves them once, safe for unit tests.
- Parameters:
score- the peer's decayed loss scoremoderateThreshold- lower bound of the band, inclusivedemoteThreshold- upper bound of the band, exclusive- Returns:
- whether the score is in the moderate band
getAverageLossRatio
public float getAverageLossRatio()Average loss ratio across the fast and high-capacity tiers with a fresh measurement, as a fraction (0.0 - 1.0). Peers whose loss data is older thanPROP_LOSSY_WINDOWare skipped; 0.0 if no fresh data. The tiers are the baseline because the threshold governs demotion FROM them: as lossy peers are evicted the average drops and the bar tightens. Used by the Tuner to adaptPROP_LOSSY_THRESHOLDto the network.- Returns:
- the mean loss ratio as a fraction of 1.0, or 0.0 if no fresh data
getFastAverageLossRatio
public float getFastAverageLossRatio()Average loss ratio across the fast tier with a fresh measurement, as a fraction (0.0 - 1.0); 0.0 if none. SeegetAverageLossRatio().- Returns:
- the mean fast-tier loss ratio as a fraction of 1.0, or 0.0 if no fresh data
getHighCapAverageLossRatio
public float getHighCapAverageLossRatio()Average loss ratio across the high-capacity tier with a fresh measurement, as a fraction (0.0 - 1.0); 0.0 if none. SeegetAverageLossRatio().- Returns:
- the mean high-capacity-tier loss ratio as a fraction of 1.0, or 0.0 if no fresh data
getMedianLossRatio
public float getMedianLossRatio()Median loss ratio of the union of the fast and high-capacity tiers with a fresh measurement, as a fraction (0.0 - 1.0). 0.0 if none. The median is robust to the worst peers dragging the mean up, so the auto-tuned demotion threshold tracks the typical tier member. SeegetAverageLossRatio().- Returns:
- the median tier loss ratio as a fraction of 1.0, or 0.0 if no fresh data
averageLossRatio
Mean loss ratio of the union of the given tiers, restricted to profiles with a measurement fresh withinPROP_LOSSY_WINDOW.- Parameters:
tiers- the tier maps to average over- Returns:
- the mean as a fraction, 0.0 if no fresh data
medianLossRatio
Median loss ratio of the union of the given tiers, restricted to profiles with a measurement fresh withinPROP_LOSSY_WINDOW.- Parameters:
tiers- the tier maps to average over- Returns:
- the median as a fraction, 0.0 if no fresh data
freshLossRatios
Fresh loss ratios (positive and withinPROP_LOSSY_WINDOW) of the union of the given tiers, sorted ascending; empty if none.- Parameters:
tiers- the tier maps to sample from- Returns:
- the sorted fresh ratios
defaultTierSize
static int defaultTierSize(int known, int base, int divisor) Fallback tier size for a threshold that scales with network size.Networks at or below
SCALING_THRESHOLDknown routers use the configured base; above it the base scales with a fraction of the network.- Parameters:
known- routers in the network databasebase- configured size to use, and the floor once scaling appliesdivisor- network size is divided by this once scaling applies- Returns:
- the size to use unless a property overrides it
fastActiveCap
static int fastActiveCap(int active, int base) Ceiling on the fast tier from the number of reachable peers.1.5x headroom over active for near-active peers, never below
base.- Parameters:
active- peers reachable over any transportbase- floor to keep even when very few peers are reachable- Returns:
- the fast tier ceiling
highCapActiveCap
static int highCapActiveCap(int active, int base) Ceiling on the high-capacity tier from the number of reachable peers.One per reachable peer, never below
base.Was
active * 2. That asks for more members than there are peers to put in them: at 1221 active peers it set a target of 2442 for a tier that tops out around 700, so the target could never be met, the early return inpromoteToFillTiers()could never fire, and every demotion walked the whole capacity-ordered profile set looking for peers to seat in a tier that had already run out of candidates. A high-capacity member that is not reachable is not reservoir depth, it is a slot that cannot be used.- Parameters:
active- peers reachable over any transportbase- floor to keep even when very few peers are reachable- Returns:
- the high-capacity tier ceiling
getMinimumFastPeers
protected int getMinimumFastPeers()Minimum number of peers to keep in the fast tier.- Returns:
- the minimum fast peers
getMinimumFastPeers
protected int getMinimumFastPeers(int known) Variant taking the network size, for callers that already know it.getKnownRouters()walks every entry in the network database, which is far too expensive to repeat once per candidate.- Parameters:
known- routers in the network database- Returns:
- the minimum fast peers
getMaximumFastPeers
protected int getMaximumFastPeers()Maximum number of peers to keep in the fast tier, capped by active peers.- Returns:
- the maximum fast peers
getMaximumFastPeers
protected int getMaximumFastPeers(int known, int active) Variant taking both expensive inputs, for callers that already have them.- Parameters:
known- routers in the network databaseactive- peers currently reachable over any transport- Returns:
- the maximum fast peers
getFastPeerCount
public int getFastPeerCount()Current number of peers in the fast tier. Used by selectors to decide cross-pool diversity mode.- Returns:
- fast tier size
getHighCapPeerCount
public int getHighCapPeerCount()Current high-capacity tier population. Paired withgetFastPeerCount()so first-hop selection can report the pool it drew from: a 256-candidate sample means something very different against a 200-peer tier (the whole pool) than a 1500-peer one (a fraction), and the two call for different responses.- Returns:
- the number of peers in the high-capacity tier
getEffectiveCandidateSample
The candidate-sample floor currently in effect, for logging.- Parameters:
ctx- the router context- Returns:
- the floor passed to
maxCandidateSample(int, int)
getMaximumHighCapPeers
protected int getMaximumHighCapPeers()Maximum number of peers to keep in the high-capacity tier, capped by active peers.- Returns:
- the maximum high cap peers
getMaximumHighCapPeers
protected int getMaximumHighCapPeers(int known, int active) Variant taking both expensive inputs, for callers that already have them.- Parameters:
known- routers in the network databaseactive- peers currently reachable over any transport- Returns:
- the maximum high cap peers
getMinimumHighCapacityPeers
protected int getMinimumHighCapacityPeers()Minimum number of peers to keep in the high-capacity tier.- Returns:
- the minimum high capacity peers
getMinimumHighCapacityPeers
protected int getMinimumHighCapacityPeers(int known) Variant taking the network size, for callers that already know it.- Parameters:
known- routers in the network database- Returns:
- the minimum high capacity peers
num
Formats one number for the statistics display.- Parameters:
num- the value to format- Returns:
- the formatted text
main
Command-line entry point; reads profile dump files and prints thresholds.- Parameters:
args- paths to gzipped profile dump files
neutraliseUnknownBuildSuccess
static double neutraliseUnknownBuildSuccess(double ratio) Maps an unknown build-success reading onto a neutral 1.0.An absent stat, or a window in which nothing has settled yet, is not evidence of failure. Reading it as anything else would trip
isLowBuildSuccess()and switch off profile eviction, lengthen ghost cooldowns and put the first-hop selector into its defensive path for no reason - so "unknown" has to arrive upstream of every one of those as 1.0.- Parameters:
ratio- the raw ratio, or NaN when unknown- Returns:
ratio, or 1.0 when it is NaN
getTunnelBuildSuccess
public double getTunnelBuildSuccess()Tunnel build success ratio across the whole router, cached forBUILD_SUCCESS_CACHE_MSbecause a single lookup costs several rate reads. 1.0 (neutral) when the window is absent or empty.- Returns:
- the build success ratio as a fraction of 1.0
isLowBuildSuccess
public boolean isLowBuildSuccess()Whether recent tunnel build success is below the attack threshold.- Returns:
- whether low build success
writeProfile
Persist the given profile to disk.- Parameters:
profile- profile to write
getAverageLowLatencyRTT
public float getAverageLowLatencyRTT()Calibrated latency floor across fast peers with data, computed during eachreorganize()as the mean of the fastest samples rather than of the whole population.This is a second bound on the low-latency judgement, not a second judgement: a peer must satisfy both the per-transport calibrated floor and this cohort floor. It is measured from peer-test response times, a different quantity from the direct-link RTT behind the per-transport floor, so the two are never averaged together and this one cannot stand in for the other.
- Returns:
- the cohort floor in ms, 0 when fewer than
ProtocolRttBaseline.SAMPLES_FOR_BASELINEpeers have data
computeAverageLowLatencyRTT
private float computeAverageLowLatencyRTT()Compute average peer test response time across fast peers with data. Pure decision — no write lock required; reads the already-rebuilt _fastPeers.- Returns:
- the average RTT in ms, 0 if fewer than
ProtocolRttBaseline.SAMPLES_FOR_BASELINEpeers have data