private void detectAndFixBrokenCaches()
    {
        // Ensure that 'sessionInfoCache' has content that reflects the locally available (client) sessions (we do not need to
        // restore the info for sessions on other nodes, as those will be dropped right after invoking this method anyway).
        Log.info("Looking for local ClientSessionInfo instances that have 'dropped out' of the cache (likely as a result of a network failure).");
        final Map<String, ClientSession> localClientSessionInfos = XMPPServer.getInstance().getRoutingTable().getClientsRoutes(true).stream().collect(Collectors.toMap(s->s.getAddress().toString(), Function.identity()));
        final Set<String> cachedClientSessionInfos = sessionInfoCache.keySet();
        final Set<String> clientSessionInfosNotInCache = new HashSet<>(localClientSessionInfos.keySet()); // defensive copy - we should not modify localClientSessionInfos!
        clientSessionInfosNotInCache.removeAll(cachedClientSessionInfos);
        if (clientSessionInfosNotInCache.isEmpty()) {
            Log.info("Found no local ClientSessionInfo instances that are missing from the cache.");
        } else {
            Log.warn("Found {} ClientSessionInfo instances that we know locally, but are not (no longer) in the cache. This can occur when a cluster node fails, but should not occur otherwise.", clientSessionInfosNotInCache.size());
            for (final String missing : clientSessionInfosNotInCache) {
                Log.info("Restoring ClientSessionInfo instances for: {}", missing);
                sessionInfoCache.put(missing, new ClientSessionInfo((LocalClientSession) localClientSessionInfos.get(missing)));
            }
        }

        // Ensure that 'componentSessionsCache' has content that reflects the locally available components sessions (we
        // do not need to restore the info for sessions on other nodes, as those will be dropped right after invoking this method anyway).
        Log.info("Looking for local component sessions that have 'dropped out' of the cache (likely as a result of a network failure).");
        final Map<String, LocalComponentSession> localComponentSessions = localSessionManager.getComponentsSessions().stream().collect(Collectors.toMap(s->s.getAddress().toString(), Function.identity()));
        final Set<String> cachedComponentSessions = componentSessionsCache.keySet();
        final Set<String> componentSessionsNotInCache = new HashSet<>(localComponentSessions.keySet()); // defensive copy - we should not modify localComponentSessions!
        componentSessionsNotInCache.removeAll(cachedComponentSessions);
        if (componentSessionsNotInCache.isEmpty()) {
            Log.info("Found no local component sessions that are missing from the cache.");
        } else {
            Log.warn("Found {} component sessions that we know locally, but are not (no longer) in the cache. This can occur when a cluster node fails, but should not occur otherwise.", componentSessionsNotInCache.size());
            for (final String missing : componentSessionsNotInCache) {
                Log.info("Restoring component sessions for: {}", missing);
                CacheUtil.addValueToMultiValuedCache(componentSessionsCache, missing, server.getNodeID(), HashSet::new);
            }
        }

        // Ensure that 'multiplexerSessionsCache' has content that reflects the locally available connection managers sessions (we
        // do not need to restore the info for sessions on other nodes, as those will be dropped right after invoking this method anyway).
        // TODO: if we every want to revive the ConnectionManager interface, we should fix this. I'm not putting in effort now to save some time.
        Log.info("Skip looking for local connection manager sessions that have 'dropped out' of the cache (likely as a result of a network failure), as these deprecated mechanisms aren't supported anymore.");

        // Ensure that 'incomingServerSessionsCache' has content that reflects the locally available incoming server sessions
        // (we do not need to restore the info for sessions on other nodes, as those will be dropped right after invoking this method anyway).
        Log.info("Looking for local incoming server sessions that have 'dropped out' of the cache (likely as a result of a network failure).");
        final Map<StreamID, LocalIncomingServerSession> localIncomingServerSessions = localSessionManager.getIncomingServerSessions().stream().collect(Collectors.toMap(LocalSession::getStreamID, Function.identity()));
        final Set<StreamID> cachedIncomingServerSessions = incomingServerSessionInfoCache.keySet();
        final Set<StreamID> incomingServerSessionsNotInCache = new HashSet<>(localIncomingServerSessions.keySet()); // defensive copy - we should not modify localClientSessionInfos!
        incomingServerSessionsNotInCache.removeAll(cachedIncomingServerSessions);
        if (incomingServerSessionsNotInCache.isEmpty()) {
            Log.info("Found no local incoming server sessions that are missing from the cache.");
        } else {
            Log.warn("Found {} incoming server sessions that we know locally, but are not (no longer) in the cache. This can occur when a cluster node fails, but should not occur otherwise.", incomingServerSessionsNotInCache.size());
            for (final StreamID missing : incomingServerSessionsNotInCache) {
                Log.info("Restoring incoming server session for: {}", missing);
                incomingServerSessionInfoCache.put(missing, new IncomingServerSessionInfo(localIncomingServerSessions.get(missing)));
            }
        }

        // Ensure that 'domainSessionsCache' has content that reflects the locally available incoming server sessions
        // (we do not need to restore the info for sessions on other nodes, as those will be dropped right after invoking this method anyway).
        Log.info("Looking for local domain sessions that have 'dropped out' of the cache (likely as a result of a network failure).");
        final Set<LocalIncomingServerSession> domainSessionsNotInCache = localSessionManager.getIncomingServerSessions().stream()
            .filter(needle -> domainSessionsCache.entrySet().stream().noneMatch(
                entry -> entry.getKey().equals(needle.getAddress().getDomain()) && entry.getValue().contains(needle.getStreamID())))
            .collect(Collectors.toSet());
        if (domainSessionsNotInCache.isEmpty()) {
            Log.info("Found no local domain sessions that are missing from the cache.");
        } else {
            Log.warn("Found {} domain sessions that we know locally, but are not (no longer) in the cache. This can occur when a cluster node fails, but should not occur otherwise.", domainSessionsNotInCache.size());
            for (final LocalIncomingServerSession missing : domainSessionsNotInCache) {
                Log.info("Restoring domain session for: {}", missing);
                CacheUtil.addValueToMultiValuedCache(domainSessionsCache, missing.getAddress().getDomain(), missing.getStreamID(), ArrayList::new);
            }
        }
    }
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. Under 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. This local copy can be used to detect and/or correct such a loss in data. This is performed
by this method.

Note that this method is expected to be called as part of {@link #leftCluster(byte[])} only. It will therefor
mostly restore data that is considered local to the server node, and won't bother with data that's considered
to be pertinent to other cluster nodes only (as that data will be removed directly after invocation of this
method anyway).
xmppserver/src/main/java/org/jivesoftware/openfire/SessionManager.java