- Direct Known Subclasses:
AliasedTunnelPool
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static classAlways build a LeaseSet with Leases in sorted order, so thatLeaseSet.equals(Object)and lease-by-lease equals() always work.(package private) static final classWhy a pool could not supply a tunnel, as counts rather than prose.(package private) static enumWhy a tunnel's local transport session was lost.Field Summary
FieldsModifier and TypeFieldDescriptionprotected booleanWhether this pool is running.protected final RouterContextThe router context.protected final LogThe log.(package private) static final longDefault early expiration time for pruned tunnels (120 seconds).(package private) static final longRolling average test duration below which the test window is narrowed.(package private) static final longMinimum remaining life for a stale UNTESTED tunnel to be worth one last-chance test before pruning: a test needs a minute of runway for the request/reply round trip (plus the test itself) before the tunnel expires anyway.(package private) static final doubleHard ceiling on the adaptive test timeout multiplier.(package private) static final intHard cap on concurrent builds per pool direction in normal operation.(package private) static final intAbsolute ceiling on builds in flight per pool and direction, applied to every build path including the pre-emergency burst.(package private) static final intCap on last-chance tests enqueued per sweep pass — the global test caps still apply, but an explicit small bound keeps a fully-UNTESTED pool from flooding the first-test buffer in one ensure cycle.(package private) static final longCap on the failure-scaled pre-build window — beyond this, starting earlier would keep nearly the whole pool's lifetime in build mode.(package private) static final longHard ceiling ongetTunnelLifetime(RouterContext): the standard 10m lifetime plus the 1m grace period other routers afford.(package private) static final longBase pre-build window: start replacements this long before existing tunnels expire so fresh builds (10-40s) complete before the old tunnels die.(package private) static final intBuilds started in one pre-emergency burst.(package private) static final doublePre-emergency threshold: fraction of target below which builds start immediately.(package private) static final Comparator<TunnelInfo> Best tunnels first for LeaseSet publication: freshest (longest remaining life) so the lease survives floodfill propagation, then lowest average latency, then fewest consecutive failures.(package private) static final doublePriority weight for service pool builds.(package private) static final longRolling average test duration above which the test window is widened.(package private) static final intSoft-failure count at which a still-usable tunnel is treated as degraded for ensure-throttle and deficit purposes.(package private) static final intHigher removal bar for soft best-effort send timeouts (status 3).(package private) static final intConsecutive build timeouts at which the spare grows to two tunnels.(package private) static final intConsecutive build timeouts at which the pool starts holding one spare tunnel above target (seecomputeDifficultySpare(int)).(package private) static final StringRate stat recording how many tunnelsgetHealthyTunnelCount()dropped for want of a transport session to their local hop.(package private) static final StringRate stat recording how many tunnelsgetUsableTunnelCount()dropped for the same reason.Constructor Summary
ConstructorsConstructorDescriptionTunnelPool(RouterContext ctx, TunnelPoolManager mgr, TunnelPoolSettings settings, TunnelPeerSelector sel) Tunnel pool.Method Summary
Modifier and TypeMethodDescriptionprotected voidaddTunnel(TunnelInfo info) Add a tunnel to the pool.(package private) static booleanalreadyCondemned(boolean isDead, TunnelTestStatus status) Whether a failure report is a repeat of a condemnation already made.(package private) booleanBuild bootstrap tunnels for exploratory pools during cold start.(package private) voidbuildComplete(PooledTunnelCreatorConfig cfg, BuildExecutor.Result result) Remove from the _inprogress list and call addTunnel() if result is SUCCESS.(package private) booleanThis will build a fallback (zero-hop) tunnel ONLY if this pool is exploratory, or the settings allow it.buildLeases(List<TunnelInfo> goodTunnels, TunnelInfo zeroHopTunnel) Assemble the Lease objects for the selected tunnels: the single zero-hop lease (if any), then the quality-sorted GOOD tunnels.cancelExcessInProgress(int maxAllowed) Cancel excess in-progress tunnel builds to stay within budget.(package private) static TunnelPool.PoolStateclassifyPoolState(List<TunnelInfo> tunnels, long now, java.util.function.Predicate<TunnelInfo> backlogged) Count the pool by selection outcome.(package private) static TunnelPool.SessionLossclassifySessionLoss(boolean hadSessionAtBuild, boolean establishedNow, boolean peerReachable) Attribute a lost local transport session to our side or the peer's.voidClear FAILING/FAILED flags on tunnels that have provably carried real traffic recently.voidDrop any lease-material prune override.(package private) static intcomputeDeficit(int healthySafe, int safeActive, int softDegraded, int failingCount, int untestedCount, int target, int effectiveTarget, int currentInProgress) Pure deficit calculation so unit tests can pin the soft-degraded occupancy rules without a router context.(package private) static intcomputeDifficultySpare(int consecutiveBuildTimeouts) Spare tunnels held above target while the pool is having difficulty building.(package private) static longcomputeLeaseEndDate(long tunnelExpiration, long now, long maxLeaseMs) Compute the end date advertised for a lease, given the tunnel's own expiration.(package private) static longcomputePreBuildWindowMs(double rate) How far ahead of expiry to start building replacements, scaled by how badly this pool's builds are failing: a pool with a high build-timeout rate needs more lead time because each attempt may burn a full timeout (17-30s) before the next one starts.(package private) PooledTunnelCreatorConfigThis only sets the peers and creation/expiration times in the configuration.(package private) PooledTunnelCreatorConfigconfigureNewTunnel(boolean forceZeroHop, Set<Hash> excludeFirstHops) Configure a new tunnel, optionally excluding peers from first-hop selection.(package private) static intcountLeaseViableTunnels(List<TunnelInfo> tunnels, List<TunnelInfo> toRemove, long now) How many tunnels could still be advertised right now, on the same barselectLeaseTunnelsapplies, matching the wording of the sibling prune path.(package private) intCount the zero-hop fallback tunnels in this pool.(package private) voidEnsure the pool has at least target valid tunnels, building replacements proactively when the count drops below target.(package private) static booleanexceedsRemovalThreshold(int failures) Whether cumulative data-phase and TestJob failures exceed the removal threshold.(package private) static booleanexceedsRemovalThreshold(int failures, boolean soft) Whether the given failure class exceeds its marking bar.static booleanexceedsSoftRemovalThreshold(int failures) Whether a soft failure count crosses the soft marking bar.voidForce a tunnel to fail immediately and trigger a replacement build.static StringformatPoolIdentity(TunnelPoolSettings settings) Format a pool identity for log messages, combining nickname and truncated hash.intCount GOOD and TESTING (non-failed, not expired) tunnels in the pool.(package private) longThis pool's rolling average of completed test durations, used to adapt the test timeout multiplier.(package private) longgetAdaptiveTestTimeout(long baseTimeout) Adapt a base test timeout to this pool's observed test speed.intAverage bandwidth per tunnel in the pool.(package private) static intQuantity override threshold from config or default (12).(package private) intCount of consecutive tunnel build timeouts.(package private) intCurrent difficulty spare for this pool.(package private) intThe tunnel count the pool builds and keeps: the ordinary active-count target, unaffected by any lease surge.(package private) intUsable tunnels that are not soft-degraded: same filters asgetUsableTunnelCount()plus a floor onTunnelInfo.getSoftFailures().Build and return current LeaseSet from our tunnels.intCount tunnels currently being built.(package private) static longMaximum lease set lease duration from config or default (10 minutes).(package private) static longLeaseSet build minimum interval from config or default (2 minutes).(package private) intThe advertiseable count an in-force surge needs, or 0 when none is.static longA lease counts toward a re-mint while it has at least this much remaining: three minutes covers the re-sign request, floodfill propagation, and initial connection establishment, and a tunnel closer to expiry than this is already inside the pool's proactive replacement window (BuildExecutor starts replacing GOOD tunnels 330s out), so counting it would overstate the pool's health.longTotal lifetime processed bytes for this pool.(package private) static intMax concurrent builds per direction from config or default (6).intTunnels in this pool whose lease falls inside the pre-build window.(package private) TunnelPoolPaired pool (inbound <-> outbound for same destination).(package private) longEarly expiry time for pruned tunnels.(package private) static longEarly expiry time for pruned tunnels, shared by every prune path (pool sweep and Remove Slow) so one pool's tunnels cannot be given a different head start than another's for the same configuration.RateStat name for the bandwidth graph.(package private) static longRefresh throttle interval from config or default (2 minutes).(package private) static intgetReplacementTunnelBuffer(double buildSuccess) How many usable tunnels above the target the pool may hold before a further build must replace a non-GOOD tunnel instead of being added.Pool settings.(package private) static longStartup suppression period from config or default (5 minutes).intCount tunnels that have been built but not yet passed their first test.Tunnel by its gateway tunnel ID.(package private) static intAverage test latency for a tunnel.static intTunnel lifetime from config or default (11 minutes).TunnelPoolManager that owns this pool.(package private) intCount tunnels that are usable for routing — not expired, not failed, not marked FAILING.intCount valid (non-failed, not expired) tunnels in the pool.(package private) static booleanhasValidTunnelsBlockingEmergency(int validCount) Emergency collapse is deferred while valid tunnels remain (soft-degraded or FAILING-but-not-FAILED).(package private) booleanWhether any active tunnel in this pool is a zero-hop fallback while the pool is configured for multi-hop.(package private) voidIncrement consecutive build timeout counter.booleanisAlive()Is this pool running AND either exploratory, or tracked by the client manager? A pool will be alive but not tracked after the client manager removes it but before all the tunnels have expired.(package private) static booleanisDeficitThrottled(long now, long lastBuild, int safeActive, int inProgress, boolean incompleteLeaseSet) Compatibility overload: callers that only have a single usable count (no soft-degraded split) treat it as both healthy and safe.(package private) static booleanisDeficitThrottled(long now, long lastBuild, int healthySafe, int safeActive, int inProgress, boolean incompleteLeaseSet) Whether a deficit-build batch must wait.(package private) static booleanisEmergencyCooldownActive(long elapsedMs, long usableCount, long inProgressCount) Whether the emergency-cooldown window has elapsed.(package private) static booleanisEnsureThrottled(long now, long lastEnsure, int usableTunnelCount, boolean incompleteLeaseSet) Whether ensureSufficientTunnels() must wait for this pool.(package private) static booleanisEnsureThrottled(long now, long lastEnsure, int usableTunnelCount, int healthyCount, boolean incompleteLeaseSet) Whether ensureSufficientTunnels() must wait for this pool, using separate usable and healthy counts.(package private) static booleanisEnsureThrottled(long now, long lastEnsure, int usableTunnelCount, int healthyCount, boolean incompleteLeaseSet, long lastRemoval) Whether ensureSufficientTunnels() must wait for this pool, using separate usable and healthy counts and a removal-time signal.(package private) static booleanisEnsureThrottled(long now, long lastEnsure, int usableTunnelCount, int healthyCount, boolean incompleteLeaseSet, long lastRemoval, int inProgress) Whether ensureSufficientTunnels() must wait for this pool, using separate usable and healthy counts, a removal-time signal, and the number of builds in flight.(package private) booleanisExpiringSoon(long now) Check if the LeaseSet is expiring soon (within 1 minute).(package private) static booleanisLastChanceTestable(long expiration, long now, long minLifeMs) Whether a tunnel expiring within the pre-build window still has enough life left for a last-chance test to mean anything.(package private) static booleanisMateriallyShort(int leases, int wanted) Whether a published LeaseSet is too small to count as a working set.(package private) static booleanisPreEmergencyBurstLive(long burstAt, long now, long window) Whether this pool dispatched a pre-emergency burst recently enough that its in-flight builds are governed by the per-pool cap instead of the global budget.(package private) booleanisPreEmergencyExempt(long now) Package-visible for the build executor: whether this pool's in-flight builds should bypass the global dispatch budget.(package private) static booleanWhether the tunnel is soft-degraded for ensure/deficit purposes: still in the pool and not FAILING, but soft failures have reachedSOFT_DEGRADED_FOR_ENSURE.(package private) static booleanisSoftSendFailure(int status) Whether the status is a soft best-effort send timeout (3).booleanWhether this pool is struggling to meet its tunnel targets.(package private) static booleanisTrafficProvenUntestedLeaseCandidate(TunnelInfo tunnel, long expireAfter, long now) Whether an inbound UNTESTED tunnel has verified delivering real data recently enough to be published as a lease.(package private) static List<TunnelInfo> leaseSurgeCandidates(List<TunnelInfo> tunnels, List<TunnelInfo> toRemove, long now) Tunnels a lease surge may schedule for early expiry: GOOD, past the lease admission floor (so they can no longer be advertised), not already being pruned, and not recently active.List of tunnelInfo instances of tunnels currently being built.List of tunnels currently in the pool.protected LeaseSetBuild a leaseSet with the required tunnels that aren't about to expire.(package private) static longmaxExpirationStagger(long lifetimeMs) Upper bound for the expiration stagger: keeps lifetime plus stagger under the NetDb's 15-minute future-lease rejection boundary with a 15-second margin, and never exceeds 4 minutes.(package private) booleanDo we really need more fallbacks? Used to prevent a zillion of them.(package private) voidCalled by TestJob when a server pool tunnel fails a test but is retained.(package private) static intRank a candidate test partner; lower is better.(package private) static booleanpassesScanGates(TunnelInfo info, long now, boolean longTunnelsOnly) Whether a pooled tunnel passes the scan eligibility gates: not failed, not over the consecutive-test-failure cap (test failures are often reply-path problems, not tunnel quality), non-expired, and — on the first pass.(package private) voidProactively republish the LeaseSet when all tunnels are healthy (all > 5 min expiry).intPrune excess tunnels from the pool to stay within budget.(package private) voidNo-op for outbound and exploratory pools.voidrefreshLeaseSet(boolean force) Refresh the LeaseSet, throttled to prevent flooding but not on initial creation.(package private) voidRemove a config that was never actually sent to the network (e.g.(package private) voidremoveTunnel(TunnelInfo info) Remove a tunnel from the pool.(package private) booleanSynchronous tunnel removal for use during recovery or critical situations.(package private) voidreportSendFailure(TunnelInfo cfg, int status) Report a data-phase dispatch failure for a tunnel in this pool.voidRequest new inbound tunnels so the pool holds the target count with at leastgetLeaseViabilityWindow(RouterContext)remaining.voidrequestLeaseSurge(int count) Note how many advertiseable tunnels the imminent mint needs, sopruneExcessTunnels()can offer up the ones that are no longer advertiseable until the pool has enough.(package private) static longrotateWindowStart(long windowStart, long now, long windowMs) Roll the build-rate window forward when it has expired.(package private) TunnelInfoPull a random tunnel out of the pool.(package private) TunnelInfoselectTunnel(Hash closestTo) Return the tunnel from the pool that is XOR-closest to the target.(package private) TunnelInfoSelect a tunnel to serve as a test round's partner, preferring partners that have demonstrated they work.(package private) voidsetPairedPool(TunnelPool pool) Paired pool (inbound <-> outbound for same destination).(package private) voidsetSettings(TunnelPoolSettings settings) Update the settings for this pool.(package private) static booleanshouldBypassEnsureThrottle(boolean soft, int failures, boolean overBar) Whether a data-phase failure signal is strong enough to run the ensure body immediately instead of through the throttle gate.(package private) static booleanshouldDeferMissingPublished(long lastRefreshTime, long now, long throttleMs) Pure defer decision when NetDB has no local LeaseSet: true when a publish is still within the throttle window (async store pending), so forcing would only re-request the same data.(package private) static booleanshouldDeferPublishedRefresh(long earliestLeaseDate, long now, long throttleMs) Pure earliest-keyed defer decision: the published LeaseSet still has full capacity (every remaining lease outlasts the throttle window), so this refresh may be deferred without losing reachability.(package private) static booleanshouldDropForLostSession(long firstMissingAt, long now, long hysteresis) Whether a tunnel whose local session is gone should be dropped from the pool.(package private) static booleanshouldLogSendFailure(long now, long lastLogTime) Whether a data-phase failure or condemnation line should be logged now.(package private) static booleanshouldSkipAsyncPublish(boolean hasPublishedLS, long lastRefreshTime, long now, long throttleMs) Pure skip decision for publishLeaseSetIfNeeded: NetDB still null but we published within the throttle window — treat as in-flight rather than missing, so tunnel-add does not re-request every build.(package private) static booleanshouldWarnSelectPeersFailure(long uptime, long now, long lastWarnTime) The selectPeers-failure WARN is rate-limited: never beforeSELECT_PEERS_FAILURE_SILENCE_MSof uptime, then at most once perSELECT_PEERS_FAILURE_WARN_INTERVAL_MSper pool.(package private) voidshutdown()Shut down the pool and clean up resources.intsize()Count every tunnel in the pool, including failed and expired ones awaiting cleanup.static intThe soft failure count that marks a tunnel for retention.(package private) static longstampLostSession(long currentMissingAt, long now) The value to record for a tunnel whose local transport session has just been observed missing.(package private) voidstartup()Warning, this may be called more than once (without an intervening shutdown()) if the tunnel is stopped and then restarted by the client manager with the same Destination (i.e.(package private) static doubletimeoutRate(long attempts, long timeouts) Fraction of counted build attempts that timed out.toString()Description of the pool and its settings.(package private) voidtunnelFailed(TunnelInfo cfg) Remove tunnel and blame all peers (not necessarily equally).(package private) voidtunnelFailed(TunnelInfo cfg, Hash blamePeer) Remove the tunnel and blame only one peer.(package private) voidMark a tunnel failed without blaming any peer.(package private) voidupdateAdaptiveTestDuration(long durationMs) Record a completed test duration and adapt this pool's test window.(package private) static doubleupdatedAdaptiveTestMultiplier(double mult, long avgMs) Next test timeout multiplier for a given rolling average.(package private) static longupdatedAvgTestDuration(long avg, long sample) Fold a completed test duration into the rolling average.(package private) static intusableLeaseMinimum(int wanted) The smallest LeaseSet treated as usable for a given target.(package private) static StringzombieTransportBucket(String style) Normalise a RouterInfo transport style into one of the known transports.
Field Details
QUALITY_COMPARATOR
Best tunnels first for LeaseSet publication: freshest (longest remaining life) so the lease survives floodfill propagation, then lowest average latency, then fewest consecutive failures._context
The router context._log
The log._alive
protected volatile boolean _aliveWhether this pool is running.ZOMBIE_STAT_HEALTHY
Rate stat recording how many tunnelsgetHealthyTunnelCount()dropped for want of a transport session to their local hop.- See Also:
ZOMBIE_STAT_USABLE
Rate stat recording how many tunnelsgetUsableTunnelCount()dropped for the same reason. Kept separate fromZOMBIE_STAT_HEALTHYbecause the two counts apply different filters, so their excluded figures are not derivable from one another.- See Also:
PRE_BUILD_WINDOW_MS
static final long PRE_BUILD_WINDOW_MSBase pre-build window: start replacements this long before existing tunnels expire so fresh builds (10-40s) complete before the old tunnels die.- See Also:
MAX_PRE_BUILD_WINDOW_MS
static final long MAX_PRE_BUILD_WINDOW_MSCap on the failure-scaled pre-build window — beyond this, starting earlier would keep nearly the whole pool's lifetime in build mode.- See Also:
LAST_CHANCE_MIN_LIFE_MS
static final long LAST_CHANCE_MIN_LIFE_MSMinimum remaining life for a stale UNTESTED tunnel to be worth one last-chance test before pruning: a test needs a minute of runway for the request/reply round trip (plus the test itself) before the tunnel expires anyway. Below this the tunnel is pruned as before.- See Also:
MAX_LAST_CHANCE_PER_SWEEP
static final int MAX_LAST_CHANCE_PER_SWEEPCap on last-chance tests enqueued per sweep pass — the global test caps still apply, but an explicit small bound keeps a fully-UNTESTED pool from flooding the first-test buffer in one ensure cycle.- See Also:
MAX_BUILD_PER_POOL_DIR
static final int MAX_BUILD_PER_POOL_DIRHard cap on concurrent builds per pool direction in normal operation. Prevents the buildComplete → ensureSufficientTunnels feedback loop from flooding the BuildExecutor when every attempt fails. Matches BuildExecutor.MAX_PER_POOL_DIR. Collapsed pools may raise the cap via shouldSkipDueToInProgress / buildReplacementTunnels. Raised from 2 so an incomplete LeaseSet / depleted pool can stage replacements in parallel instead of serializing one build per throttle. Raised from 4 to 8 because four failure-handling interventions did not move the outbound test failure rate — the bottleneck is build throughput, not failure handling. 8 parallel builds per pool direction is safe because exploratory pools already run higher concurrency without collapse.- See Also:
SOFT_REMOVAL_THRESHOLD
static final int SOFT_REMOVAL_THRESHOLDHigher removal bar for soft best-effort send timeouts (status 3). Congestion / slow destinations must not mass-remove healthy tunnels at the hard-failure bar (4), but a tunnel that times out dozens of times with no successful tests still rotates out.Must stay strictly above
SOFT_DEGRADED_FOR_ENSURE. The gap between the two is the pre-emptive build window: at the degraded count the ensure gate starts seeing a deficit and builds replacements while the tunnel is still selectable, so a pool never reaches zero usable tunnels. Equalising them collapses that window to nothing — the ensure gate only reacts once the tunnel has already been removed, which is total collapse rather than pre-emptive recovery, and is what drove 38 EMERGENCY rebuilds and 128 all-backlogged events in one short window. Lowering this to 5 (from 7) was a regression.- See Also:
PRE_EMERGENCY_FRACTION
static final double PRE_EMERGENCY_FRACTIONPre-emergency threshold: fraction of target below which builds start immediately.Half, not a quarter: at 0.25 a 4-tunnel pool only burst at zero healthy, so the burst's exemption from the global build budget engaged only after the pool had already collapsed, and recovery started from a hole while its non-exempt replacements queued behind other pools' builds. Half engages the burst and its exemption one healthy tunnel before the drain reaches zero. The threshold is exactly the
isMateriallyShort(int, int)line, which made the separate thin-pool fast-path below the pre-emergency branch unreachable; it was removed and its warning folded into the branch.- See Also:
PRE_EMERGENCY_BURST
static final int PRE_EMERGENCY_BURSTBuilds started in one pre-emergency burst. Named so it stays consistent withPRE_EMERGENCY_FRACTIONrather than sitting as a bare literal in the trigger.- See Also:
MAX_INPROGRESS_PER_POOL_DIR
static final int MAX_INPROGRESS_PER_POOL_DIRAbsolute ceiling on builds in flight per pool and direction, applied to every build path including the pre-emergency burst.Bounds the damage from any single path bypassing the normal guards. Without it a pool that stays below the emergency threshold starts a burst on every cycle while its builds neither complete nor fail, and the in-progress count climbs without limit. Observed at 99 concurrent outbound builds in a 4-tunnel pool, which starves every other pool for build capacity while fixing nothing.
- See Also:
SLOW_TEST_AVG_MS
static final long SLOW_TEST_AVG_MSRolling average test duration above which the test window is widened.- See Also:
FAST_TEST_AVG_MS
static final long FAST_TEST_AVG_MSRolling average test duration below which the test window is narrowed.- See Also:
MAX_ADAPTIVE_TEST_MULTIPLIER
static final double MAX_ADAPTIVE_TEST_MULTIPLIERHard ceiling on the adaptive test timeout multiplier. A dead tunnel must still be condemned, so the window cannot be widened without limit.- See Also:
SOFT_DEGRADED_FOR_ENSURE
static final int SOFT_DEGRADED_FOR_ENSURESoft-failure count at which a still-usable tunnel is treated as degraded for ensure-throttle and deficit purposes. Below the soft removal bar so replacements start building before the tunnel is rotated out. A pool of soft-degraded tunnels can pass tests yet fail every data-phase send (status 3), leaving clients with half-open connects while the ensure gate still sees a "full" pool. Raised from 3 so transient congestion does not mark half the pool degraded and trip collapse-sized rebuilds every few seconds.- See Also:
SERVICE_POOL_PRIORITY
static final double SERVICE_POOL_PRIORITYPriority weight for service pool builds. Service pools carry user-facing traffic (eepsites, I2PSnark) and should get priority when the build executor is saturated.- See Also:
SPARE_LOW_THRESHOLD
static final int SPARE_LOW_THRESHOLDConsecutive build timeouts at which the pool starts holding one spare tunnel above target (seecomputeDifficultySpare(int)).- See Also:
SPARE_HIGH_THRESHOLD
static final int SPARE_HIGH_THRESHOLDConsecutive build timeouts at which the spare grows to two tunnels.- See Also:
DEFAULT_PRUNE_EARLY_EXPIRY
static final long DEFAULT_PRUNE_EARLY_EXPIRYDefault early expiration time for pruned tunnels (120 seconds).- See Also:
MAX_TUNNEL_LIFETIME_MS
static final long MAX_TUNNEL_LIFETIME_MSHard ceiling ongetTunnelLifetime(RouterContext): the standard 10m lifetime plus the 1m grace period other routers afford. The published lease is capped separately atgetLeaseMaxDuration(RouterContext)(10m), so the tunnel runs one grace period past the lease it advertises — which is what keeps the advertised value on the standard 10m. Exceeding 11m buys no overlap: the lease cap binds first, so a longer tunnel is simply paid for during a window no LeaseSet covers.- See Also:
Constructor Details
TunnelPool
TunnelPool(RouterContext ctx, TunnelPoolManager mgr, TunnelPoolSettings settings, TunnelPeerSelector sel) Tunnel pool.- Parameters:
ctx- the router contextmgr- the pool manager owning this poolsettings- the pool's tunnel quantity, direction and exploratory flagssel- the peer selector used to choose tunnel build participants
Method Details
updatedAvgTestDuration
static long updatedAvgTestDuration(long avg, long sample) Fold a completed test duration into the rolling average.Pure so the smoothing is testable without a router.
- Parameters:
avg- current average, 0 if nonesample- the duration just observed, ms- Returns:
- the updated average
updatedAdaptiveTestMultiplier
static double updatedAdaptiveTestMultiplier(double mult, long avgMs) Next test timeout multiplier for a given rolling average.Pure. Rises when tests are consistently slow so a slow-but-alive tunnel is not condemned for exceeding a window that was never long enough for it, and falls back when they are not. Capped at
MAX_ADAPTIVE_TEST_MULTIPLIERso a persistently slow path cannot widen the window without bound and mask genuinely dead tunnels.- Parameters:
mult- current multiplier, 1.0 up toMAX_ADAPTIVE_TEST_MULTIPLIERavgMs- rolling average test duration, ms- Returns:
- the updated multiplier
updateAdaptiveTestDuration
void updateAdaptiveTestDuration(long durationMs) Record a completed test duration and adapt this pool's test window.- Parameters:
durationMs- elapsed test time, ms
getAdaptiveAvgTestDuration
long getAdaptiveAvgTestDuration()This pool's rolling average of completed test durations, used to adapt the test timeout multiplier.- Returns:
- this pool's rolling average test duration, 0 if none recorded
getAdaptiveTestTimeout
long getAdaptiveTestTimeout(long baseTimeout) Adapt a base test timeout to this pool's observed test speed.- Parameters:
baseTimeout- the configured timeout, ms- Returns:
- the adapted timeout, ms
isPreEmergencyBurstLive
static boolean isPreEmergencyBurstLive(long burstAt, long now, long window) Whether this pool dispatched a pre-emergency burst recently enough that its in-flight builds are governed by the per-pool cap instead of the global budget. Pure decision helper for unit tests.- Parameters:
burstAt- last pre-emergency burst time in ms, or 0 if nevernow- current time in mswindow- how long a burst keeps its builds exempt- Returns:
- true when in-flight builds of this pool bypass the global reduction
isPreEmergencyExempt
boolean isPreEmergencyExempt(long now) Package-visible for the build executor: whether this pool's in-flight builds should bypass the global dispatch budget.- Parameters:
now- current time in ms- Returns:
- true when a pre-emergency burst is still recent
getPruneEarlyExpiry
long getPruneEarlyExpiry()Early expiry time for pruned tunnels. Reads fromrouter.pruneEarlyExpiryDelay, falling back to the legacyrouter.tunnel.pruneEarlyExpiryDelayalias, then the default (120s).- Returns:
- early expiry time in milliseconds
getPruneEarlyExpiry
Early expiry time for pruned tunnels, shared by every prune path (pool sweep and Remove Slow) so one pool's tunnels cannot be given a different head start than another's for the same configuration. Precedence: canonicalPROP_PRUNE_EARLY_EXPIRYfirst, then the legacyLEGACY_PROP_PRUNE_EARLY_EXPIRYalias kept for configs and scripts that predate the canonical name, elseDEFAULT_PRUNE_EARLY_EXPIRY.- Parameters:
ctx- the router context- Returns:
- early expiry time in milliseconds
getTunnelLifetime
Tunnel lifetime from config or default (11 minutes).The tunnel outlives its own published lease by
LEASE_SAFETY_MARGIN(60s). That difference is the grace period, and it is what makes the advertised value land on the I2P standard: the lease end ismin(tunnel expiry, now + leaseMaxDuration) - 60s, so with an 11m tunnel and the 10mgetLeaseMaxDuration(RouterContext)cap the cap binds and peers see ~10m. Shortening the tunnel below the cap would drag the advertised lease down with it — a 10m tunnel advertises only 9m — so the tunnel is deliberately held one grace period above the lease cap.Hard-capped at 11m: other routers afford a 1m grace period beyond the standard 10m lifetime and no more, so a longer tunnel would be paid for during a window no LeaseSet covers and would outlive what peers will tolerate caching.
Tunable via
i2p.tunnel.lifetime(default: 660000, capped at 660000).- Parameters:
ctx- the router context- Returns:
- the tunnel lifetime in milliseconds, never above 11 minutes
getLeaseViabilityWindow
A lease counts toward a re-mint while it has at least this much remaining: three minutes covers the re-sign request, floodfill propagation, and initial connection establishment, and a tunnel closer to expiry than this is already inside the pool's proactive replacement window (BuildExecutor starts replacing GOOD tunnels 330s out), so counting it would overstate the pool's health. Shrinks toward two minutes for very short lifetimes so short-lifetime configs aren't stuck deferring forever.- Parameters:
ctx- the router context- Returns:
- the lease viability window in ms
getMaxConcurrentBuildsPerDirection
Max concurrent builds per direction from config or default (6). Tunable viai2p.tunnel.maxConcurrentBuildsPerDirection(default: 6).- Parameters:
ctx- the router context- Returns:
- the max concurrent builds per direction
getStartupTime
Startup suppression period from config or default (5 minutes). Tunable viai2p.tunnel.startupTime(default: 300000).- Parameters:
ctx- the router context- Returns:
- the startup time in milliseconds
getBuildTriesQuantityOverride
Quantity override threshold from config or default (12). If less than one success in this many, reduce quantity. Tunable viai2p.tunnel.buildTriesQuantityOverride(default: 12).- Parameters:
ctx- the router context- Returns:
- the quantity override threshold
getRefreshThrottle
Refresh throttle interval from config or default (2 minutes). Minimum interval between LeaseSet publishes, so a failed inbound tunnel is dropped from the published LeaseSet promptly while staying under the floodfill republish budget (~6 per destination per 10 min). Tunable viai2p.tunnel.refreshThrottle(default: 120000).- Parameters:
ctx- the router context- Returns:
- the refresh throttle interval in milliseconds
getLeaseSetBuildMinInterval
LeaseSet build minimum interval from config or default (2 minutes). Minimum interval between LeaseSet object rebuilds, kept at or below the refresh throttle so a republish always reflects the current tunnel set (including newly built tunnels and excluding failed ones). Tunable viai2p.tunnel.leasesetBuildMinInterval(default: 120000).- Parameters:
ctx- the router context- Returns:
- the LeaseSet build minimum interval in milliseconds
getLeaseMaxDuration
Maximum lease set lease duration from config or default (10 minutes). Lease end dates in published LeaseSets are capped to this value from now, so peers re-fetch our LeaseSet sooner than the 11-minute tunnel lifetime. The tunnel itself continues to process messages; only the cached LeaseSet on the requesting side expires earlier, triggering a re-fetch.The 5-minute republish cycle re-floods (or re-mints) well before a lease dies: the re-mint floor re-signs any stored copy under 10 minutes, and the reschedule anchors each check inside the 5-minute expiry window. Set "
i2p.tunnel.leaseMaxDuration" explicitly to override.- Parameters:
ctx- the router context- Returns:
- the maximum lease duration in milliseconds
startup
void startup()Warning, this may be called more than once (without an intervening shutdown()) if the tunnel is stopped and then restarted by the client manager with the same Destination (i.e. for servers or clients w/ persistent key, or restarting close-on-idle clients).shutdown
void shutdown()Shut down the pool and clean up resources.getRateName
getTunnelPoolManager
TunnelPoolManager that owns this pool.- Returns:
- non-null
getAvgBWPerTunnel
public int getAvgBWPerTunnel()Average bandwidth per tunnel in the pool.- Returns:
- average bandwidth per configured tunnel in Bps
selectTunnel
TunnelInfo selectTunnel()Pull a random tunnel out of the pool. If there are none available but the pool is configured to allow 0hop tunnels, this builds a fake one and returns it.- Returns:
- null on failure, but it should always build and return a fallback
selectTunnelForTest
TunnelInfo selectTunnelForTest()Select a tunnel to serve as a test round's partner, preferring partners that have demonstrated they work.Distinct from
selectTunnel(), which serves the live data path and must keep its uniform-random choice: changing that would alter which tunnel carries real traffic, which is well outside what a diagnosis warrants. This is used only where a test round picks the leg its reply comes back through.A test round is a composite of the tunnel under test and the partner the reply arrives through, and a single pass/fail bit cannot separate them. Choosing partners uniformly at random therefore injects a source of noise that looks exactly like a failing tunnel: a partner whose gateway has gone quiet since the tunnel was built still satisfies every status gate, because those gates inspect the tunnel and never its peers. Ranked preference costs nothing and cannot reject a working partner, because every tier falls through to the existing behaviour.
Preference order, best first:
- tested GOOD and carrying recent real traffic — the only tier that proves the tunnel works end to end, since receiving data means its gateway is delivering
- tested GOOD
- carrying real traffic, status not yet known
- anything passing the scan gates (the previous behaviour)
Deliberately a preference and not a filter. A transport-level reachability probe on the gateway was considered and rejected: the gateway is a peer we may hold no session with, or an established but idle one, so treating "not established" as disqualifying would discard good partners and could make the round trip worse.
- Returns:
- the chosen tunnel, or null if none passes the scan gates
partnerPreferenceTier
Rank a candidate test partner; lower is better. SeeselectTunnelForTest()for why this is a preference.- Parameters:
info- a tunnel that already passed the scan gates- Returns:
- the preference tier, 0 best
passesScanGates
Whether a pooled tunnel passes the scan eligibility gates: not failed, not over the consecutive-test-failure cap (test failures are often reply-path problems, not tunnel quality), non-expired, and — on the first pass. Longer than a single hop. Pure decision. No side effects.- Parameters:
info- the candidate tunnelnow- current time in mslongTunnelsOnly- first-pass mode (zero-hop-avoiding pools): long tunnels only- Returns:
- whether the tunnel is a scan candidate
classifyPoolState
static TunnelPool.PoolState classifyPoolState(List<TunnelInfo> tunnels, long now, java.util.function.Predicate<TunnelInfo> backlogged) Count the pool by selection outcome. Pure: no lock, no clock, no logging.A tunnel is counted as usable when it clears the same gates
passesScanGates(TunnelInfo, long, boolean)applies and is not set aside, so a pool reportingusable=0against a non-zero pool count has tunnels it is refusing to use -- which is the case worth investigating.- Parameters:
tunnels- the pool's tunnels, not necessarily lockednow- current time in msbacklogged- tests whether a tunnel's next peer is backlogged- Returns:
- the classified counts
selectTunnel
Return the tunnel from the pool that is XOR-closest to the target. By using this instead of the randomselectTunnel(), we force some locality in OBEP-IBGW connections to minimize those connections network-wide.Does not check for backlogged next peer. Does not return an expired tunnel.
- Parameters:
closestTo- the hash to find the closest tunnel to- Returns:
- the tunnel closest to the target, or null on failure
getTunnel
Tunnel by its gateway tunnel ID.- Parameters:
gatewayId- for inbound, the GW rcv tunnel ID; for outbound, the GW send tunnel ID.- Returns:
- the tunnel with the matching gateway ID, or null if not found
listTunnels
List of tunnels currently in the pool.- Returns:
- a copy of the list of TunnelInfo objects
needFallback
boolean needFallback()Do we really need more fallbacks? Used to prevent a zillion of them. Does not check config, only call if config allows zero hop.- Returns:
- true if more fallback tunnels are needed
hasZeroHopFallback
boolean hasZeroHopFallback()Whether any active tunnel in this pool is a zero-hop fallback while the pool is configured for multi-hop. Such tunnels are bootstrap placeholders: they provide no anonymity and must be replaced as fast as possible, so the executor treats their presence as an emergency that bypasses build budget caps.- Returns:
- true if a length-1 tunnel exists in this pool
countZeroHopFallbacks
int countZeroHopFallbacks()Count the zero-hop fallback tunnels in this pool.- Returns:
- number of length-1 tunnels
listPending
List of tunnelInfo instances of tunnels currently being built.- Returns:
- the list of tunnels currently being built
getUsableTunnelCount
int getUsableTunnelCount()Count tunnels that are usable for routing — not expired, not failed, not marked FAILING. Used by collapse detection and the EMERGENCY balance check so zombie tunnels don't skew the comparison. Does not exclude near-expiry tunnels; callers that care about lease viability use their own windows.- Returns:
- the number of usable tunnels
getHealthyTunnelCount
int getHealthyTunnelCount()Usable tunnels that are not soft-degraded: same filters asgetUsableTunnelCount()plus a floor onTunnelInfo.getSoftFailures(). Soft-degraded tunnels may still pass tests while failing every data-phase send, so counting them as healthy would hold the ensure gate at the long throttle while the pool cannot carry traffic.Also excludes tunnels whose next hop is backlogged. A backlogged peer is congested, not healthy: it can pass tests and still time out every data-phase send, which is exactly the condition that produced 128 consecutive "All tunnels are backlogged" events while the pool reported itself full and the ensure gate therefore never built.
- Returns:
- the number of healthy (non soft-degraded, non backlogged) usable tunnels
classifySessionLoss
static TunnelPool.SessionLoss classifySessionLoss(boolean hadSessionAtBuild, boolean establishedNow, boolean peerReachable) Attribute a lost local transport session to our side or the peer's.A zombie proves only that a session which existed at build time is gone now. It does not say why, and the two causes point at completely different fixes: our side closing idle sessions is a policy question, while a dead peer is not. This separates them without the transport needing to know anything about tunnels - the pool asks whether the peer would still answer an establishment.
Returns UNKNOWN whenever the inputs cannot support a conclusion, because attributing a loss wrongly sends the next investigation in the wrong direction, which is worse than not classifying it.
- Parameters:
hadSessionAtBuild- true when a session to the local hop existed when the tunnel was builtestablishedNow- true when a session to that hop exists nowpeerReachable- true when the peer would still answer an establishment- Returns:
- the attribution, or UNKNOWN when it cannot be determined
shouldDropForLostSession
static boolean shouldDropForLostSession(long firstMissingAt, long now, long hysteresis) Whether a tunnel whose local session is gone should be dropped from the pool.Excluding a zombie from the health counts is not enough: it stays in the pool, occupying a slot that carries nothing, until the normal pruning walk happens to remove it. Only tunnels never stamped with a first-seen-session timestamp are kept, since there is no evidence of how long the session has been missing and dropping them would be a guess.
- Parameters:
firstMissingAt- when the session was first seen gone, in ms, or 0 if never recordednow- current time in mshysteresis- how long the session must stay missing before the drop- Returns:
- true when the tunnel should be removed
stampLostSession
static long stampLostSession(long currentMissingAt, long now) The value to record for a tunnel whose local transport session has just been observed missing. Pure decision helper for unit tests.The existing timestamp survives every later pass. Resetting it would restart the hysteresis clock each time a tunnel was looked at, so a session that never came back would never accumulate enough duration to be dropped — the opposite of what the drop exists to do.
- Parameters:
currentMissingAt- the timestamp already recorded, or 0 when this is the first pass to noticenow- current time in ms- Returns:
- the timestamp to store
zombieTransportBucket
Normalise a RouterInfo transport style into one of the known transports.A zombie is a tunnel whose transport session died after the build completed, so peer selection is not the cause and cannot fix it: the candidate had a session when the build succeeded. What does point at the cause is which transport lost that session, and RouterInfo carries the style as a free-form string, so the values published into the netdb are folded here before they are used as a stat name.
- Parameters:
style- the style from the peer's RouterAddress, or null when the peer has no address we can read- Returns:
- NTCP, NTCP2, SSU, SSU2, or unknown
isSoftDegraded
Whether the tunnel is soft-degraded for ensure/deficit purposes: still in the pool and not FAILING, but soft failures have reachedSOFT_DEGRADED_FOR_ENSURE.- Parameters:
t- tunnel to classify; may be null (not degraded)- Returns:
- true when soft failures indicate the tunnel cannot be trusted to carry data even though tests may still pass
requestLeaseSurge
public void requestLeaseSurge(int count) Note how many advertiseable tunnels the imminent mint needs, sopruneExcessTunnels()can offer up the ones that are no longer advertiseable until the pool has enough. Not a build target -- see_leaseSurgeNeed.Monotonic: a later request never lowers the requirement, so a second mint asking for less cannot relax pruning that an earlier one still needs.
- Parameters:
count- how many advertiseable tunnels the imminent mint needs
clearLeaseSurge
public void clearLeaseSurge()Drop any lease-material prune override. Called once the mint is published or its window passes, so the pool returns to plain active-count upkeep.getLeaseSurgeNeed
int getLeaseSurgeNeed()The advertiseable count an in-force surge needs, or 0 when none is.- Returns:
- the number of additional tunnels the surge should build
getEffectiveTarget
int getEffectiveTarget()The tunnel count the pool builds and keeps: the ordinary active-count target, unaffected by any lease surge. The surge influences supply through pruning, not by moving this.- Returns:
- the number of tunnels to build and keep
getSettings
setSettings
Update the settings for this pool.- Parameters:
settings- the new settings, may be null
setPairedPool
Paired pool (inbound <-> outbound for same destination).- Parameters:
pool- the paired pool
getPairedPool
TunnelPool getPairedPool()Paired pool (inbound <-> outbound for same destination).- Returns:
- the paired pool, or null
isAlive
public boolean isAlive()Is this pool running AND either exploratory, or tracked by the client manager? A pool will be alive but not tracked after the client manager removes it but before all the tunnels have expired.- Returns:
- true if the pool is alive and should be tracked
size
public int size()Count every tunnel in the pool, including failed and expired ones awaiting cleanup.- Returns:
- the number of tunnels in the pool
getValidTunnelCount
public int getValidTunnelCount()Count valid (non-failed, not expired) tunnels in the pool. Includes untested and testing tunnels — suitable for exploratory pools and build management.- Returns:
- the number of valid tunnels
getActiveTunnelCount
public int getActiveTunnelCount()Count GOOD and TESTING (non-failed, not expired) tunnels in the pool. Excludes untested, failing, and failed tunnels. Includes TESTING tunnels so the pool doesn't build unnecessarily while tunnels await test results. Suitable for LeaseSet publication and UI "ready" indicators.- Returns:
- the number of active tunnels
getTestingTunnelCount
public int getTestingTunnelCount()Count tunnels that have been built but not yet passed their first test. Excludes previously-GOOD (FAILING), FAILED, and definitely-failed tunnels. These are treated as "in progress" for replacement accounting and UI display.- Returns:
- the number of testing tunnels
getInProgressCount
public int getInProgressCount()Count tunnels currently being built.- Returns:
- the number of in-progress builds
removeInProgress
Remove a config that was never actually sent to the network (e.g. a build skipped because the first-hop peer already had a build in flight). Unlike buildComplete(), no result accounting, peer cooldowns, or profile updates occur. The pool's deficit logic will simply request the tunnel again later.- Parameters:
cfg- the config to remove from the in-progress list
cancelExcessInProgress
Cancel excess in-progress tunnel builds to stay within budget. Cancels the newest builds first (less time invested).- Parameters:
maxAllowed- the maximum number of in-progress builds allowed- Returns:
- list of cancelled tunnel configs (BuildExecutor should remove from _currentlyBuildingMap)
pruneExcessTunnels
public int pruneExcessTunnels()Prune excess tunnels from the pool to stay within budget. UsesExpireJob.scheduleExpiration(RouterContext, PooledTunnelCreatorConfig)for budget-based pruning instead of direct removal.- Returns:
- number of tunnels marked for removal
leaseSurgeCandidates
static List<TunnelInfo> leaseSurgeCandidates(List<TunnelInfo> tunnels, List<TunnelInfo> toRemove, long now) Tunnels a lease surge may schedule for early expiry: GOOD, past the lease admission floor (so they can no longer be advertised), not already being pruned, and not recently active.Split out from
pruneForLeaseSurge(List, int, long)as a pure function because this is the decision that can remove a tunnel from the pool, and it needs to be testable without standing up a populated pool. Soonest-expiry-first is applied by the caller, not here.- Parameters:
tunnels- the pool's tunnelstoRemove- already-scheduled prunes, excludednow- current time in ms- Returns:
- prunable tunnels, in pool order
countLeaseViableTunnels
How many tunnels could still be advertised right now, on the same barselectLeaseTunnelsapplies, matching the wording of the sibling prune path.- Parameters:
tunnels- the pool's tunnelstoRemove- already-scheduled prunes, excludednow- current time in ms- Returns:
- number of tunnels with lease admission remaining
getReplacementTunnelBuffer
static int getReplacementTunnelBuffer(double buildSuccess) How many usable tunnels above the target the pool may hold before a further build must replace a non-GOOD tunnel instead of being added.A healthy pool keeps a 2-tunnel buffer (target + 2) so a tunnel lost to expiry or test failure still leaves a full target's worth of usable leases while replacements build. Under tunnel stress (build success below
ProfileOrganizer.ATTACK_THRESHOLD) the buffer drops to 1 (target + 1): replacement builds trigger sooner, so the pool regains coverage instead of hoarding near-dead tunnels, and the buffer restores itself once build success recovers.A NaN rate (no data yet, e.g. early startup) is treated as healthy — missing stats are not evidence of an attack, and the pool must not shrink its buffer before it has any statistics to act on.
- Parameters:
buildSuccess- the global build success rate as a fraction [0.0, 1.0]; NaN when the rate is not yet available- Returns:
- 2 when healthy, 1 when stressed
isStruggling
public boolean isStruggling()Whether this pool is struggling to meet its tunnel targets. Returns true if this pool can't meet its published LeaseSet target or has fewer active tunnels than the configured quantity. Used byClientPeerSelectorto relax the cross-tunnel peer diversity constraint when the pool is starved for build candidates.- Returns:
- true if the pool is struggling
addTunnel
Add a tunnel to the pool.- Parameters:
info- the tunnel to add
removeTunnel
Remove a tunnel from the pool.- Parameters:
info- the tunnel to remove
removeTunnelSynchronous
Synchronous tunnel removal for use during recovery or critical situations. This removes the tunnel and ensures a new LeaseSet is published BEFORE returning, preventing client connection failures during recovery.- Parameters:
info- tunnel to remove- Returns:
- true if removed and LeaseSet republished, false otherwise
isTrafficProvenUntestedLeaseCandidate
Whether an inbound UNTESTED tunnel has verified delivering real data recently enough to be published as a lease. Inbound delivery through InboundEndpointProcessor is end-to-end proof the tunnel works, so it is safe to publish even though it never ran an explicit TestJob. The traffic must be fresh (withinTestJob.TRAFFIC_PROOF_MS) and backed by verified bytes, not merely recent activity.- Parameters:
tunnel- the tunnel to inspect (must be inbound)expireAfter- don't accept tunnels that near expiry so the LeaseSet can propagate before the lease lapsesnow- wall-clock timestamp for the freshness check- Returns:
- true if the tunnel is eligible to publish as a traffic-proven lease
buildLeases
Assemble the Lease objects for the selected tunnels: the single zero-hop lease (if any), then the quality-sorted GOOD tunnels.Package-visible so the assembly can be exercised directly: the guarantee that matters here is that every tunnel offered yields a lease, and that is a property of this method rather than of the tunnels the router happened to build when the test ran.
- Parameters:
goodTunnels- the tunnels to advertise, best firstzeroHopTunnel- the zero-hop tunnel if one is in use, else null- Returns:
- the leases, ordered by the pool's lease comparator
isMateriallyShort
static boolean isMateriallyShort(int leases, int wanted) Whether a published LeaseSet is too small to count as a working set.Half the target is the same line as the pool's pre-emergency threshold (
PRE_EMERGENCY_FRACTION), so the two signals agree on which tier matters instead of one covering everything and the other covering half of it.- Parameters:
leases- number of leases actually assembledwanted- the target the pool was trying to reach- Returns:
- true if the set is below half of target
usableLeaseMinimum
static int usableLeaseMinimum(int wanted) The smallest LeaseSet treated as usable for a given target.- Parameters:
wanted- the target the pool was trying to reach- Returns:
- the half-target floor, rounded up
tunnelFailed
Remove tunnel and blame all peers (not necessarily equally). This may be called multiple times fromTestJob. Enhanced with intelligent failure analysis and recovery.- Parameters:
cfg- the tunnel that failed
tunnelFailedWithoutBlame
Mark a tunnel failed without blaming any peer.Called from
PooledTunnelCreatorConfig.tunnelFailedFirstHop()when a data-phase first-hop send-failure streak reaches the fatal threshold. The first-hop send-failure path records spaced, decayed send failures that are overwhelmingly local congestion (expired-on-queue, CoDel drops, no-bid replies), not a dead peer. Blaming peers here accumulates ~50 points per failure on each hop of a 3-hop tunnel — 8 consecutive failures banlists the first hop, the tunnel is removed, the pool collapses to zero usable, and the cycle repeats on the next generation. No peer is proven guilty by a send that never left us.- Parameters:
cfg- the tunnel that failed
tunnelFailed
Remove the tunnel and blame only one peer. This may be called multiple times. Data-phase first-hop send failures also feed the selection cooldown and the tier-demotion strike tracker so a persistently failing first hop is not re-selected on the next tunnel generation.- Parameters:
cfg- the tunnel that failedblamePeer- the peer to blame
reportSendFailure
Report a data-phase dispatch failure for a tunnel in this pool. Called from the I2CP dispatch path (OutboundClientMessageOneShotJob) for hard outbound-dispatch failures (status 7, 8, 9, 13) and soft send timeouts (status 3). Hard failures share the cumulative counter with TestJob and are marked once it exceedsTunnelCreatorConfig.MAX_CONSECUTIVE_TEST_FAILURES. Soft timeouts increment only the soft counter so congestion cannot trip getTunnelFailed()/passesScanGates at the hard bar; they are marked only aboveSOFT_REMOVAL_THRESHOLD. Either way the tunnel is retained in the pool rather than dropped — seefailWithCount(TunnelInfo, int).- Parameters:
cfg- the tunnel that carried the failed messagestatus- the I2CP MessageStatusMessage failure code
shouldBypassEnsureThrottle
static boolean shouldBypassEnsureThrottle(boolean soft, int failures, boolean overBar) Whether a data-phase failure signal is strong enough to run the ensure body immediately instead of through the throttle gate. Pure decision helper: hard failures and bar-crossing always change pool composition — the tunnel is marked and a replacement starts building. A soft timeout only does so at the degraded-bar crossing, ahead of the marking bar. Below that bar a status-3 flood is congestion, not evidence that the pool needs rebuilding now.- Parameters:
soft- true when the reported status was a soft send timeoutfailures- the failure count just recordedoverBar- true if this failure crossed the tunnel's removal bar- Returns:
- true to bypass the ensure throttle
isSoftSendFailure
static boolean isSoftSendFailure(int status) Whether the status is a soft best-effort send timeout (3). Mirrors OutboundClientMessageOneShotJob.isSoftSendFailure so the pool can pick the higher removal bar without importing the job class.- Parameters:
status- the I2CP MessageStatusMessage failure code- Returns:
- true for soft send timeouts only
exceedsRemovalThreshold
static boolean exceedsRemovalThreshold(int failures) Whether cumulative data-phase and TestJob failures exceed the removal threshold. Hard failures remove when the count is strictly greater thanTunnelCreatorConfig.MAX_CONSECUTIVE_TEST_FAILURES; soft send timeouts remove only aboveSOFT_REMOVAL_THRESHOLD.- Parameters:
failures- hard (test+data) or soft failure count- Returns:
- true if the tunnel should be removed from the pool
exceedsRemovalThreshold
static boolean exceedsRemovalThreshold(int failures, boolean soft) Whether the given failure class exceeds its marking bar.- Parameters:
failures- hard (test+data) or soft failure countsoft- true when the reporting status was a soft send timeout- Returns:
- true if the tunnel should be marked and retained rather than left selectable
exceedsSoftRemovalThreshold
public static boolean exceedsSoftRemovalThreshold(int failures) Whether a soft failure count crosses the soft marking bar. Exposed so a test can assert that a saturated soft streak still condemns, without duplicating the threshold.- Parameters:
failures- soft failure count- Returns:
- true if the tunnel should be marked and retained
softRemovalThreshold
public static int softRemovalThreshold()The soft failure count that marks a tunnel for retention. Exposed so a test can assert that the soft streak ceiling sits above the bar it exists to cross.- Returns:
- the soft removal bar
alreadyCondemned
Whether a failure report is a repeat of a condemnation already made.A condemned tunnel is retained until it expires and is replaced immediately, so it keeps receiving work and keeps reporting failures for its whole remaining lifetime. Re-processing those reports only re-logs, asks the pool for a replacement it already has, and lets the failure counter of a retained tunnel climb without bound.
The test is "would
fail()change anything", not "is this the terminal state". A soft-only cascade marks the tunnel FAILING and leaves it there; requiring the hard counter to be tripped as well meant a retained-but-soft-failing tunnel was re-condemned on every subsequent status-3 timeout, and the observed effect was one WARN per failure with the count running past a thousand on a single tunnel inside an hour.The one case that must still proceed is the escalation from a soft-only FAILING tunnel to a hard FAILED one, because that is the report which actually removes the tunnel from selection.
- Parameters:
isDead- whether the tunnel's hard failure counter is trippedstatus- the tunnel's current test status, may be null- Returns:
- true when this report adds nothing
forceTunnelFailure
Force a tunnel to fail immediately and trigger a replacement build. Used when rotation is saturated and the pool has no viable alternative tunnels. This bypasses the incremental failure counter and marks the tunnel conclusively dead, so the pool builds a replacement without waiting for multiple failure reports.- Parameters:
cfg- the tunnel to force-fail
refreshLeaseSet
void refreshLeaseSet()No-op for outbound and exploratory pools.proactiveRepublishIfHealthy
void proactiveRepublishIfHealthy()Proactively republish the LeaseSet when all tunnels are healthy (all > 5 min expiry). This ensures the LeaseSet always has fresh lifetime and prevents orphaned LeaseSets where the LeaseSet is technically valid but all leases have expired.Unlike
refreshLeaseSet()which republishes when tunnels are EXPIRING SOON, this republishes when tunnels are ALL HEALTHY to reset the LeaseSet lifetime.Called for every alive pool on every build loop iteration, so the cheap checks (throttle timestamp, then usable count) run before anything that takes the pool-wide tunnel lock.
refreshLeaseSet
public void refreshLeaseSet(boolean force) Refresh the LeaseSet, throttled to prevent flooding but not on initial creation. Public so the netdb facade can nudge local-dest refresh on accessLeaseSet.- Parameters:
force- if true, bypass throttle (for critical refresh when below minimum or near expiry)
shouldDeferMissingPublished
static boolean shouldDeferMissingPublished(long lastRefreshTime, long now, long throttleMs) Pure defer decision when NetDB has no local LeaseSet: true when a publish is still within the throttle window (async store pending), so forcing would only re-request the same data.- Parameters:
lastRefreshTime- last local publish/refresh timestamp (ms)now- current router time (ms)throttleMs- refresh throttle window (ms)- Returns:
- true if the refresh should be deferred despite a null lookup
shouldSkipAsyncPublish
static boolean shouldSkipAsyncPublish(boolean hasPublishedLS, long lastRefreshTime, long now, long throttleMs) Pure skip decision for publishLeaseSetIfNeeded: NetDB still null but we published within the throttle window — treat as in-flight rather than missing, so tunnel-add does not re-request every build.- Parameters:
hasPublishedLS- whether lookupLeaseSetLocally returned a setlastRefreshTime- last local publish/refresh timestamp (ms)now- current router time (ms)throttleMs- refresh throttle window (ms)- Returns:
- true to skip the redundant requestLeaseSet
shouldDeferPublishedRefresh
static boolean shouldDeferPublishedRefresh(long earliestLeaseDate, long now, long throttleMs) Pure earliest-keyed defer decision: the published LeaseSet still has full capacity (every remaining lease outlasts the throttle window), so this refresh may be deferred without losing reachability.- Parameters:
earliestLeaseDate- earliest lease end of the published LeaseSet (ms)now- current router time (ms)throttleMs- refresh throttle window (ms)- Returns:
- true if the refresh may be deferred
isExpiringSoon
boolean isExpiringSoon(long now) Check if the LeaseSet is expiring soon (within 1 minute).- Parameters:
now- milliseconds since the epoch, fromClock.now()- Returns:
- true if refresh should be forced
getInboundTunnelsAsLeaseSet
Build and return current LeaseSet from our tunnels. Used byTunnelPoolManagerto validate NetDB has current data and by RepublishLeaseSetJob to re-mint local-only (unpublished) LeaseSets.- Returns:
- current LeaseSet or null
notifyServerPoolTestFailed
void notifyServerPoolTestFailed()Called by TestJob when a server pool tunnel fails a test but is retained. Schedules a deferred republish to batch any additional failures within the 10s debounce window rather than publishing an emergency LeaseSet on every single failure.buildFallback
boolean buildFallback()This will build a fallback (zero-hop) tunnel ONLY if this pool is exploratory, or the settings allow it. Matches upstream: client pools with zero-hop disabled must never fall back to 0-hop, since such a tunnel would be selected for data and would fail every connection through it.- Returns:
- true if a fallback tunnel is built, false otherwise
buildBootstrapTunnels
boolean buildBootstrapTunnels()Build bootstrap tunnels for exploratory pools during cold start. Unlike buildFallback(), this builds 2-hop tunnels (not zero-hop) to provide usable reply paths for client tunnel builds. Called by BootstrapPool during startup until the pool has sufficient tunnels.- Returns:
- true if a bootstrap tunnel build was initiated, false otherwise
locked_buildNewLeaseSet
Build a leaseSet with the required tunnels that aren't about to expire. Caller must hold _tunnelsLock. The returned LeaseSet will be incomplete; it will not have the destination set and will not be signed. Only the leases will be included.- Returns:
- null on failure
getTunnelAvgLatency
Average test latency for a tunnel.- Parameters:
t- the tunnel to query- Returns:
- average test latency in ms for the tunnel, or -1 if unknown
computeLeaseEndDate
static long computeLeaseEndDate(long tunnelExpiration, long now, long maxLeaseMs) Compute the end date advertised for a lease, given the tunnel's own expiration. Pure so the lifetime/grace relationship is testable without a live tunnel: the lease must end before the gateway stops routing, and must never be advertised beyond the configured maximum.The three steps are, in order: subtract
LEASE_SAFETY_MARGINso peers re-fetch while the gateway still routes; clamp tonow + maxLeaseMsso nobody caches us for the full lifetime; and clamp up tonow + 60sso a nearly-dead tunnel still yields a usable lease rather than one already expired. That last floor is what a thin pool hits, and it is why leases were observed living ~89s: the pool had no fresher tunnel to offer, not because the lifetime was wrong.- Parameters:
tunnelExpiration- when the tunnel stops routing, msnow- current time in msmaxLeaseMs- maximum advertised lease duration from now, ms- Returns:
- the lease end date, always greater than
now
getLifetimeProcessed
public long getLifetimeProcessed()Total lifetime processed bytes for this pool.- Returns:
- the total number of bytes processed through this pool
configureNewTunnel
PooledTunnelCreatorConfig configureNewTunnel()This only sets the peers and creation/expiration times in the configuration. For the crypto, seeBuildRequestorand BuildMessageGenerator.- Returns:
- null on failure
computeDifficultySpare
static int computeDifficultySpare(int consecutiveBuildTimeouts) Spare tunnels held above target while the pool is having difficulty building. Config-free replacement for a static backupQuantity: difficulty is tracked with the existing consecutive build-timeout counter (reset on any successful or answered build), so the reserve grows only while builds are silently timing out and decays as soon as builds complete again.- Parameters:
consecutiveBuildTimeouts- consecutive silent build timeouts since the last successful or answered build- Returns:
- 0 with no sustained difficulty, 1 after
SPARE_LOW_THRESHOLDtimeouts, 2 afterSPARE_HIGH_THRESHOLD
getDifficultySpare
int getDifficultySpare()Current difficulty spare for this pool.- Returns:
- spare tunnels to hold above target, 0-2
rotateWindowStart
static long rotateWindowStart(long windowStart, long now, long windowMs) Roll the build-rate window forward when it has expired.- Parameters:
windowStart- start of the current window (ms), 0 if nonenow- current router time (ms)windowMs- window length (ms)- Returns:
- start time to use for the next count —
nowwhen the window is missing or expired, otherwise unchanged
timeoutRate
static double timeoutRate(long attempts, long timeouts) Fraction of counted build attempts that timed out.- Parameters:
attempts- build attempts in the window, may be 0timeouts- timed-out attempts in the window- Returns:
- rate clamped to [0.0, 1.0]; 0.0 when there were no attempts
computePreBuildWindowMs
static long computePreBuildWindowMs(double rate) How far ahead of expiry to start building replacements, scaled by how badly this pool's builds are failing: a pool with a high build-timeout rate needs more lead time because each attempt may burn a full timeout (17-30s) before the next one starts. Missing or invalid data (NaN) keeps the base window. Absent statistics are not evidence of failure.getNearExpiryCount
public int getNearExpiryCount()Tunnels in this pool whose lease falls inside the pre-build window.- Returns:
- count of near-expiry live tunnels as of the last sweep
isLastChanceTestable
static boolean isLastChanceTestable(long expiration, long now, long minLifeMs) Whether a tunnel expiring within the pre-build window still has enough life left for a last-chance test to mean anything.- Parameters:
expiration- tunnel expiration (router clock, ms)now- current router time (ms)minLifeMs- minimum remaining life for the test to be worthwhile- Returns:
- true when at least
minLifeMsof life remains
clearFailingOnTraffic
public void clearFailingOnTraffic()Clear FAILING/FAILED flags on tunnels that have provably carried real traffic recently. Data that actually moved through the tunnel is stronger evidence than a tunnel test, so the tunnel has not failed and must stay selectable and in the pool. A test failure on such a tunnel is a reply-path false negative.Proof is direction-neutral: InboundEndpointProcessor stamps only after the tunnel's crypto verified, and OutboundClientMessageOneShotJob's SendSuccessJob stamps only after the remote peer replied to the message. Neither dispatch nor a bare gateway accept reaches either call site, so the old inbound-only restriction no longer describes what the marker means.
FAILED (3+ consecutive failures) is dead and is NOT cleared here. A traffic stamp proves one packet got through, which is not evidence against three consecutive failed tests; only a real passing test (or a rebuild) returns a dead tunnel to service. FAILING (1-2 failures) is transient by definition and clears on a single proof. A genuinely dead tunnel stamps neither marker, so it stays FAILING/FAILED, is excluded from
getUsableTunnelCount()andgetHealthyTunnelCount()while marked, and ages out at natural expiry — retention cannot strand it as usable.UNTESTED tunnels that have delivered verified bytes are promoted to GOOD outright as well. A tunnel that received real end-to-end traffic is proven to work — waiting for a TestJob that may never be scheduled (the fresh tunnel can sit UNTESTED until expiry while the pool keeps rebuilding replacements) means the published LeaseSet stays thin even though usable tunnels are standing. Promoting proof-of-traffic UNTESTED tunnels lets LeaseSet building see them immediately. Verified-bytes is required for UNTESTED (not just recency) because such a tunnel has never passed an explicit test, so its only proof is actual data arrival.
Called from the periodic pool maintenance sweep (~15s).
isDeficitThrottled
static boolean isDeficitThrottled(long now, long lastBuild, int healthySafe, int safeActive, int inProgress, boolean incompleteLeaseSet) Whether a deficit-build batch must wait. Pure decision helper so throttle policy is unit-testable without a router context.Policy:
DEFICIT_THROTTLE_MS(30s) when the pool still has healthy usable tunnels.DEFICIT_DEGRADED_MS(10s) while the published LeaseSet is incomplete, or the pool is soft-degraded- dominated (safeActive > 0 but healthySafe == 0);DEFICIT_COLLAPSE_COOLDOWN_MS(5s) only when safeActive is truly empty, or <= 1 with nothing in flight — so recovery stays prompt but repeated fast failures cannot hammer selectSingleHop. An empty pool uses the short cooldown even with builds in flight: hung builds (17-30s timeouts) must not pin an empty pool on the 30s healthy gate. Concurrent builds stay capped by shouldSkipDueToInProgress(). A zerolastBuild(never built) is never throttled.- Parameters:
now- current router time (ms)lastBuild- last deficit-build timestamp (ms), 0 if neverhealthySafe- healthy (non soft-degraded) safe tunnel countsafeActive- all safe tunnels including soft-degradedinProgress- builds currently in flightincompleteLeaseSet- true when the pool cannot publish a full LeaseSet or is soft-degraded-dominated- Returns:
- true if the deficit build should be skipped this cycle
isDeficitThrottled
static boolean isDeficitThrottled(long now, long lastBuild, int safeActive, int inProgress, boolean incompleteLeaseSet) Compatibility overload: callers that only have a single usable count (no soft-degraded split) treat it as both healthy and safe.- Parameters:
now- current router time (ms)lastBuild- last deficit-build timestamp (ms), 0 if neversafeActive- all safe tunnels including soft-degradedinProgress- builds currently in flightincompleteLeaseSet- true when the pool cannot publish a full LeaseSet- Returns:
- true if the deficit build should be skipped this cycle
isEnsureThrottled
static boolean isEnsureThrottled(long now, long lastEnsure, int usableTunnelCount, boolean incompleteLeaseSet) Whether ensureSufficientTunnels() must wait for this pool. Pure decision helper for unit tests.Policy:
ENSURE_THROTTLE_MS(15s) normally;ENSURE_DEGRADED_MS(5s) while the published LeaseSet is incomplete (thin reachability), or when some usable tunnels are soft-degraded but the pool still has healthy capacity;ENSURE_COLLAPSED_MIN_MS(2s) floor when the pool is collapsed (usable tunnels <= 1) or healthy (non soft-degraded) tunnels <= 1 — a pool of soft-degraded tunnels looks full to getUsableTunnelCount() while failing every data-phase send, so the long gate would starve replacements during a half-open outage.Compatibility overload: callers that only have a single usable count treat it as both usable and healthy. See
isEnsureThrottled(long, long, int, int, boolean)for the full policy.- Parameters:
now- current router time (ms)lastEnsure- last ensure timestamp (ms), 0 if neverusableTunnelCount- current usable tunnel count (includes soft-degraded)incompleteLeaseSet- true when the pool cannot publish a full LeaseSet- Returns:
- true if ensureSufficientTunnels() should return early
isEnsureThrottled
static boolean isEnsureThrottled(long now, long lastEnsure, int usableTunnelCount, int healthyCount, boolean incompleteLeaseSet) Whether ensureSufficientTunnels() must wait for this pool, using separate usable and healthy counts. Pure decision helper.- Parameters:
now- current router time (ms)lastEnsure- last ensure timestamp (ms), 0 if neverusableTunnelCount- current usable tunnel count (includes soft-degraded)healthyCount- usable tunnels belowSOFT_DEGRADED_FOR_ENSUREincompleteLeaseSet- true when the pool cannot publish a full LeaseSet- Returns:
- true if ensureSufficientTunnels() should return early
isEnsureThrottled
static boolean isEnsureThrottled(long now, long lastEnsure, int usableTunnelCount, int healthyCount, boolean incompleteLeaseSet, long lastRemoval) Whether ensureSufficientTunnels() must wait for this pool, using separate usable and healthy counts and a removal-time signal. Pure decision helper.Policy:
ENSURE_THROTTLE_MS(15s) normally;ENSURE_DEGRADED_MS(5s) while the published LeaseSet is incomplete (thin reachability), or when some usable tunnels are soft-degraded but the pool still has healthy capacity;ENSURE_COLLAPSED_MIN_MS(2s) floor when the pool is collapsed (usable tunnels <= 1), healthy (non soft-degraded) tunnels <= 1, or a tunnel was removed since the last ensure run — a removal is a liveness signal that must not wait behind the healthy gate.- Parameters:
now- current router time (ms)lastEnsure- last ensure timestamp (ms), 0 if neverusableTunnelCount- current usable tunnel count (includes soft-degraded)healthyCount- usable tunnels belowSOFT_DEGRADED_FOR_ENSUREincompleteLeaseSet- true when the pool cannot publish a full LeaseSetlastRemoval- last tunnel-removal timestamp (ms), 0 if never- Returns:
- true if ensureSufficientTunnels() should return early
isEnsureThrottled
static boolean isEnsureThrottled(long now, long lastEnsure, int usableTunnelCount, int healthyCount, boolean incompleteLeaseSet, long lastRemoval, int inProgress) Whether ensureSufficientTunnels() must wait for this pool, using separate usable and healthy counts, a removal-time signal, and the number of builds in flight. Pure decision helper.Policy:
ENSURE_THROTTLE_MS(15s) normally;ENSURE_DEGRADED_MS(5s) while the published LeaseSet is incomplete (thin reachability), or when some usable tunnels are soft-degraded but the pool still has healthy capacity;ENSURE_COLLAPSED_MIN_MS(2s) floor when the pool is collapsed (usable tunnels <= 1), healthy (non soft-degraded) tunnels <= 1, or a tunnel was removed since the last ensure run — a removal is a liveness signal that must not wait behind the healthy gate. A fully empty pool with no builds in flight bypasses the gate entirely: there is nothing to rate-limit (the deficit and emergency throttles still pace the builds themselves), and waiting even 2s on the way to an empty pool extends the outage for no benefit.- Parameters:
now- current router time (ms)lastEnsure- last ensure timestamp (ms), 0 if neverusableTunnelCount- current usable tunnel count (includes soft-degraded)healthyCount- usable tunnels belowSOFT_DEGRADED_FOR_ENSUREincompleteLeaseSet- true when the pool cannot publish a full LeaseSetlastRemoval- last tunnel-removal timestamp (ms), 0 if neverinProgress- builds currently in flight, or -1 when unknown- Returns:
- true if ensureSufficientTunnels() should return early
computeDeficit
static int computeDeficit(int healthySafe, int safeActive, int softDegraded, int failingCount, int untestedCount, int target, int effectiveTarget, int currentInProgress) Pure deficit calculation so unit tests can pin the soft-degraded occupancy rules without a router context.- Parameters:
healthySafe- non soft-degraded safe tunnelssafeActive- all safe tunnels (includes soft-degraded)softDegraded- safe tunnels at or aboveSOFT_DEGRADED_FOR_ENSUREfailingCount- hard-failing tunnels (not in safeActive)untestedCount- untested tunnels counted against the deficittarget- base effective tunnel counteffectiveTarget- target plus emergency/failure boostcurrentInProgress- builds currently in flight- Returns:
- non-negative replacement count for this cycle
hasValidTunnelsBlockingEmergency
static boolean hasValidTunnelsBlockingEmergency(int validCount) Emergency collapse is deferred while valid tunnels remain (soft-degraded or FAILING-but-not-FAILED). Pure helper for unit tests.- Parameters:
validCount-getValidTunnelCount()at decision time- Returns:
- true when valid tunnels are present, so do not fire EMERGENCY
isEmergencyCooldownActive
static boolean isEmergencyCooldownActive(long elapsedMs, long usableCount, long inProgressCount) Whether the emergency-cooldown window has elapsed. Uses a shorter cooldown during total collapse: when a pool has zero usable tunnels and nothing being built, the standard cooldown lefts it stranded (the first EMERGENCY build takes ~20s to timeout, so with the standard 30s cooldown the next attempt is 50s later, during which all connections fail).- Parameters:
elapsedMs- time since the last emergency build started, in msusableCount- usable tunnel count at snapshot timeinProgressCount- in-progress build count at snapshot time- Returns:
- true if the cooldown has NOT yet elapsed, so the next emergency build must wait
ensureSufficientTunnels
void ensureSufficientTunnels()Ensure the pool has at least target valid tunnels, building replacements proactively when the count drops below target. This prevents the pool from silently draining to zero, avoiding tunnel collapse cascades.requestFreshTunnelBuild
public void requestFreshTunnelBuild()Request new inbound tunnels so the pool holds the target count with at leastgetLeaseViabilityWindow(RouterContext)remaining. Called by the LeaseSet republisher before a re-mint: the normal build logic replaces tunnels only when below target or inside its proactive replacement window (BuildExecutor starts 330s out), so a pool at target with aging leases never rebuilds on its own, and a re-mint would re-sign the same near-expired leases. Respects the in-progress cap, the per-period dedup guard and pool backoff to avoid build storms.configureNewTunnel
Configure a new tunnel, optionally excluding peers from first-hop selection. The exclusion set is used by the build executor's dispatch loop to ensure concurrent builds in the same batch target diverse first-hop peers — stacking multiple build requests on one peer floods it and makes the first build answer slower.- Parameters:
forceZeroHop- true to force a zero-hop tunnelexcludeFirstHops- first-hop peers already selected by concurrent builds in the same dispatch batch- Returns:
- the tunnel config, or null if none could be configured
maxExpirationStagger
static long maxExpirationStagger(long lifetimeMs) Upper bound for the expiration stagger: keeps lifetime plus stagger under the NetDb's 15-minute future-lease rejection boundary with a 15-second margin, and never exceeds 4 minutes.- Parameters:
lifetimeMs- the configured tunnel lifetime in ms- Returns:
- the maximum stagger in ms, 0 when the lifetime alone reaches the boundary
shouldWarnSelectPeersFailure
static boolean shouldWarnSelectPeersFailure(long uptime, long now, long lastWarnTime) The selectPeers-failure WARN is rate-limited: never beforeSELECT_PEERS_FAILURE_SILENCE_MSof uptime, then at most once perSELECT_PEERS_FAILURE_WARN_INTERVAL_MSper pool.- Parameters:
uptime- the router uptime in msnow- the current time in mslastWarnTime- the last WARN time in ms- Returns:
- true if the WARN should fire
shouldLogSendFailure
static boolean shouldLogSendFailure(long now, long lastLogTime) Whether a data-phase failure or condemnation line should be logged now. Rate-limited per pool to one line perSEND_FAILURE_LOG_INTERVAL_MS. State changes are never rate limited:failWithCount(TunnelInfo, int)andtellProfileFailed(TunnelInfo)still run on every report, only the log line is suppressed.- Parameters:
now- the current time in mslastLogTime- the last log time for this stream, ms- Returns:
- true if the line should be logged
buildComplete
Remove from the _inprogress list and call addTunnel() if result is SUCCESS. Updates consecutive build timeout count.- Parameters:
cfg- the completed tunnel configurationresult- the build result
getConsecutiveBuildTimeouts
int getConsecutiveBuildTimeouts()Count of consecutive tunnel build timeouts.- Returns:
- the number of consecutive build timeouts
incrementBuildTimeout
void incrementBuildTimeout()Increment consecutive build timeout counter. Called byBuildExecutorfor first-hop failures (OTHER_FAILURE with buildTime >= 1000ms) so the pool's build-health checks can respond to sustained first-hop failures.toString
formatPoolIdentity
Format a pool identity for log messages, combining nickname and truncated hash. Format: "nickname/hash" if nickname set, or just "hash" if not. Globally available helper for consistent log formatting across all tunnel pool code.- Parameters:
settings- the pool settings- Returns:
- formatted identity string