/* * Copyright 2013 Thomas Bocek * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package net.tomp2p.peers; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import net.tomp2p.utils.ConcurrentCacheMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The default maintenance implementation. * * @author Thomas Bocek * */ public class DefaultMaintenance implements Maintenance { private static final Logger LOG = LoggerFactory.getLogger(DefaultMaintenance.class); private final int peerUrgency; private final int[] intervalSeconds; private final List<Map<Number160, PeerStatistic>> peerMapVerified; private final List<Map<Number160, PeerStatistic>> peerMapNonVerified; private final ConcurrentCacheMap<Number160, PeerAddress> offlineMap; private final ConcurrentCacheMap<Number160, PeerAddress> shutdownMap; private final ConcurrentCacheMap<Number160, PeerAddress> exceptionMap; /** * Creates a new maintenance class with the verified and non verified map. * * @param peerMapVerified * The verified map * @param peerMapNonVerified * The non-verified map * @param offlineMap * The offline map * @param exceptionMap * @param shutdownMap * @param peerUrgency * The number of peers that should be in the verified map. If the number is lower, urgency is set to yes * and we are looking for peers in the non verified map * @param intervalSeconds * The number of intervals to test a peer. The longer a peer is available the less often we need to check * */ private DefaultMaintenance(final List<Map<Number160, PeerStatistic>> peerMapVerified, final List<Map<Number160, PeerStatistic>> peerMapNonVerified, final ConcurrentCacheMap<Number160, PeerAddress> offlineMap, final ConcurrentCacheMap<Number160, PeerAddress> shutdownMap, final ConcurrentCacheMap<Number160, PeerAddress> exceptionMap, final int peerUrgency, final int[] intervalSeconds) { this.peerMapVerified = peerMapVerified; this.peerMapNonVerified = peerMapNonVerified; this.offlineMap = offlineMap; this.shutdownMap = shutdownMap; this.exceptionMap = exceptionMap; this.peerUrgency = peerUrgency; this.intervalSeconds = intervalSeconds; } /** * Constructor that initializes the maps as null references. To use this class init must be called that creates a * new class with the private constructor. * * @param peerUrgency * The number of peers that should be in the verified map. If the number is lower, urgency is set to yes * and we are looking for peers in the non verified map * @param intervalSeconds * The number of intervals to test a peer. The longer a peer is available the less often we need to check */ public DefaultMaintenance(final int peerUrgency, final int[] intervalSeconds) { this.peerMapVerified = null; this.peerMapNonVerified = null; this.offlineMap = null; this.shutdownMap = null; this.exceptionMap = null; this.peerUrgency = peerUrgency; this.intervalSeconds = intervalSeconds; } @Override public Maintenance init(final List<Map<Number160, PeerStatistic>> peerMapVerified, final List<Map<Number160, PeerStatistic>> peerMapNonVerified, final ConcurrentCacheMap<Number160, PeerAddress> offlineMap, final ConcurrentCacheMap<Number160, PeerAddress> shutdownMap, final ConcurrentCacheMap<Number160, PeerAddress> exceptionMap) { return new DefaultMaintenance(peerMapVerified, peerMapNonVerified, offlineMap, shutdownMap, exceptionMap, peerUrgency, intervalSeconds); } /** * Finds the next peer that should have a maintenance check. Returns null if no maintenance is needed at the moment. * It will return the most important peers first. Importance is as follows: The most important peers are the close * ones in the verified peer map. If a certain threshold in a bag is not reached, the unverified becomes important * too. * * @return The next most important peer to check if it is still alive. */ public PeerStatistic nextForMaintenance(Collection<PeerAddress> notInterestedAddresses) { if (peerMapVerified == null || peerMapNonVerified == null || offlineMap == null || shutdownMap == null || exceptionMap == null) { throw new IllegalArgumentException("Did not initialize some of the maintenance maps."); } int peersBefore = 0; for (int i = 0; i < Number160.BITS; i++) { final Map<Number160, PeerStatistic> mapVerified = peerMapVerified.get(i); boolean urgent = false; synchronized (mapVerified) { final int size = mapVerified.size(); peersBefore += size; urgent = isUrgent(i, size, peersBefore); } if (urgent) { final Map<Number160, PeerStatistic> mapNonVerified = peerMapNonVerified.get(i); final PeerStatistic readyForMaintenance = next(mapNonVerified); if (readyForMaintenance != null && !notInterestedAddresses.contains(readyForMaintenance.peerAddress())) { LOG.debug("check urgent peer {} from the non-verified map.", readyForMaintenance.peerAddress()); return readyForMaintenance; } } final PeerStatistic readyForMaintenance = next(mapVerified); if (readyForMaintenance != null && !notInterestedAddresses.contains(readyForMaintenance.peerAddress())) { LOG.debug("check peer {} from the non-verified map.", readyForMaintenance.peerAddress()); return readyForMaintenance; } } return null; } /** * Returns a peer with its statistics from a bag that needs maintenance. * * @param map * The bag with all the peers * @return A peer that needs maintenance */ private PeerStatistic next(final Map<Number160, PeerStatistic> map) { synchronized (map) { for (PeerStatistic peerStatistic : map.values()) { if (needMaintenance(peerStatistic, intervalSeconds)) { return peerStatistic; } } } return null; } /** * Indicates if it is urgent to search for a peer. This means that we have not enough peers in the verified map and * we need to get one from the non-verified map. * * @param bagIndex * The number of the bagindex. The smaller the index, the more important the peer * @param bagSize * The size of the current bag * @param peersBefore * The number of peers we have that are smaller than in this bag index * @return True, if we need urgently a peer from the non-verified map */ protected boolean isUrgent(final int bagIndex, final int bagSize, final int peersBefore) { return bagSize < peerUrgency; } /** * Indicates if a peer needs a maintenance check. * * @param peerStatistic * The peer with its statistics * @return True if the peer needs a maintenance check */ public static boolean needMaintenance(final PeerStatistic peerStatistic, final int[] intervalSeconds) { final long onlineSec = TimeUnit.MILLISECONDS.toSeconds(peerStatistic.onlineTime()); final long timeSinceLastCheckMillis = System.currentTimeMillis() - peerStatistic.lastSeenOnline(); if (onlineSec <= 0) { return timeSinceLastCheckMillis > TimeUnit.SECONDS.toMillis(intervalSeconds[0]); } else { for(int i=0;i<intervalSeconds.length;i++) { //interval is 2,4,8,16,32,64 //examples //I have seen a peer online for 5 sec -> next interval to check is 8 //I have seen a peer online for 4 sec -> next interval to check is 4 //I have seen a peer online for 17 sec -> next interval to check is 32 //I have seen a peer online for 112321 sec -> next interval to check is 64 if(intervalSeconds[i]>=onlineSec) { return timeSinceLastCheckMillis > TimeUnit.SECONDS.toMillis(intervalSeconds[i]); } } return timeSinceLastCheckMillis > TimeUnit.SECONDS.toMillis(intervalSeconds[intervalSeconds.length - 1]); } } }