- All Implemented Interfaces:
Runnable
This class is optimized for minimal overhead and maximum event dispatch efficiency. All hot paths (e.g., processRead(SelectionKey), processWrite(SelectionKey), runDelayedEvents()) avoid synchronization, allocations, and unnecessary syscalls.
Key performance characteristics:
- Fixed 200ms selector timeout for consistent latency
- Immediate
wakeup()on pending I/O - Duplicate-free write request tracking via
ConcurrentHashSet - Spin detection: yields briefly when select() returns repeatedly without blocking
- Failsafe iteration every 2 seconds to clean stale connections
- Efficient buffer pooling with bounded size
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static enumOutcome of screening an inbound Session Request against the bans, connection limits, and flood defenses before the connection is accepted.private static classCreates byte buffers for the read buffer cache.(package private) static enumHow a thrown exception from outbound connect setup should be treated.(package private) static final classOutcome ofevaluateInboundFlood(long, int, int, long, boolean, int, int, RandomSource): whether to reject the incoming connection plus the numbers used in the warn log, so the caller stays side-effect free.private static classCreates byte arrays for the write buffer cache.(package private) static enumProgress of a single write-buffer drain step.Field Summary
FieldsModifier and TypeFieldDescriptionprivate booleanFalse once the pumper has been asked to stop.private final ObjectCounter<String> Recent connection attempts from blocked IPs.private static final TryCache<ByteBuffer> Pooled byte buffers for socket reads.private intTracks consecutive select() calls that returned immediately (tight loop guard).private final RouterContextThe router context.private static longSelector delay in effect right now.private longWhen idle write interest is next expired.private final ObjectCounter<String> Recent inbound handshake decryption failures by IP.When an outbound connection to a peer was last attempted.Failed outbound attempts per peer, for backoff.private static longHow often the failsafe pass runs, tunable.private intInbound connections in the current rate period.private intInbound connections in the previous rate period.private longWhen the current rate period began.private longWhen the inbound rate snapshot was taken.private longWhen idle connections were last expired.private longWhen the retry maps were last pruned.private final LogLogger for the pumper thread.private static intMax idle loop iterations per second before the pumper sleeps to cap CPU.private final booleanWhether TCP_NODELAY is set on connections.private SelectorThe selector watching all NTCP channels.private static longDelay between selector passes when idle.private final NTCPTransportThe transport this pumper serves.private final Queue<NTCPConnection> Outbound connections waiting for selector registration.private final Queue<NTCPConnection> The following 3 are unbounded and lockless for performance inrunDelayedEvents().private final Queue<ServerSocketChannel> Server channels waiting for selector registration.private final Set<NTCPConnection> Connections waiting for the selector to watch writes.private static final TryCache<byte[]> NTCP2 write-frame buffers, fixedWRITE_BUFSIZEclass.private static final longBlocked-IP statistics are logged every 43 minutes.private static final intThis probably doesn't need to be bigger than the largest typical message, which is a 5-slot VTBM (~2700 bytes).private static final intRatchet constant: max accept when the current rate exceeds baseline by this bias.private static final intRandom range for the probabilistic reject decision.private static final longLength of the previous period used to normalize the baseline rate.private static final longHow often the inbound rate snapshot is refreshed, one second.private static final float...and tightened when it reports saturation.private static final floatFlood threshold is relaxed when the transport still has headroom...private static final longRight after a period rolls over, the accept rate is meaningless; always allow.private static final booleanEvery few seconds, iterate across all ntcp connections just to make sure we have their interestOps set properly (and to expire any looong idle cons).private static final intMost messages written in one pass.private static final intAfter this many consecutive immediate select returns, yield briefly.private static final longLongest a connection may stay idle before being closed.private static final longSlowest allowed failsafe interval.private static final intHighest max idle loop setting.private static final intHighest minimum buffer count.private static final intMax consecutive failures before giving up on a peer entirely (until map clearance).private static final intLongest interval between pumper restart tries.private static final intMax entries before clearing retry maps to prevent unbounded growth under DoS.private static final longA disconnect notice gets a 10 second grace period.private static final intFewest buffers the pumper uses.private static final longTunnel test now disabled, but this should be long enough to allow an active tunnel to get started.private static final longFastest allowed failsafe interval.private static final intMinimum accepted rate in the previous period; never below this floor.private static final intLowest max idle loop setting.private static final intLowest minimum buffer count.private static final longA failed peer is retried no sooner than 500 ms.private static final StringDo we use direct buffers for reading? Default false.private static final long[]Rate stat periods for inbound connection rates.private static final floatMultiplier applied to the retry wait per failure.private static final longThe retry maps are pruned every 5 minutes.private static final longThe router info is re-stored every 29 minutes.private static final longMax delay when idle — must stay low for client responsiveness.private static final Set<CommSystemFacade.Status> Reachability statuses that allow inbound connections.private static final booleanWhether socket buffers use direct byte buffers.private static final intFixed size class for NTCP2 data-phase write frames.Constructor Summary
ConstructorsConstructorDescriptionEventPumper(RouterContext ctx, NTCPTransport transport) Create the pump for a transport, registering the rate stats it writes.Method Summary
Modifier and TypeMethodDescriptionstatic ByteBufferHigh-frequency path in thread.static byte[]Acquire a fixed-size NTCP2 frame buffer for the next data-phase frame.private voidadjustExpireIdleWriteTime(boolean haveCap) Adjust the idle-expire window based on connection capacity so the router keeps fewer idle connections when resources are tight.voidblockIP(byte[] ip) Record the given IP as blocked.(package private) static EventPumper.ConnectErrorKindClassify an exception thrown during outbound connect setup.private voidClose every registered channel (NTCP connections and the server socket) and the selector itself on pumper shutdown.static voidclearInterest(SelectionKey key, int op) Clear the given interest operation on the selection key.(package private) static booleancloseIdleOrSendRouterInfo(NTCPConnection con, long now, long expire, long failsafeIterationFreq) Close a connection that has exceeded its idle timeout, or send our RouterInfo when the connection has been established long enough that periodic re-announce is due (uptime in the RI_STORE_INTERVAL band).private static StringThe bare failure reason from a failed connect: the exception message without the JDK 13+ " (connect failed)" operation suffix, or the exception class name when there is no message.private voidcountInboundNoMessage(String ipStr, NTCPConnection con, String reason) Record an inbound connection that closed before delivering any message, feeding the per-IP attempt counter and the drop statistic.private voidPeriodic failsafe scan of all connections to: - Reassert OP_WRITE interest if data is pending - Close idle/stale connections - Send periodic RouterInfo.(package private) static intelapsedSeconds(long elapsedMs) Whole seconds in a window, floored at 1 so a clock that has not advanced cannot divide by zero and report an inflated rate.(package private) static EventPumper.InboundFloodDecisionevaluateInboundFlood(long now, int rawCurrentCount, int rawLastCount, long periodStart, boolean hasCapacity, int maxConnections, int currentConnections, RandomSource random) Decide whether an inbound NTCP connection should be probabilistically rejected because the accept rate is spiking well above the recent baseline while the router is already heavily loaded with connections.private voidCloses connections that have been idle too long.static longFailsafe iteration frequency in milliseconds.(package private) static longgetIdleExpire(NTCPConnection con, boolean haveCap, long expireIdleWriteTime) Choose the idle timeout for a single connection.longThe idle timeout for connections.static intMax idle loop rate in loops per second.static longSelector loop delay in milliseconds.private voidhandleConnectError(NTCPConnection con, IOException ioe) Handle an outbound connect that failed with an I/O error: log at debug (or a quiet warn) level, close the connection, record the peer for retry backoff, and mark it unreachable so the transport stops attempting it until it hears proof of life.private booleanhandleReadData(NTCPConnection con, SelectionKey key, ByteBuffer buf, int totalRead, boolean eof) Hand a filled read buffer to the connection, honoring the bandwidth limiter.private voidhandleReadEof(NTCPConnection con, SocketChannel chan, ByteBuffer buf) Handle end-of-stream on a read.private voidhandleReadError(NTCPConnection con, IOException ioe, boolean shouldInfo) Handle an I/O error on a read.private voidhandleReadZero(NTCPConnection con, boolean shouldDebug, boolean shouldInfo) Handle a select() wakeup with no readable bytes.booleanisAlive()Selector can take quite a while to close after callingstopPumping().(package private) static intperSecond(int count, int elapsedSeconds) Convert a raw window counter into a per-second rate.private voidAccepts one inbound connection and starts a handshake.private voidCompletes one outbound connection's handshake.private voidprocessKeys(Set<SelectionKey> selected) Handles the keys the selector reported as ready.private voidprocessOutboundConnectionRegistrations(boolean debug, boolean warn) Registers new outbound channels with the selector.private voidprocessRead(SelectionKey key) Reads from one connection until drained.private voidprocessReadRequests(boolean debug, boolean warn) Registers connections that asked to be read.private voidprocessServerSocketRegistrations(boolean debug, boolean warn) Registers new listening channels with the selector.private voidprocessWrite(SelectionKey key) Writes queued data to one connection.private booleanprocessWrite(NTCPConnection con, SelectionKey key) Try to write the queued buffers for the connection.private voidprocessWriteRequests(boolean debug, boolean warn) Registers connections that asked to be written to.(package private) static voidpublishLoopRates(java.util.function.BiConsumer<String, Long> publish, int loopCount, int idleLoopCount, int elapsedSeconds) Publish both pumper loop rates, normalised by the same window.private voidrecordFailedOutbound(RouterIdentity remote) Remember a failed outbound connect attempt for retry backoff, evicting the whole history when the cap is hit so the maps never grow past MAX_RETRY_MAP_SIZE.private voidRefresh the cached snapshot of the one-minute inbound accept rate.private voidrefuseAccepted(SocketChannel chan, EventPumper.AcceptVerdict verdict, String ba, boolean shouldWarn, boolean shouldInfo) Log and close a refused inbound connection.voidregister(ServerSocketChannel chan) Register the acceptor.voidOutbound connection registration with optional retry backoff.static voidreleaseBuf(ByteBuffer buf) Read buffer returned to the pool.(package private) static voidReturn a fully drained NTCP2 frame buffer to the pool.static voidreleaseWriteBuf(byte[] data) Return an NTCP2 frame buffer to the pool.voidrun()Run the selector event loop.private voidRuns the periodic events queued for the pumper thread.private EventPumper.AcceptVerdictscreenAccept(byte[] ip, String ba) Screen an inbound connection attempt and decide whether to register it.static voidsetFailsafeIterationFreq(long ms) Failsafe iteration frequency, bounded by MIN-MAX.static voidsetInterest(SelectionKey key, int op) Interest operations on the given selection key.static voidsetMaxIdleLps(int lps) Max idle loop rate in loops per second, bounded 1-5000.static voidsetSelectorLoopDelay(long ms) Selector loop delay, bounded 1-SELECTOR_MAX_DELAY ms.private booleanDecides whether load allows another inbound connection.private booleanDecides whether to enable TCP keep-alive on a channel.voidOpen the selector and start the pump thread.voidStop the pump thread and wake the selector.private booleanthrottleIdleLoop(long iterStartNanos) Throttle a single idle loop iteration when select() spins faster than the configured maximum (1e9 / _maxIdleLps executes per second).voidtrackFailedInboundHandshake(byte[] ip) Track failed inbound handshake (IP only, no hash).voidtrackFailedInboundHandshake(byte[] ip, Hash hash) Track failed inbound handshake for diagnostic stats.voidtrackInvalidEncryption(byte[] ip, Hash hash) Track failed inbound handshake with invalid encryption and ban if too many failures.private voidupdateLoopRateStats(int loopCountSinceLastRate, int idleLoopCountSinceLastRate, long lastLoopRateUpdate, long now) Record pumper loop statistics for each 5-second window: total loops per second and the idle-loop count used by the Tuner to derive an idle ratio.voidwantsRead(NTCPConnection con) This is only called fromNTCPConnection.complete()if there is more data, which is rare (never?) so we don't need to check for dups or make _wantsRead a Set.voidwantsWrite(NTCPConnection con) Called by the connection when it has data ready to write (after bw allocation).(package private) static EventPumper.WriteStatewriteOneBuffer(NTCPConnection con, SocketChannel chan) Write one queued buffer to the socket.
Field Details
_context
The router context._log
Logger for the pumper thread._alive
private volatile boolean _aliveFalse once the pumper has been asked to stop._selector
The selector watching all NTCP channels._wantsWrite
Connections waiting for the selector to watch writes._wantsRead
The following 3 are unbounded and lockless for performance inrunDelayedEvents()._wantsRegister
Server channels waiting for selector registration._wantsConRegister
Outbound connections waiting for selector registration._transport
The transport this pumper serves._blockedIPs
Recent connection attempts from blocked IPs._failedInboundEncryption
Recent inbound handshake decryption failures by IP._expireIdleWriteTime
private long _expireIdleWriteTimeWhen idle write interest is next expired._consecutiveFastSelects
private int _consecutiveFastSelectsTracks consecutive select() calls that returned immediately (tight loop guard).MAX_CONSECUTIVE_FAST_SELECTS
private static final int MAX_CONSECUTIVE_FAST_SELECTSAfter this many consecutive immediate select returns, yield briefly.- See Also:
USE_DIRECT
private static final boolean USE_DIRECTWhether socket buffers use direct byte buffers.- See Also:
_nodelay
private final boolean _nodelayWhether TCP_NODELAY is set on connections._failedOutboundAttempts
_failedOutboundCount
MIN_RETRY_INTERVAL
private static final long MIN_RETRY_INTERVALA failed peer is retried no sooner than 500 ms.- See Also:
MAX_RETRY_INTERVAL
private static final int MAX_RETRY_INTERVALLongest interval between pumper restart tries.- See Also:
RETRY_BACKOFF_FACTOR
private static final float RETRY_BACKOFF_FACTORMultiplier applied to the retry wait per failure.- See Also:
MAX_OUTBOUND_RETRY_COUNT
private static final int MAX_OUTBOUND_RETRY_COUNTMax consecutive failures before giving up on a peer entirely (until map clearance).- See Also:
_lastRetryMapClear
private long _lastRetryMapClearWhen the retry maps were last pruned.RETRY_MAP_CLEAR_INTERVAL
private static final long RETRY_MAP_CLEAR_INTERVALThe retry maps are pruned every 5 minutes.- See Also:
MAX_RETRY_MAP_SIZE
private static final int MAX_RETRY_MAP_SIZEMax entries before clearing retry maps to prevent unbounded growth under DoS.- See Also:
INBOUND_RATE_REFRESH_MS
private static final long INBOUND_RATE_REFRESH_MSHow often the inbound rate snapshot is refreshed, one second. The snapshot is lock-free, so the accept path does not grab the global rate monitor during a flood.- See Also:
_inboundSnapshotAt
private volatile long _inboundSnapshotAtWhen the inbound rate snapshot was taken._inboundCurrentCount
private volatile int _inboundCurrentCountInbound connections in the current rate period._inboundLastCount
private volatile int _inboundLastCountInbound connections in the previous rate period._inboundPeriodStart
private volatile long _inboundPeriodStartWhen the current rate period began.BUF_SIZE
private static final int BUF_SIZEThis probably doesn't need to be bigger than the largest typical message, which is a 5-slot VTBM (~2700 bytes). The occasional larger message can use multiple buffers.- See Also:
IS_SLOW
private static final boolean IS_SLOWEvery few seconds, iterate across all ntcp connections just to make sure we have their interestOps set properly (and to expire any looong idle cons). As the number of connections grows, we should try to make this happen less frequently (or not at all), but while the connection count is small, the time to iterate across them to check a few flags shouldn't be a problem. True if the runtime is slow enough to throttle the failsafe iteration._failsafeIterationFreq
private static volatile long _failsafeIterationFreqHow often the failsafe pass runs, tunable.MIN_FAILSAFE_FREQ
private static final long MIN_FAILSAFE_FREQFastest allowed failsafe interval.- See Also:
MAX_FAILSAFE_FREQ
private static final long MAX_FAILSAFE_FREQSlowest allowed failsafe interval.- See Also:
_maxIdleLps
private static volatile int _maxIdleLpsMax idle loop iterations per second before the pumper sleeps to cap CPU. This is the real governor for idle busy-spin: the selector timeout alone is defeated by wakeup() storms, so we enforce a minimum idle iteration time (1e9 / this) instead of the old fixed 25ms-per-2048-loops failsafe.MIN_MAX_IDLE_LPS
private static final int MIN_MAX_IDLE_LPSLowest max idle loop setting.- See Also:
MAX_MAX_IDLE_LPS
private static final int MAX_MAX_IDLE_LPSHighest max idle loop setting.- See Also:
_selectorLoopDelay
private static volatile long _selectorLoopDelayDelay between selector passes when idle.SELECTOR_MAX_DELAY
private static final long SELECTOR_MAX_DELAYMax delay when idle — must stay low for client responsiveness.- See Also:
_currentDelay
private static volatile long _currentDelaySelector delay in effect right now.BLOCKED_IP_FREQ
private static final long BLOCKED_IP_FREQBlocked-IP statistics are logged every 43 minutes.- See Also:
MIN_EXPIRE_IDLE_TIME
private static final long MIN_EXPIRE_IDLE_TIMETunnel test now disabled, but this should be long enough to allow an active tunnel to get started.- See Also:
MAX_EXPIRE_IDLE_TIME
private static final long MAX_EXPIRE_IDLE_TIMELongest a connection may stay idle before being closed.- See Also:
MAY_DISCON_TIMEOUT
private static final long MAY_DISCON_TIMEOUTA disconnect notice gets a 10 second grace period.- See Also:
RI_STORE_INTERVAL
private static final long RI_STORE_INTERVALThe router info is re-stored every 29 minutes.- See Also:
PROP_NODELAY
Do we use direct buffers for reading? Default false. NOT recommended as we don't keep good track of them so they will leak.Unsupported, set USE_DIRECT above.
- See Also:
MIN_MINB
private static final int MIN_MINBLowest minimum buffer count.MAX_MINB
private static final int MAX_MINBHighest minimum buffer count.MIN_BUFS
private static final int MIN_BUFSFewest buffers the pumper uses._bufferCache
Pooled byte buffers for socket reads.WRITE_BUFSIZE
private static final int WRITE_BUFSIZEFixed size class for NTCP2 data-phase write frames.Each frame carries up to
NTCPConnection.BUFFER_SIZEpayload bytes plus the 16-byte MAC (seeOutboundNTCP2State.MAC_SIZE) and a 2-byte length header. The buffer must be a constant size for pooling: the previous per-framenew byte[2 + framelen]allocation was variable-sized, so every frame smaller thanBUF_SIZEescaped the cache and was reallocated on each send.- See Also:
_writeBufCache
NTCP2 write-frame buffers, fixedWRITE_BUFSIZEclass.STATUS_OK
Reachability statuses that allow inbound connections.RATES
private static final long[] RATESRate stat periods for inbound connection rates.MIN_INBOUND_LAST_EVENTS
private static final int MIN_INBOUND_LAST_EVENTSMinimum accepted rate in the previous period; never below this floor.- See Also:
INBOUND_LAST_PERIOD_MS
private static final long INBOUND_LAST_PERIOD_MSLength of the previous period used to normalize the baseline rate.- See Also:
INBOUND_WARMUP_MS
private static final long INBOUND_WARMUP_MSRight after a period rolls over, the accept rate is meaningless; always allow.- See Also:
INBOUND_DROP_RANGE
private static final int INBOUND_DROP_RANGERandom range for the probabilistic reject decision.- See Also:
INBOUND_DROP_BIAS
private static final int INBOUND_DROP_BIASRatchet constant: max accept when the current rate exceeds baseline by this bias.- See Also:
INBOUND_SPARE_CAPACITY_FACTOR
private static final float INBOUND_SPARE_CAPACITY_FACTORFlood threshold is relaxed when the transport still has headroom...- See Also:
INBOUND_SATURATED_FACTOR
private static final float INBOUND_SATURATED_FACTOR...and tightened when it reports saturation.- See Also:
MAX_BATCH
private static final int MAX_BATCHMost messages written in one pass._lastExpired
private long _lastExpiredWhen idle connections were last expired.
Constructor Details
EventPumper
Create the pump for a transport, registering the rate stats it writes. No thread is started untilstartPumping().- Parameters:
ctx- the router context, used for configuration, logging and statstransport- the NTCP transport whose connections this pump drives
Method Details
acquireWriteBuf
public static byte[] acquireWriteBuf()Acquire a fixed-size NTCP2 frame buffer for the next data-phase frame. High-frequency path in the writer threads.The caller writes the frame into the returned buffer and hands it to the connection via
wantsWrite(data, 0, len). The pumper later returns it to the pool viareleaseWriteBuf(byte[])once the frame has fully drained to the socket; error paths in the producer must release it instead.- Returns:
- a byte array of length
WRITE_BUFSIZE
releaseWriteBuf
public static void releaseWriteBuf(byte[] data) Return an NTCP2 frame buffer to the pool.Only buffers of the exact
WRITE_BUFSIZEclass are accepted, so handshake and termination buffers that happen to drain through the same write path never enter this pool. The buffer is discarded, not pooled, when the cache is full.- Parameters:
data- the buffer to return, or null (no-op)
startPumping
public void startPumping()Open the selector and start the pump thread.stopPumping
public void stopPumping()Stop the pump thread and wake the selector.isAlive
public boolean isAlive()Selector can take quite a while to close after callingstopPumping().- Returns:
- true until stopPumping() is called and the selector has closed
register
Register the acceptor. This is only called fromNTCPTransport.bindAddress(int), so it isn't clear why this needs a queue.- Parameters:
chan- the listening channel to add to the selector
registerConnect
Outbound connection registration with optional retry backoff.- Parameters:
con- the finished outbound connection to add to the selector
run
throttleIdleLoop
private boolean throttleIdleLoop(long iterStartNanos) Throttle a single idle loop iteration when select() spins faster than the configured maximum (1e9 / _maxIdleLps executes per second). Selector wakeup() storms can defeat the select() timeout and busy-spin the loop. This holds each no-work iteration to a minimum duration so the real work path never sleeps.- Parameters:
iterStartNanos-System.nanoTime()taken at the start of the iteration- Returns:
trueif throttling was interrupted and the pumper must exit
perSecond
static int perSecond(int count, int elapsedSeconds) Convert a raw window counter into a per-second rate.Both pumper loop stats describe rates ("loops/s"), so both must be divided by the window length. Reporting the raw idle count instead made that series read about five times too large on the five-second window, which also made it uncomparable with the total-loop series it is meant to be read against.
- Parameters:
count- events observed across the windowelapsedSeconds- window length in seconds, coerced to at least 1- Returns:
- the per-second rate, truncated toward zero
elapsedSeconds
static int elapsedSeconds(long elapsedMs) Whole seconds in a window, floored at 1 so a clock that has not advanced cannot divide by zero and report an inflated rate.- Parameters:
elapsedMs- window length in milliseconds- Returns:
- window length in seconds, never below 1
publishLoopRates
static void publishLoopRates(java.util.function.BiConsumer<String, Long> publish, int loopCount, int idleLoopCount, int elapsedSeconds) Publish both pumper loop rates, normalised by the same window.Both stats are documented as a rate, so both are divided here rather than at the call site. Publishing them from one place is deliberate: the original defect was a raw idle count published beside a normalised total, which no test of either helper alone could catch, because the error lived in which value reached the stat manager.
- Parameters:
publish- receives the stat name and its per-second valueloopCount- total iterations observed across the windowidleLoopCount- no-work iterations observed across the windowelapsedSeconds- window length in seconds
updateLoopRateStats
private void updateLoopRateStats(int loopCountSinceLastRate, int idleLoopCountSinceLastRate, long lastLoopRateUpdate, long now) Record pumper loop statistics for each 5-second window: total loops per second and the idle-loop count used by the Tuner to derive an idle ratio. Also scales _currentDelay to curb busy-spinning: raise it quickly when the loop rate stays far above the spin threshold, lower it slowly once the rate drops. The hard idle cap lives inthrottleIdleLoop(long); this stays as a responsiveness lever.- Parameters:
loopCountSinceLastRate- total iterations in the windowidleLoopCountSinceLastRate- no-work iterations in the windowlastLoopRateUpdate- timestamp of the previous window startnow- current time in milliseconds, the end of the window
cleanupShutdown
private void cleanupShutdown()Close every registered channel (NTCP connections and the server socket) and the selector itself on pumper shutdown. Iterates a snapshot copy of the keys because closing a connection cancels its key and mutates the selector's live key set. Iterating the set directly would race with that mutation.doFailsafeCheck
private void doFailsafeCheck()Periodic failsafe scan of all connections to: - Reassert OP_WRITE interest if data is pending - Close idle/stale connections - Send periodic RouterInfo.adjustExpireIdleWriteTime
private void adjustExpireIdleWriteTime(boolean haveCap) Adjust the idle-expire window based on connection capacity so the router keeps fewer idle connections when resources are tight. Raising the window by 1s per check under capacity and lowering it by 3s under pressure lets the window drift toward the useful range without hunting.- Parameters:
haveCap-truewhen this router has spare connection capacity
getIdleExpire
Choose the idle timeout for a single connection. A barely-communicative connection that is disposable (may disconnect, hardly any traffic) gets the short MAY_DISCON_TIMEOUT so dead handshakes free capacity quickly; everything else falls back to the capacity-adjusted window.- Parameters:
con- the connection being scannedhaveCap-truewhen this router has spare connection capacityexpireIdleWriteTime- current capacity-adjusted idle window- Returns:
- the timeout in milliseconds for this connection
closeIdleOrSendRouterInfo
static boolean closeIdleOrSendRouterInfo(NTCPConnection con, long now, long expire, long failsafeIterationFreq) Close a connection that has exceeded its idle timeout, or send our RouterInfo when the connection has been established long enough that periodic re-announce is due (uptime in the RI_STORE_INTERVAL band).- Parameters:
con- the connection being scannednow- current time in millisecondsexpire- idle timeout for this connectionfailsafeIterationFreq- pumper slab interval; keeps the RI re-announce roughly once per interval per connection- Returns:
trueif the connection was idle and has been closed
processKeys
Handles the keys the selector reported as ready.- Parameters:
selected- the ready keys
wantsWrite
Called by the connection when it has data ready to write (after bw allocation). Only wakeup if new.- Parameters:
con- the connection with buffered data
wantsRead
This is only called fromNTCPConnection.complete()if there is more data, which is rare (never?) so we don't need to check for dups or make _wantsRead a Set.- Parameters:
con- the connection with data to read
acquireBuf
High-frequency path in thread.- Returns:
- a cleared buffer of BUF_SIZE capacity, direct or heap as configured
releaseBuf
Read buffer returned to the pool. These buffers must be fromacquireBuf(), i.e. capacity() == BUF_SIZE. High-frequency path in thread.- Parameters:
buf- the buffer to give back, null and undersized buffers are ignored
processAccept
Accepts one inbound connection and starts a handshake.- Parameters:
key- the server channel's selection key
screenAccept
Screen an inbound connection attempt and decide whether to register it. Blocklisted peers and refused attempts skip the flood/establishment decision entirely. The cheap hard rejections (bans, global limit) are done first so the per-IP count and the rate-based flood check run only for otherwise-eligible peers.- Parameters:
ip- raw address bytes of the peerba- canonical string form of the peer address- Returns:
- the
EventPumper.AcceptVerdictfor this attempt, never null
refuseAccepted
private void refuseAccepted(SocketChannel chan, EventPumper.AcceptVerdict verdict, String ba, boolean shouldWarn, boolean shouldInfo) Log and close a refused inbound connection. The BLOCKED and FLOOD verdicts are already logged by their detectors; only BANNED and LIMIT emit a message here so repeated refusals from the same source stay informative but quiet.- Parameters:
chan- the rejected socket, closed by this callverdict- why the connection was refused (never ACCEPT)ba- canonical string form of the peer addressshouldWarn- pre-fetched log level check for warnshouldInfo- pre-fetched log level check for info
shouldAllowInboundEstablishment
private boolean shouldAllowInboundEstablishment()Decides whether load allows another inbound connection.- Returns:
- true to accept the connection
refreshInboundRateIfStale
private void refreshInboundRateIfStale()Refresh the cached snapshot of the one-minute inbound accept rate.The expensive part — aggregating the
Ratebuckets — requires the global Rate monitor and only happens once per refresh interval; every accepted connection in between reads the four volatile fields lock-free. A connection flood therefore never contends on the stat lock.evaluateInboundFlood
static EventPumper.InboundFloodDecision evaluateInboundFlood(long now, int rawCurrentCount, int rawLastCount, long periodStart, boolean hasCapacity, int maxConnections, int currentConnections, RandomSource random) Decide whether an inbound NTCP connection should be probabilistically rejected because the accept rate is spiking well above the recent baseline while the router is already heavily loaded with connections.This is the pure decision half of
shouldAllowInboundEstablishment(). It performs no locking and takes raw counters instead of aRate, so the hot accept path never blocks on the global Rate monitor. The arithmetic is pinned byInboundFloodDecisionTestin the router test tree.- Parameters:
now- router clock time in millisecondsrawCurrentCount- events so far in the current (partial) rate periodrawLastCount- events in the most recent full rate periodperiodStart- start time of the current rate period, millisecondshasCapacity- true if the transport reports spare bandwidth, which relaxes the flood threshold vs. punishing when saturatedmaxConnections- the transport connection ceilingcurrentConnections- the live peer connection countrandom- source of randomness for the probabilistic decision- Returns:
- the decision; never null
processConnect
Completes one outbound connection's handshake.- Parameters:
key- the channel's selection key
handleConnectError
Handle an outbound connect that failed with an I/O error: log at debug (or a quiet warn) level, close the connection, record the peer for retry backoff, and mark it unreachable so the transport stops attempting it until it hears proof of life.- Parameters:
con- the connection whose connect failedioe- the failure cause
connectFailReason
The bare failure reason from a failed connect: the exception message without the JDK 13+ " (connect failed)" operation suffix, or the exception class name when there is no message.- Parameters:
e- the exception from the failed connect attempt- Returns:
- the failure reason for the close-on-timeout message, never null
recordFailedOutbound
Remember a failed outbound connect attempt for retry backoff, evicting the whole history when the cap is hit so the maps never grow past MAX_RETRY_MAP_SIZE.- Parameters:
remote- the failed peer's identity; ignored when null
shouldSetKeepAlive
Decides whether to enable TCP keep-alive on a channel.- Parameters:
chan- the channel to check- Returns:
- true to enable keep-alive
processRead
Reads from one connection until drained.- Parameters:
key- the channel's selection key
handleReadData
private boolean handleReadData(NTCPConnection con, SelectionKey key, ByteBuffer buf, int totalRead, boolean eof) Hand a filled read buffer to the connection, honoring the bandwidth limiter. When the limiter queues the read, the buffer's ownership transfers to the connection and read interest is cleared so no more data is drained while the queue is backed up.- Parameters:
con- the connection the data arrived onkey- the connection's selection key (to clear OP_READ when throttled)buf- flipped buffer containing the received bytestotalRead- bytes received in this iterationeof-trueif the socket signaled EOF after these bytes- Returns:
falseto stop the read loop (throttled, queued, or EOF)
handleReadEof
Handle end-of-stream on a read. An inbound connection that received nothing before EOF is treated as a connection-flood probe (or a dead handshake); its IP is counted and escalated to the banlist unless already blocklisted.- Parameters:
con- the connection that hit EOFchan- the connection's socket channel (for the peer address)buf- the read buffer, released by this call
handleReadZero
Handle a select() wakeup with no readable bytes. A burst of these within a one-second window is treated as a connection defect and closed. Otherwise the connection stays read-interested and the zero-read count is recorded so the Tuner can judge how much of the loop is spurious wakeups.- Parameters:
con- the connection that returned zero bytesshouldDebug- pre-fetched debug levelshouldInfo- pre-fetched info level
countInboundNoMessage
Record an inbound connection that closed before delivering any message, feeding the per-IP attempt counter and the drop statistic. Shared by the EOF and I/O-error paths so both connection-flood sources funnel into one counter.- Parameters:
ipStr- the peer address string, or null when it cannot be resolvedcon- the connection that droppedreason- short phrase logged with the event, e.g. "EOF" or "IO Error: ..."
handleReadError
Handle an I/O error on a read. An inbound connection that errored before its first message escalates its IP throughcountInboundNoMessage(String, NTCPConnection, String); the connection is recorded as a read error when established or as a failed connect otherwise (marking the peer unreachable if it was an outbound attempt).- Parameters:
con- the connection that failedioe- the failure causeshouldInfo- pre-fetched info level
processWrite
Writes queued data to one connection.- Parameters:
key- the channel's selection key
processWrite
Try to write the queued buffers for the connection.Single-writer contract: this must only be called on the pumper thread (via processWrite(SelectionKey) for OP_WRITE events). Producer threads call
wantsWrite(NTCPConnection)to register interest; they never write to the socket themselves. This keepsSocketChannel.write(ByteBuffer)strictly single-threaded per connection and removes the _writeLock serialization that previously spanned the whole drain loop (which could stall frame pushes from the writer pool and other connections).- Parameters:
con- the connection whose outbound data is drainedkey- the selection key for the OP_WRITE event being processed- Returns:
- true if OP_WRITE interest was cleared (drain done or connection closed), false if it was re-set to keep draining
writeOneBuffer
static EventPumper.WriteState writeOneBuffer(NTCPConnection con, SocketChannel chan) throws IOException Write one queued buffer to the socket. Empty head buffers are discarded and the drain continues; a zero-byte write means the socket buffer is full and the caller must keep OP_WRITE interest, unless the queue is empty and fully drained, in which case interest can be dropped.Buffers are returned to the pool once their frame is fully flushed. Only the fixed-size NTCP2 frame class re-enters the pool; smaller handshake and termination buffers are left alone. The release happens on the pumper thread immediately after
NTCPConnection.removeWriteBuf(ByteBuffer)so a frame array can never be reused while any producer still references it.- Parameters:
con- the connection being drainedchan- the connection's socket channel- Returns:
- the
EventPumper.WriteStategoverning the next step in the drain loop - Throws:
IOException- if the channel write fails
releaseDrainedWriteBuf
Return a fully drained NTCP2 frame buffer to the pool.Invoked from
writeOneBuffer(NTCPConnection, SocketChannel)on the pumper thread right after the buffer leaves the connection's write queue. Only arrays of the pooledWRITE_BUFSIZEclass are returned; everything else (handshake, termination, and legacy buffers) is ignored.- Parameters:
buf- the drained buffer, wrapped around a frame array
runDelayedEvents
private void runDelayedEvents()Runs the periodic events queued for the pumper thread.processReadRequests
private void processReadRequests(boolean debug, boolean warn) Registers connections that asked to be read.- Parameters:
debug- true for debug loggingwarn- true to log slow passes
processWriteRequests
private void processWriteRequests(boolean debug, boolean warn) Registers connections that asked to be written to.- Parameters:
debug- true for debug loggingwarn- true to log slow passes
processServerSocketRegistrations
private void processServerSocketRegistrations(boolean debug, boolean warn) Registers new listening channels with the selector.- Parameters:
debug- true for debug loggingwarn- true to log slow passes
processOutboundConnectionRegistrations
private void processOutboundConnectionRegistrations(boolean debug, boolean warn) Registers new outbound channels with the selector.- Parameters:
debug- true for debug loggingwarn- true to log slow passes
classifyConnectException
Classify an exception thrown during outbound connect setup. Genuine channel-state failures from the socket layer (already-connected, connect pending, not-yet-connected) mean the peer or the connect attempt is at fault and the peer should be marked unreachable. Anything else - local configuration misuse (blocking-mode channel), invalid arguments, or an internal defect - only tears down the local connection so an internal bug never blames (and blacks out) a healthy remote peer.Classified by exception type rather than class-name substrings: name matching silently misreads e.g. NotYetConnectedException as OTHER and blames a peer for a local IllegalBlockingModeException.
- Parameters:
e- the exception to classify (never null)- Returns:
- the
EventPumper.ConnectErrorKindfor this exception
blockIP
public void blockIP(byte[] ip) Record the given IP as blocked.- Parameters:
ip- the 4 or 16 byte address to count as blocked, null is ignored
trackFailedInboundHandshake
Track failed inbound handshake for diagnostic stats. Handshake timeouts on the open internet are network noise, not hostile, so we do not ban for them.- Parameters:
ip- source IP address (tracked for stats only)hash- optional router hash if available
trackInvalidEncryption
Track failed inbound handshake with invalid encryption and ban if too many failures. Ban reason: "Invalid encryption".- Parameters:
ip- byte array IP addresshash- optional router hash if available
trackFailedInboundHandshake
public void trackFailedInboundHandshake(byte[] ip) Track failed inbound handshake (IP only, no hash).- Parameters:
ip- the 4 or 16 byte source address, null is ignored
expireTimedOut
private void expireTimedOut()Closes connections that have been idle too long.getIdleTimeout
public long getIdleTimeout()The idle timeout for connections.- Returns:
- the idle timeout
getSelectorLoopDelay
public static long getSelectorLoopDelay()Selector loop delay in milliseconds.- Returns:
- the base delay the pumper relaxes toward
getMaxIdleLps
public static int getMaxIdleLps()Max idle loop rate in loops per second.- Returns:
- the ceiling on idle selector iterations per second
setMaxIdleLps
public static void setMaxIdleLps(int lps) Max idle loop rate in loops per second, bounded 1-5000. The pumper enforces this as a minimum idle iteration time (1e9 / rate), capping idle busy-spin even when selector wakeups defeat the timeout.- Parameters:
lps- the requested idle loop rate, clamped to 1-5000
setSelectorLoopDelay
public static void setSelectorLoopDelay(long ms) Selector loop delay, bounded 1-SELECTOR_MAX_DELAY ms. Updates the base delay the pumper relaxes toward and raises the live delay immediately so Tuner-driven increases take effect without waiting for the pumper's own 60s ramp.- Parameters:
ms- the requested base delay, clamped to 1-SELECTOR_MAX_DELAY ms
getFailsafeIterationFreq
public static long getFailsafeIterationFreq()Failsafe iteration frequency in milliseconds.- Returns:
- how often the failsafe iteration runs
setFailsafeIterationFreq
public static void setFailsafeIterationFreq(long ms) Failsafe iteration frequency, bounded by MIN-MAX.- Parameters:
ms- the requested interval, clamped to the supported range
setInterest
Interest operations on the given selection key.- Parameters:
key- the selector key to add the interest to, ignored if invalidop- the SelectionKey interest operations to add- Throws:
CancelledKeyException
clearInterest
Clear the given interest operation on the selection key.- Parameters:
key- the selector key to remove the interest from, ignored if invalidop- the SelectionKey interest operations to remove- Throws:
CancelledKeyException