private Mono<Integer> closeLocalSessions(
            @NotNull Long userId,
            @NotEmpty Set<@ValidDeviceType DeviceType> deviceTypes,
            @NotNull CloseReason closeReason,
            @NotNull UserSessionsManager manager) {
        try {
            Validator.notNull(closeReason, "closeReason");
            Validator.notNull(manager, "manager");
        } catch (ResponseException e) {
            return Mono.error(e);
        }
        // Don't close the session (connection) first and then remove the session status in Redis
        // because it will make trouble if a client logins again while the session status in Redis
        // hasn't been removed
        return userStatusService.removeStatusByUserIdAndDeviceTypes(userId, deviceTypes)
                .map(ignored -> {
                    int sessionCount = 0;
                    for (DeviceType deviceType : deviceTypes) {
                        UserSession session = manager.getSession(deviceType);
                        if (session == null) {
                            continue;
                        }
                        boolean wasSessionOpen =
                                manager.closeSession(session.getDeviceType(), closeReason);
                        ByteArrayWrapper ip = session.getIp();
                        if (ip != null) {
                            ipToSessions.computeIfPresent(ip,
                                    (key, sessions) -> sessions.remove(session)
                                            ? (sessions.isEmpty()
                                                    ? null
                                                    : sessions)
                                            : sessions);
                        }
                        if (sessionLocationService.isLocationEnabled()) {
                            sessionLocationService
                                    .removeUserLocation(session.getUserId(),
                                            session.getDeviceType())
                                    .subscribe(null,
                                            t -> LOGGER.error(
                                                    "Failed to remove the location of the user session: {user={}, device={}}",
                                                    session.getUserId(),
                                                    session.getDeviceType(),
                                                    t));
                        }
                        if (wasSessionOpen) {
                            sessionCount++;
                            notifyOnSessionClosedListeners(session);
                        }
                    }
                    removeSessionsManagerIfEmpty(closeReason, manager, userId);
                    return sessionCount;
                });
    }
@return closed session count
@implNote The method will be called definitely when a session is closed no matter it is
          closed by the client or the server, and the method will clean up sessions in both
          local and Redis.
turms-gateway/src/main/java/im/turms/gateway/domain/session/service/SessionService.java