java.lang.Object
net.i2p.router.tunnel.pool.BuildHopAttribution

public class BuildHopAttributionextends Object
Attributes an expired build to the hop position that failed to answer.

Why this exists: every expired build was logged with per-hop state, but only once per minute under a global throttle, so roughly 1 in 19,000 expiries was visible. Sample counts that thin cannot answer "which hop position fails?" — 18 samples named 47 distinct peers. This class turns the same per-hop state into rate stats on every expiry, which is the only way to see the distribution rather than an anecdote.

The first observation on this router was that Hop1 (the first *remote* hop; Hop0 is ourselves) accounted for the most silent hops, which is what makes the first-hop failure cooldown the most consequential tuning parameter.

Pure and static so the stat-name derivation is unit-testable without a router; the caller supplies the already-computed classification from BuildExecutor.classifyExpiredHop(boolean, boolean, boolean, boolean, boolean, boolean).

  • Method Details

    • statName

      public static String statName(int hopIndex, String classification)
      Build the stat name for one hop position and classification.

      Shape: tunnel.buildExpired.Hop1.establishedSilent. Lower-cased and stripped of non-alphanumerics so a classification containing a space or comma still yields one legal stat name. Those come from BuildExecutor.HOP_ESTABLISHED_SILENT and friends, and a stat name containing a space would not register.

      Parameters:
      hopIndex - zero-based position in the build path; 0 is us
      classification - token from BuildExecutor.classifyExpiredHop(boolean, boolean, boolean, boolean, boolean, boolean)
      Returns:
      a stat name safe to pass to addRateData
    • isPeerFailure

      public static boolean isPeerFailure(int hopIndex, String classification)
      Whether this hop position is worth counting as a peer failure.

      Hop0 is this router and several classifications are expected states rather than failures — selfExpected on the gateway, and an unassigned hop that simply was not needed. Counting those would make the distribution say "hop 0 fails most", which is meaningless.

      Parameters:
      hopIndex - zero-based position in the build path
      classification - token from BuildExecutor.classifyExpiredHop(boolean, boolean, boolean, boolean, boolean, boolean)
      Returns:
      true if this hop should be attributed as a failure
    • isEstablishedSilent

      public static boolean isEstablishedSilent(String classification)
      Whether a classification means the peer never answered at all.

      This is the distinction that matters for the build path: an established peer that stays silent is a different problem from a peer that was never reachable, and they warrant opposite responses. Confirmed 18/18 and then 3/3 silent on two deployments, so the split is worth measuring rather than assuming.

      Parameters:
      classification - token from BuildExecutor.classifyExpiredHop(boolean, boolean, boolean, boolean, boolean, boolean)
      Returns:
      true if the hop was established but produced no reply
    • registeredClassifications

      public static String[] registeredClassifications()
      The peer-failure classifications worth registering a stat for.

      A fixed set, not the full hop-index x classification cross-product: StatManager.addRateData(String, long, long) silently drops names that were never registered, so an open-ended name generator records nothing at all. This set covers every classification BuildExecutor.classifyExpiredHop(boolean, boolean, boolean, boolean, boolean, boolean) can return for a remote hop.

      Returns:
      stat-name suffixes, in registration order
    • registeredHopIndexes

      public static int[] registeredHopIndexes()
      The hop positions worth registering a stat for.

      Hop0 is this router, so it can never be a peer failure.

      Returns:
      zero-based hop indexes, in registration order
    • normalise

      public static String normalise(String classification)
      Lower-case a classification for display or comparison.
      Parameters:
      classification - token from BuildExecutor.classifyExpiredHop(boolean, boolean, boolean, boolean, boolean, boolean)
      Returns:
      the token lower-cased, or the empty string when null