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() uses try-lock escalation so a read path never blocks on the write lock.
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static enumWhy a peer was evicted from a tier.static enumDefines peer selection slicing modes for tier-based peer organization.Field Summary
FieldsModifier and TypeFieldDescriptionstatic 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.static final intCeiling on profiles held in memory, in profiles.static final doubleThreshold below which the network is considered under attack.static final intDefault minimum packet sample size (20 = full 5% bucket resolution).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.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.(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.(package private) static final intUpper bound, so a typo cannot turn selection into a full tier scan.(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.static final intFloor for the in-memory profile cap, in profiles.(package private) static final intLifetime requests a peer needs before its acceptance ratio may exclude it.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.(package private) static final intNetwork size above which tier thresholds scale with the network instead of staying at their configured default.(package private) static final longShortest gap between two full tier-refill scans.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.(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.(package private) static intclampMaxProfiles(int maxProfiles) Clamps the configured max-profiles value into [100, ABSOLUTE_MAX_PROFILES].(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.(package private) static longcomputeFastRttCeiling(double boundaryRttMs) Compute the adaptive absolute-RTT ceiling for fast-tier selection.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.(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.(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.(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.(package private) static booleanfirstHopRttIsSlow(int firstHopRttMs, int thresholdMs) Whether a measured first-hop RTT warrants removal from the fast tiers.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.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.(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.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.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.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.(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.(package private) static inthighCapActiveCap(int active, int base) Ceiling on the high-capacity tier from the number of reachable peers.(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.(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.(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.(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.(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.(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.booleanisSelectable(Hash peer) 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.(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.(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 StringmaskedIPKey(byte[] ip, int mask) Fingerprint key for one masked IP, byte-identical to the keyMaskedIPSet.maskedIPbuilds for the same address and mask.(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.(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.(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.(package private) static StringpromotionEvidence(int bonus, int raw, boolean promotedIsLive) Evidence line for a promotion log: only the values that explain something.(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.(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.(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.(package private) static intremainingToSelect(int howMany, Set<Hash> matches) How many slots are still free in a cascade's destination set.(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.(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.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.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.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.(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.(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.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:
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:
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:
PROP_LOW_LATENCY_MULTIPLE
Property key for the calibrated low-latency multiple. @since 0.9.71+.- See Also:
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:
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_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:
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:
PROP_LOSSY_WINDOW
Config property for how long a reported loss ratio stays fresh.- 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:
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:
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:
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
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
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
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)
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
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
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
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
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
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
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
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 the newest activity timestampnow- 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
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
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
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
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.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
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.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
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
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
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
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)
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
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
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:ClientPeerSelectorbuilds four fallback sets asnew ArraySet<>(needed)and passesneededashowMany.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
maskedIPKey
Fingerprint key for one masked IP, byte-identical to the keyMaskedIPSet.maskedIPbuilds for the same address and mask.Format: a family-delimiting leading char ('.' for IPv4, ':' for IPv6, which also doubles the matched byte count), then two '0'-offset hex nibbles per matched byte. The leading char differs by address family and port keys begin with 'p', family keys with 'x', so an IP key can never equal a port or family key. Keeping the
MaskedIPSetformat means an accumulator built by MaskedIPSet's own constructor still compares equal; the floodfill path's packed-longkey cannot be used because the accumulator arrives as aMaskedIPSetfrom callers outside this package.ProfileOrganizerSelectionCostTestpins the format.- Parameters:
ip- an IPv4 (4-byte) or IPv6 (16-byte) addressmask- 1-4, the number of leading bytes to match- Returns:
- the fingerprint key; equal keys mean "same masked subnet"
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
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
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
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
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
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
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
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 of lockedPlaceProfile().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
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 from PeerTestJob when 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
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
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
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
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 call promoteToFillTiers() — 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
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
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 via ProfileManagerImpl so 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
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
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