{"record":{"id":"bf6b573e38d964d3","repo":"YunaiV/ruoyi-vue-pro","slug":"error-bf6b57","errorCode":null,"errorMessage":"会话数已达上限: {}","messagePattern":"会话数已达上限: (.+?)","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/manager/IotUdpSessionManager.java","lineNumber":55,"sourceCode":"    public IotUdpSessionManager(int maxSessions, long sessionTimeoutMs) {\n        this.maxSessions = maxSessions;\n        this.deviceSessionCache = CacheBuilder.newBuilder()\n                .maximumSize(maxSessions)\n                .expireAfterAccess(sessionTimeoutMs, TimeUnit.MILLISECONDS)\n                .build();\n    }\n\n    /**\n     * 注册设备会话\n     *\n     * @param deviceId    设备 ID\n     * @param sessionInfo 会话信息\n     */\n    public synchronized void registerSession(Long deviceId, SessionInfo sessionInfo) {\n        // 检查是否为新设备，且会话数已达上限（同步方法确保检查和注册的原子性）\n        if (deviceSessionCache.getIfPresent(deviceId) == null\n                && deviceSessionCache.size() >= maxSessions) {\n            throw new IllegalStateException(\"会话数已达上限: \" + maxSessions);\n        }\n        // 注册会话\n        deviceSessionCache.put(deviceId, sessionInfo);\n        log.info(\"[registerSession][注册设备会话，设备 ID: {}，地址: {}，productKey: {}，deviceName: {}]\",\n                deviceId, buildAddressKey(sessionInfo.getAddress()),\n                sessionInfo.getProductKey(), sessionInfo.getDeviceName());\n    }\n\n    /**\n     * 获取会话信息\n     * <p>\n     * 注意：调用此方法会自动刷新会话的过期时间\n     *\n     * @param deviceId 设备 ID\n     * @return 会话信息，不存在则返回 null\n     */\n    public SessionInfo getSession(Long deviceId) {\n        return deviceSessionCache.getIfPresent(deviceId);","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/YunaiV/ruoyi-vue-pro/blob/0418084e222612af2fc1141f566af454f9236ab1/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/manager/IotUdpSessionManager.java#L37-L73","documentation":"Thrown by IotUdpSessionManager.registerSession when a NEW device (not already in deviceSessionCache) attempts to register while the cache has reached maxSessions. The manager is a Guava Cache bounded by maximumSize with expireAfterAccess eviction; this check is an explicit ceiling on top of Guava's own eviction so that a full cache rejects brand-new devices instead of silently evicting an existing one. It is an IllegalStateException because the gateway is in a valid but capacity-blocked state. Note the check is a race-free compare-and-register guarded by the synchronized method.","triggerScenarios":"Calling registerSession(deviceId, sessionInfo) where deviceSessionCache.getIfPresent(deviceId) == null AND deviceSessionCache.size() >= maxSessions. Re-registration (same deviceId already cached) never trips it because the first conjunct is false. The limit is the maxSessions value passed to the constructor (IotUdpSessionManager(maxSessions, sessionTimeoutMs)).","commonSituations":"A UDP gateway deployed with a too-small maxSessions for the fleet size; a device fleet larger than the configured cap all reconnecting after a gateway restart; stale sessions not expiring fast enough (long sessionTimeoutMs) so live slots are consumed by idle devices; misconfiguration where maxSessions was left at a default low value.","solutions":["Increase maxSessions in the IotUdpSessionManager construction (gateway config) to exceed the expected concurrent device count.","Shorten sessionTimeoutMs so idle device sessions expire sooner, freeing slots for new devices.","Catch IllegalStateException at the call site of registerSession and respond to the device with a 'server busy / retry later' NACK instead of crashing the handler.","Confirm the deviceId being passed is correct and stable per physical device; a deviceId that churns (e.g. per-source-port) will exhaust the cache with duplicate logical devices."],"exampleFix":"// before\nmanager.registerSession(deviceId, info); // throws when full\n\n// after\ntry {\n    manager.registerSession(deviceId, info);\n} catch (IllegalStateException e) {\n    log.warn(\"[udp][会话已满，拒绝设备 {}]\", deviceId);\n    // reply with retry-later or drop packet\n}","handlingStrategy":"try-catch","validationCode":"// No public size() accessor exists; validate by attempting registration\ndefault void registerOrReject(IotUdpSessionManager mgr, Long deviceId, SessionInfo info) {\n    try {\n        mgr.registerSession(deviceId, info);\n    } catch (IllegalStateException e) {\n        // cache full — tell the device to retry, or shed load\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    sessionManager.registerSession(deviceId, sessionInfo);\n} catch (IllegalStateException e) {\n    log.warn(\"[udp][session capacity reached, device {} rejected]\", deviceId);\n    // optionally send a retry-later response; do NOT propagate and kill the UDP handler\n}","preventionTips":["Size maxSessions above the peak concurrent device count with headroom for reconnect storms.","Tune sessionTimeoutMs so idle devices free slots quickly.","Monitor deviceSessionCache eviction/size metrics and alert before the cap.","Ensure deviceId is stable per physical device so the cache does not fill with logical duplicates."],"tags":["iot","udp","session","capacity","guava-cache"],"backgroundTag":null,"analyzedSha":"0418084e222612af2fc1141f566af454f9236ab1","analyzedAt":"2026-08-14T00:56:18.412Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}