- All Implemented Interfaces:
Job
Unreachable (U-cap) peers publish SSU2 introducers as router hashes (the "ih0".."ih4" options on their SSU/SSU2 addresses). When we need an inbound connection from such a peer - or need to reach one ourselves - EstablishmentManager looks up each introducer RI on demand; if the RI is not cached locally, establishment stalls while the search runs, and IntroductionManager must fall back to ad-hoc direct queries. This job reviews the introducers of known unreachable peers on a slow schedule and issues remote RouterInfo lookups for any introducer missing locally, so hole-punching has its prerequisites ready before a connection is attempted.
Pacing and politeness toward floodfills: a randomized 3-7 minute cycle whose query phase is capped at 30s wall time per cycle (no per-cycle count cap), at most MAX_CONCURRENT_LOOKUPS searches in flight, per-introducer exponential backoff after failures, and a dedup/backoff map shared across cycles (which also gates keys already covered by an overlapping search elsewhere). Each search inherits the standard adaptive RouterInfo lookup deadline used by peer lookups. Floodfill diversity is provided by the netdb selection layer itself: FloodfillPeerSelector shuffles candidates within quality tiers, skips floodfills queried in the last 60s (markQueried cooldown), and spreads same-/16 IP clusters, so repeated keys don't repeatedly land on the same floodfills and no single one sees enough volume to trigger throttling. Concurrent duplicate searches of the same key are additionally coalesced by IterativeSearchJob. SSU1 introducers carry no router hash (IP/port/intro-key only) and cannot be resolved by netDB lookup; they are skipped here and handled by the transport's existing IP-based introduction path.
Field Summary
FieldsModifier and TypeFieldDescriptionIntroducer hash -> {last attempt time, consecutive failures}.private final FloodfillNetworkDatabaseFacadeThe facade running the lookups.private final AtomicIntegerRemote introducer searches currently in flight.private final LogLogger for this class.private static final intPrune threshold for the attempts map.private static final longBase delay between cycles, jittered ±CYCLE_JITTER before each requeue.private static final longRandom spread added to or subtracted from the cycle delay, up to 2 minutes.private static final longWall-time budget for one cycle's query phase: lookups are issued until this window closes (no per-cycle count cap), and each lookup's deadline is clamped to the budget left at issue time, so every query of a cycle completes within ~30s of cycle start.private static final longBase backoff after a failed lookup, doubling per consecutive failure.private static final intMaximum remote searches in flight during the query phase.private static final longLongest backoff after repeated failures - 6 hours.private static final intIntroducer slots per address.private static final longRe-attempt gate while a lookup may still be in flight (fails == 0).private static final intIssue floor: IterativeSearchJob never lets the RouterInfo lookup maximum drop below 5s, so a search with less budget left is not worth issuing.private static final StringSSU2 introducer router hash option prefix ("ih0".."ih4").private static final StringIntroducer relay tag option prefix ("itag0".."itag4"); required in every slot.private static final longRandom spread of the first lookup after startup, up to 4 min.private static final longFirst run 8-12 minutes after startup: let the netdb fill from bootstrap first.Constructor Summary
ConstructorsConstructorDescriptionConstructor.Method Summary
Modifier and TypeMethodDescription(package private) static longbackoffMillis(int fails) Retry backoff for an introducer whose last lookup failedfailsconsecutive times: 0 (in flight / find pending) →MIN_RETRY_MS; 1+ →FAIL_BACKOFF_MSdoubling per failure, capped atMAX_FAIL_BACKOFF_MS.(package private) static booleancanAttempt(long now, long[] state) Gate for issuing a lookup now: true when never attempted, or when the elapsed time since the last attempt meets the state's backoff.getIntroducerHashes(Collection<RouterAddress> addresses) Extract SSU2 introducer router hashes ("ih0".."ih4") from the given addresses.getName()Descriptive name of the task.(package private) static intissueTimeout(long remainingBudgetMs, int adaptiveCapMs) Deadline for one issued lookup: the standard adaptive RouterInfo lookup cap (the same deadline peer/transit next-hop lookups get), never extended past this cycle's remaining query budget, and zero when too little budget remains to fund a useful search.(package private) static booleanWhether the peer publishes the unreachable capability ('U'), i.e.private longNext cycle delay: CYCLE_INTERVAL with uniform ±CYCLE_JITTER.(package private) static HashDecode an "ih{i}" option value into a Hash: Base64 of exactly 32 bytes, else null.private voidpruneAttempts(long now) Bound the attempts map: drop entries whose last attempt predates the maximum possible backoff window (they would failcanAttempt(long, long[])as eligible anyway and would be recreated fresh).private voidrequestLookup(RouterContext ctx, Hash h, long now, int timeoutMs) Record the attempt and issue one non-blocking remote RI lookup with the given deadline (never past this cycle's query budget, never above the standard adaptive RouterInfo lookup cap).voidrunJob()Actually perform the task.Methods inherited from class JobImpl
dropped, getContext, getJobId, getMadeReadyOn, getTiming, madeReady, requeue, toStringModifier and TypeMethodDescriptionvoiddropped()Default implementation does nothing.final RouterContextThe router context that this job belongs to.longgetJobId()The unique job ID for this job.longThe timestamp when this job was last made ready for execution.The timing object for this job.voidmadeReady(long now) For JobQueue only, not for external use.protected voidrequeue(long delayMs) Requeue this job to run after the specified delay.toString()
Field Details
_log
Logger for this class._facade
The facade running the lookups._attempts
_inFlight
Remote introducer searches currently in flight.CYCLE_INTERVAL
private static final long CYCLE_INTERVALBase delay between cycles, jittered ±CYCLE_JITTER before each requeue.- See Also:
CYCLE_JITTER
private static final long CYCLE_JITTERRandom spread added to or subtracted from the cycle delay, up to 2 minutes.- See Also:
STARTUP_DELAY_MIN
private static final long STARTUP_DELAY_MINFirst run 8-12 minutes after startup: let the netdb fill from bootstrap first.- See Also:
STARTUP_DELAY_JITTER
private static final long STARTUP_DELAY_JITTERRandom spread of the first lookup after startup, up to 4 min.- See Also:
CYCLE_QUERY_BUDGET_MS
private static final long CYCLE_QUERY_BUDGET_MSWall-time budget for one cycle's query phase: lookups are issued until this window closes (no per-cycle count cap), and each lookup's deadline is clamped to the budget left at issue time, so every query of a cycle completes within ~30s of cycle start.- See Also:
MAX_CONCURRENT_LOOKUPS
private static final int MAX_CONCURRENT_LOOKUPSMaximum remote searches in flight during the query phase.- See Also:
MIN_USEFUL_TIMEOUT_MS
private static final int MIN_USEFUL_TIMEOUT_MSIssue floor: IterativeSearchJob never lets the RouterInfo lookup maximum drop below 5s, so a search with less budget left is not worth issuing.- See Also:
MIN_RETRY_MS
private static final long MIN_RETRY_MSRe-attempt gate while a lookup may still be in flight (fails == 0).- See Also:
FAIL_BACKOFF_MS
private static final long FAIL_BACKOFF_MSBase backoff after a failed lookup, doubling per consecutive failure.- See Also:
MAX_FAIL_BACKOFF_MS
private static final long MAX_FAIL_BACKOFF_MSLongest backoff after repeated failures - 6 hours.- See Also:
ATTEMPT_MAP_MAX
private static final int ATTEMPT_MAP_MAXPrune threshold for the attempts map.- See Also:
MAX_INTRO_SLOTS
private static final int MAX_INTRO_SLOTSIntroducer slots per address. Mirrors UDPAddress.MAX_INTRODUCERS, which is package-private in the transport package.- See Also:
PROP_INTRO_TAG_PREFIX
Introducer relay tag option prefix ("itag0".."itag4"); required in every slot.- See Also:
PROP_INTRO_HASH_PREFIX
SSU2 introducer router hash option prefix ("ih0".."ih4").- See Also:
Constructor Details
IntroducerLookupJob
Constructor. Sets the staggered initial start time.- Parameters:
ctx- the router contextfacade- the floodfill network database facade
Method Details
getName
runJob
public void runJob()Description copied from interface:JobActually perform the task. This call blocks until the Job is complete.requestLookup
Record the attempt and issue one non-blocking remote RI lookup with the given deadline (never past this cycle's query budget, never above the standard adaptive RouterInfo lookup cap). Success clears the tracking entry (the RI is then cached locally and expires through the normal netdb lifecycle). Failure increments the consecutive-failure count, which lengthens the retry backoff. Both outcomes release a concurrency slot.- Parameters:
ctx- the router context for the lookup jobsh- hash of the peer whose RouterInfo is to be looked upnow- the current time in ms, recorded as the attempt timestamptimeoutMs- lookup deadline in ms for this attempt
nextInterval
Next cycle delay: CYCLE_INTERVAL with uniform ±CYCLE_JITTER.- Parameters:
ctx- the router context supplying the random source- Returns:
- the delay in ms until the next lookup cycle
pruneAttempts
private void pruneAttempts(long now) Bound the attempts map: drop entries whose last attempt predates the maximum possible backoff window (they would failcanAttempt(long, long[])as eligible anyway and would be recreated fresh).- Parameters:
now- the current time in ms, used for the age cutoff
isUnreachablePeer
Whether the peer publishes the unreachable capability ('U'), i.e. it is firewalled / cannot accept unsolicited connections and therefore relies on introducers for inbound connectivity.- Parameters:
ri- the peer's RouterInfo (non-null)- Returns:
- true if the capabilities string contains 'U'
getIntroducerHashes
Extract SSU2 introducer router hashes ("ih0".."ih4") from the given addresses. An introducer slot exists only if its required relay tag option ("itag{i}") is present; SSU1-only slots (ihost/iport/ikey with no "ih") carry no hash and yield nothing. Duplicates removed. Pure decision — no context access, safe for unit tests.- Parameters:
addresses- the peer's transport addresses, may be null or empty- Returns:
- the introducer hashes, never null
parseIntroducerHash
backoffMillis
static long backoffMillis(int fails) Retry backoff for an introducer whose last lookup failedfailsconsecutive times: 0 (in flight / find pending) →MIN_RETRY_MS; 1+ →FAIL_BACKOFF_MSdoubling per failure, capped atMAX_FAIL_BACKOFF_MS. Pure decision. Safe for unit tests.- Parameters:
fails- consecutive failed lookup attempts- Returns:
- the backoff in milliseconds
canAttempt
static boolean canAttempt(long now, long[] state) Gate for issuing a lookup now: true when never attempted, or when the elapsed time since the last attempt meets the state's backoff. Pure decision — safe for unit tests.- Parameters:
now- current time in millisecondsstate- {last attempt time, consecutive failures}, or null- Returns:
- true if a lookup may be attempted
issueTimeout
static int issueTimeout(long remainingBudgetMs, int adaptiveCapMs) Deadline for one issued lookup: the standard adaptive RouterInfo lookup cap (the same deadline peer/transit next-hop lookups get), never extended past this cycle's remaining query budget, and zero when too little budget remains to fund a useful search. Pure decision — safe for unit tests.- Parameters:
remainingBudgetMs- ms left in this cycle's query phaseadaptiveCapMs- current adaptive RouterInfo lookup deadline cap- Returns:
- timeout in ms, or 0 when no useful lookup remains