@Override
    public void leftCluster(byte[] nodeID)
    {
        // Another node left the cluster.
        final NodeID nodeIDOfLostNode = NodeID.getInstance(nodeID);
        Log.debug("Cluster node {} just left the cluster.", nodeIDOfLostNode);

        // When the local node drops out of the cluster (for example, due to a network failure), then from the perspective
        // of that node, all other nodes leave the cluster. This method is invoked for each of them. In certain
        // circumstances, this can mean that the local node no longer has access to all data (or its backups) that is
        // maintained in the clustered caches. From the perspective of the remaining node, this data is lost. (OF-2297/OF-2300).
        // To prevent this being an issue, most caches have supporting local data structures that maintain a copy of the most
        // critical bits of the data stored in the clustered cache, which is to be used to detect and/or correct such a
        // loss in data. This is done in the next few lines of this method.
        detectAndFixBrokenCaches();

        // When a peer server leaves the cluster, any remote sessions that were associated with the defunct node must be
        // dropped from the session caches (and supporting data structures) that are shared by the remaining cluster member(s).

        // Note: All remaining cluster nodes will be in a race to clean up the same data. We can not depend on cluster
        // seniority to appoint a 'single' cleanup node, because for a small moment we may not have a senior cluster member.

        // Remove incoming server sessions hosted in node that left the cluster
        final Set<StreamID> removedServerSessions = incomingServerSessionInfoByClusterNode.remove(nodeIDOfLostNode);
        if (removedServerSessions != null) {
            removedServerSessions
                .forEach(streamID -> {
                    try {
                        // Remove all the domains that were registered for this server session.
                        unregisterIncomingServerSession(streamID);
                    } catch (Exception e) {
                        Log.error("Node {} left the cluster. Incoming server sessions on that node are no longer available. To reflect this, we're deleting these sessions. While doing this for '{}', this caused an exception to occur.", nodeIDOfLostNode, streamID, e);
                    }
                });
        }

        // For componentSessionsCache and multiplexerSessionsCache there is no clean up to be done, except for removing
        // the value from the cache. Therefore it is unnecessary to create a reverse lookup tracking state per (remote)
        // node.
        CacheUtil.removeValueFromMultiValuedCache(componentSessionsCache, NodeID.getInstance(nodeID));
        CacheUtil.removeValueFromCache(multiplexerSessionsCache, NodeID.getInstance(nodeID));

        // Remove client sessions hosted in node that left the cluster
        final Set<String> removedSessionInfo = sessionInfoKeysByClusterNode.remove(nodeIDOfLostNode);
        if (removedSessionInfo != null) {
            removedSessionInfo.forEach(fullJID -> {
                final JID offlineJID = new JID(fullJID);
                boolean sessionIsAnonymous = false;
                final ClientSessionInfo clientSessionInfoAboutToBeRemoved = sessionInfoCache.remove(fullJID);
                if (clientSessionInfoAboutToBeRemoved != null) {
                    sessionIsAnonymous = clientSessionInfoAboutToBeRemoved.isAnonymous();
                } else {
                    // Apparently there is an inconsistency between sessionInfoKeysByClusterNode and sessionInfoCache.
                    // That's troublesome, so log a warning. For the session removal we can't do more than just assume
                    // the session was not anonymous (which has the highest probability for most use cases).
                    Log.warn("Session information for {} is not available from sessionInfoCache, while it was still expected to be there", fullJID);
                }
                removeSession(null, offlineJID, sessionIsAnonymous, true);
            });
        }

        // In some cache implementations, the entry-set is unmodifiable. To guard against potential
        // future changes of this implementation (that would make the implementation incompatible with
        // these cache implementations), the entry-set that's operated on in this implementation is
        // explicitly wrapped in an unmodifiable collection. That forces this implementation to be
        // compatible with the 'lowest common denominator'.
        final Set<Map.Entry<String, ClientSessionInfo>> entries = Collections.unmodifiableSet(sessionInfoCache.entrySet() );
        for ( final Map.Entry<String, ClientSessionInfo> entry : entries )
        {
            if (entry.getValue().getNodeID().equals( NodeID.getInstance(nodeID) )) {
                sessionInfoCache.remove(entry.getKey());
            }
        }
    }
Returns the number of milliseconds to elapse between clearing of idle server sessions.

@return the number of milliseconds to elapse between clearing of idle server sessions.
xmppserver/src/main/java/org/jivesoftware/openfire/SessionManager.java