Owns all reverse-DNS state that previously lived in CommSystemFacadeImpl: the size-limited in-memory IP-to-hostname cache with persistent file backup, the background lookup executor used by the non-blocking getCanonicalHostName(String) and getLocalHostName(String) lookups, and the periodic cache file writer. The facade retains thin delegation stubs so external callers are unaffected.
The cache is backed by a disk file that is safely read and written using the writer's own snapshot, so a crash mid-write cannot corrupt existing entries. Entries expire in memory after EXPIRE_TIME and are evicted from disk if older than EVICT_THRESHOLD. Cache size is bounded by memory-based limits (see maxCacheSize(boolean, boolean)) with automatic eviction of expired and then oldest entries.
Supports migration from older cache file formats missing timestamps.
Nested Class Summary
Modifier and TypeClassDescriptionstatic classCache entry for IP address and hostname mappings.private static classTimer writing the cache file periodically.Field Summary
FieldsModifier and TypeFieldDescriptionprivate final RouterContextThe router context.private static intThreads kept in the lookup pool.private ExecutorServicePool running the lookups.private final ObjectLock for the lookup pool.private final GeoIPThe GeoIP service the results feed.private static intThreads allowed in the lookup pool.private static booleanFlag set after the first normalize sweep to avoid re-processing every 15 minutes.In-flight background lookups; guards against duplicate submissions.private static intLookups since the last cache cleanup.private static TimerTimer writing the cache file.private static final LogLogger for the reverse DNS lookups.private static final StringTranslation bundle used for the localized "unknown" marker.(package private) static final intCleanup frequency once the cache is at or over its cap: a full sweep runs on every 1<<CLEANUP_BITS puts that happen while over/at the cap, instead of on every put.private static final intMask counting the periodic cache cleanups.private static final longAge beyond which a disk-cache entry is not reloaded at startup.private static final longIn-memory entry lifetime, e.g.private static final booleanTrue if the JVM has at least 1 GB.private static final booleanTrue if the JVM has at least 512 MB.private static final intEntries kept in the reverse DNS cache.(package private) static final intHow many cache entries the map may exceed its cap by before a sweep runs immediately.(package private) static final StringProperty that enables reverse lookups; the single source of truth.private static final long[]Rate-stat aggregation periods for the executor signals.private static final StringFile caching the reverse DNS results.private static final intInterval between cache file writes.private static final ConcurrentHashMap<String, ReverseDnsLookup.CacheEntry> In-memory reverse DNS cache storing IP-to-hostname mappings.private static final long"unknown" entries expire after 15 minutes — no point keeping dead lookups.Constructor Summary
ConstructorsConstructorDescriptionReverseDnsLookup(RouterContext ctx, GeoIP geoIP) Create the resolver and its background executor.Method Summary
Modifier and TypeMethodDescriptionprivate final StringLocalized string for the given key using the router console bundle.(package private) static booleanaccelerateEviction(int size, int maxSize) Whether a full cache should evict entries at an accelerated rate.voidadjustPool(int coreSize) Adjust the running rDNS executor pool sizes.(package private) static booleancacheFileEntryFresh(long ageMs, long evictThresholdMs) Whether an entry read from the persistence file is fresh enough to reuse.private static voidDrops the stale reverse DNS cache entries.static intThe number of entries in the rDNS cache.private static voidWrites the reverse DNS cache to disk.booleanWhether reverse lookups are enabled.(package private) static booleanentryExpired(long ageMs, long baseExpireMs, boolean isUnknown) Whether a cache entry has outlived its permitted lifetime.(package private) static intexpireHours(boolean has512MB, boolean has1GB) RDNS cache entry lifetime in hours, based on available memory.intReturns rDNS executor active thread count, or 0 if not running.getCanonicalHostName(String ipAddress) Canonical hostname for the given IP address from cache or DNS.getCanonicalHostNameSync(String ipAddress) The canonical host name for the given IP address, resolved synchronously.static intThe rDNS executor core pool size.private ExecutorServiceReverse DNS executor, initializing it if necessary.getLocalHostName(String ipAddress) Fast hostname lookup that never blocks on I/O (MMDB or network).static intThe rDNS executor max pool size.static intThe maximum rDNS cache size, computed from the memory available.intReturns rDNS executor total thread count, or 0 if not running.intReturns rDNS executor pending lookup count, or 0 if not running.static StringCache statistics for monitoring.doubleReturns rDNS executor utilization as a ratio (0.0-1.0), orDouble.NaNif not running.private voidlookupHostNameAsync(String ipAddress) Background RDNS/ASN resolution task submitted to the reverse-DNS executor.private voidlookupOrgNameAsync(String ipAddress) Background ASN-first resolution task submitted to the reverse-DNS executor.(package private) static intmaxCacheSize(boolean has512MB, boolean has1GB) Maximum rDNS cache size, based on available memory.(package private) static booleanneedRdnscacheSweep(int size, int maxSize, int counter) Decide whether a cache insert must run a full-cache cleanup sweep.private static voidrdnsCachePut(String ipAddress, ReverseDnsLookup.CacheEntry entry) Insert into the rdns cache with budget enforcement.static StringThe size of the rDNS cache file, in KB.(package private) static ReverseDnsLookup.CacheEntryParse a persisted line back into a cache entry.(package private) static StringSerialize a cache entry for persistence.(package private) static voidLoad the persistence file into the in-memory cache at startup.static voidsetCorePoolSize(int size) The rDNS executor core pool size, bounded 2-8.static voidsetMaxPoolSize(int size) The rDNS executor max pool size, bounded 2-8.voidshutdown()Stop the periodic cache writer and shut down the lookup executor.
Field Details
_slog
Logger for the reverse DNS lookups.RATES
private static final long[] RATESRate-stat aggregation periods for the executor signals.BUNDLE_NAME
Translation bundle used for the localized "unknown" marker.- See Also:
PROP_ENABLE_REVERSE_LOOKUPS
Property that enables reverse lookups; the single source of truth.- See Also:
RDNS_CACHE_FILE
File caching the reverse DNS results.RDNS_WRITE_INTERVAL
private static final int RDNS_WRITE_INTERVALInterval between cache file writes.- See Also:
HAS_512_MB
private static final boolean HAS_512_MBTrue if the JVM has at least 512 MB.HAS_1_GB
private static final boolean HAS_1_GBTrue if the JVM has at least 1 GB.EXPIRE_TIME
private static final long EXPIRE_TIMEIn-memory entry lifetime, e.g. 24/36/48 hours depending on memory.EVICT_THRESHOLD
private static final long EVICT_THRESHOLDAge beyond which a disk-cache entry is not reloaded at startup.- See Also:
UNKNOWN_ENTRY_EXPIRE_MS
private static final long UNKNOWN_ENTRY_EXPIRE_MS"unknown" entries expire after 15 minutes — no point keeping dead lookups.- See Also:
MAX_RDNS_CACHE_SIZE
private static final int MAX_RDNS_CACHE_SIZEEntries kept in the reverse DNS cache.MAX_SLACK
static final int MAX_SLACKHow many cache entries the map may exceed its cap by before a sweep runs immediately. Trades a few hundred surplus entries (CHM node wins are negligible) for avoiding a synchronous full-map sweep on every put.- See Also:
CLEANUP_BITS
static final int CLEANUP_BITSCleanup frequency once the cache is at or over its cap: a full sweep runs on every 1<<CLEANUP_BITS puts that happen while over/at the cap, instead of on every put.- See Also:
CLEANUP_MASK
private static final int CLEANUP_MASKMask counting the periodic cache cleanups.- See Also:
_rdnsCleanupCounter
private static int _rdnsCleanupCounterLookups since the last cache cleanup._context
The router context._geoIP
The GeoIP service the results feed._pendingLookups
_executor
Pool running the lookups._executorLock
Lock for the lookup pool._corePoolSize
private static volatile int _corePoolSizeThreads kept in the lookup pool._maxPoolSize
private static volatile int _maxPoolSizeThreads allowed in the lookup pool.rdnsCache
In-memory reverse DNS cache storing IP-to-hostname mappings. Backed byConcurrentHashMapfor lock-free reads. Entries are periodically flushed to disk and expired entries are cleaned up.Keys are IP addresses as Strings. Values are CacheEntry objects containing hostname and timestamp.
_normalizeSweepDone
private static volatile boolean _normalizeSweepDoneFlag set after the first normalize sweep to avoid re-processing every 15 minutes._rdnsTimer
Timer writing the cache file.
Constructor Details
ReverseDnsLookup
Create the resolver and its background executor.The executor is started eagerly so the queue-size rate stat is ready before the first lookup, avoiding a first-hit initialization stall on the page-rendering path.
- Parameters:
ctx- the router contextgeoIP- already-constructed GeoIP database for ASN fallback
Method Details
expireHours
static int expireHours(boolean has512MB, boolean has1GB) RDNS cache entry lifetime in hours, based on available memory.Routers with less than 512 MB keep entries for 24 h; routers with at least 1 GB keep them for 48 h; everything else for 36 h. This bounds how stale a cached hostname may be before it is re-resolved, trading freshness against load on small memory-constrained routers.
- Parameters:
has512MB- true if the router has at least 512 MB of max memoryhas1GB- true if the router has at least 1 GB of max memory- Returns:
- entry lifetime in hours (24, 36, or 48)
maxCacheSize
static int maxCacheSize(boolean has512MB, boolean has1GB) Maximum rDNS cache size, based on available memory.Caches 8000 entries on very limited routers, 24000 on routers with at least 1 GB, and 16000 otherwise. The bound prevents the persistence file and the in-memory map from growing without limit on the control path.
- Parameters:
has512MB- true if the router has at least 512 MB of max memoryhas1GB- true if the router has at least 1 GB of max memory- Returns:
- maximum number of cached entries (8000, 16000, or 24000)
accelerateEviction
static boolean accelerateEviction(int size, int maxSize) Whether a full cache should evict entries at an accelerated rate.When the cache exceeds 90% of its maximum size, the TTL applied by
entryExpired(long, long, boolean)is halved so eviction keeps up with insertion load. Choosing the threshold as a percentage of the max (not an absolute number) keeps behavior consistent across the memory-dependent cache sizes.- Parameters:
size- current cache entry countmaxSize- maximum cache entry count- Returns:
- true if the cache is more than 90% full
entryExpired
static boolean entryExpired(long ageMs, long baseExpireMs, boolean isUnknown) Whether a cache entry has outlived its permitted lifetime."unknown" entries (failed lookups) expire after a short fixed window so dead IPs are re-probed; all other entries use the caller-supplied base TTL, which itself may be halved by
accelerateEviction(int, int).- Parameters:
ageMs- age of the entry in millisecondsbaseExpireMs- normal entry TTL in millisecondsisUnknown- true if the cached hostname is the "unknown" marker- Returns:
- true if the entry should be evicted
cacheFileEntryFresh
static boolean cacheFileEntryFresh(long ageMs, long evictThresholdMs) Whether an entry read from the persistence file is fresh enough to reuse.Applies only at startup when the file is loaded. Entries older than the eviction threshold are skipped so a long-idle router does not resurrect months-old hostnames.
- Parameters:
ageMs- age of the entry in millisecondsevictThresholdMs- maximum age accepted at load time- Returns:
- true if the entry is fresh enough to cache
enableReverseLookups
public boolean enableReverseLookups()Whether reverse lookups are enabled.- Returns:
- true if reverse lookups are enabled
getCorePoolSize
public static int getCorePoolSize()The rDNS executor core pool size.- Returns:
- the core pool size, bounded 2-8
setCorePoolSize
public static void setCorePoolSize(int size) The rDNS executor core pool size, bounded 2-8.- Parameters:
size- the new core pool size
getMaxPoolSize
public static int getMaxPoolSize()The rDNS executor max pool size.- Returns:
- the max pool size, bounded 2-8
setMaxPoolSize
public static void setMaxPoolSize(int size) The rDNS executor max pool size, bounded 2-8.- Parameters:
size- the new max pool size
rdnsCacheSize
The size of the rDNS cache file, in KB.- Returns:
- the file size in KB, suffixed with "KB"
countRdnsCacheEntries
public static int countRdnsCacheEntries()The number of entries in the rDNS cache.- Returns:
- the number of cached IP address to host name mappings
getRdnsCacheStats
Cache statistics for monitoring.- Returns:
- formatted string with cache stats
getMaxRdnsCacheSize
public static int getMaxRdnsCacheSize()The maximum rDNS cache size, computed from the memory available.- Returns:
- the entries the cache holds
getExecutor
Reverse DNS executor, initializing it if necessary.- Returns:
- the reverse dns executor
adjustPool
public void adjustPool(int coreSize) Adjust the running rDNS executor pool sizes. Called by the Tuner when queue depth signals scaling.- Parameters:
coreSize- new core pool size (bounded 2-8)
getQueueSize
public int getQueueSize()Returns rDNS executor pending lookup count, or 0 if not running.- Returns:
- the rdns queue size
getActiveCount
public int getActiveCount()Returns rDNS executor active thread count, or 0 if not running.- Returns:
- the rdns active count
getPoolSize
public int getPoolSize()Returns rDNS executor total thread count, or 0 if not running.- Returns:
- the rdns pool size
getUtilization
public double getUtilization()Returns rDNS executor utilization as a ratio (0.0-1.0), orDouble.NaNif not running.- Returns:
- the rdns utilization
shutdown
public void shutdown()Stop the periodic cache writer and shut down the lookup executor.Cancels the background cache-file timer and the rDNS executor, waiting up to 5 seconds for in-flight lookups to drain before force-interrupting them, and clears the in-flight marker set so a later restart does not inherit stale pending state.
readRDNSCacheFromFile
static void readRDNSCacheFromFile()Load the persistence file into the in-memory cache at startup.Skips "#" comment lines and entries older than the eviction threshold, then schedules the periodic cache-file writer task (
RDNSCacheFileWriter). A missing file is created so the periodic writer always has a valid target. Invoked from the facade once reverse lookups are enabled after the netdb settles.rdnsEntryToString
Serialize a cache entry for persistence.Format is "ip,hostname,timestamp". One line per entry. The timestamp enables expiration and startup filtering, and distinguishes the current format from the two-part legacy format (see
rdnsEntryFromString(String)).- Parameters:
entry- the entry to serialize- Returns:
- the serialized entry line
rdnsEntryFromString
Parse a persisted line back into a cache entry.Three-part lines carry an explicit timestamp. Two-part lines are the pre-timestamp legacy format and are migrated with the current time. Any other shape returns null and the caller skips the line.
- Parameters:
s- the persisted line- Returns:
- the parsed entry, or null if the line is malformed
createRdnsCacheFile
private static void createRdnsCacheFile()Writes the reverse DNS cache to disk.cleanupRDNSCache
private static void cleanupRDNSCache()Drops the stale reverse DNS cache entries.needRdnscacheSweep
static boolean needRdnscacheSweep(int size, int maxSize, int counter) Decide whether a cache insert must run a full-cache cleanup sweep. SeeneedRdnscacheSweep(int, int, int)for the amortization rule.Sweeping on every put when the cache sits at its cap is O(n) per insert and shows up as CHM iterator churn on the rDNS hot path. Instead the sweep is amortized: once the map is at/over its cap it runs at most once every
CLEANUP_BITSinserts (it usually stays pegged at the cap in steady state), while exceeding the cap by more thanMAX_SLACKentries still sweeps immediately so the memory bound stays hard.A benign race on the shared
_rdnsCleanupCounteronly changes which insert triggers the sweep; it never under-sweeps below the slack guard (the size check is made against the live map by the caller).- Parameters:
size- current cache size; userdnsCache.size()maxSize- the cache cap, e.g.MAX_RDNS_CACHE_SIZEcounter- a per-put counter, e.g. an incrementing static int- Returns:
- true if a cleanup sweep must run before the insert
rdnsCachePut
Insert into the rdns cache with budget enforcement. Evicts expired entries first, then oldest entries if still over budget. The cleanup sweep is amortized to at most one full map pass perCLEANUP_BITSinserts while at the cap; seeneedRdnscacheSweep(int, int, int)for the rationale.- Parameters:
ipAddress- the cache key the entry is stored underentry- the entry to insert; its hostname is normalized first, replacing the entry if the normalized form differs
getCanonicalHostName
Canonical hostname for the given IP address from cache or DNS. If RDNS is enabled, performs a reverse DNS lookup first. Falls back to local ASN database if DNS fails, returning the org name.- Parameters:
ipAddress- IP address to resolve, or null/"null" to return null- Returns:
- hostname, org name from ASN database, or null for invalid input
lookupHostNameAsync
Background RDNS/ASN resolution task submitted to the reverse-DNS executor. Performs a reverse lookup, falling back to the local ASN database when the result is the IP itself or unknown. Always clears the in-flight marker.- Parameters:
ipAddress- non-null IP to resolve
lookupOrgNameAsync
Background ASN-first resolution task submitted to the reverse-DNS executor. Prefers the local ASN organization database, falling back to reverse DNS. Always clears the in-flight marker.- Parameters:
ipAddress- non-null IP to resolve
getCanonicalHostNameSync
getLocalHostName
Fast hostname lookup that never blocks on I/O (MMDB or network). Returns cached result if available and useful. On cache miss, queues a background job that does ASN org name lookup (MMDB file read + regex normalization) and optional RDNS, then returns null immediately — page rendering is never blocked.- Parameters:
ipAddress- the peer IP address to name, a null or literal "null" yields null- Returns:
- cached hostname/ASN org name, or null if not yet resolved
_t