package com.aelitis.azureus.core.speedmanager.impl.v2; import com.aelitis.azureus.core.speedmanager.SpeedManagerPingSource; import com.aelitis.azureus.core.util.average.Average; import java.util.Map; import java.util.HashMap; import org.gudy.azureus2.core3.util.SystemTime; /** * Created on May 31, 2007 * Created by Alan Snyder * Copyright (C) 2007 Aelitis, All Rights Reserved. * <p/> * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * <p/> * AELITIS, SAS au capital de 63.529,40 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. */ /** * This class manage cycling though the PingSources. It keep track of PingSource stats and * applies rules on if/when to cycle though a ping-source. * * #1) If the slowest ping-source is 10x the the best for a 1 min average. kick it. * #2) If a ping-source is slower then two combined sources (2x) for a 5 min average. then kick it. * #3) Every 30 minutes kick the slowest ping source and request a new one. Just to keep things fresh. * * Also maintain logic do determine if a new source is better then the previous one. (To determine * if these rules lead to good data.) * */ public class PingSourceManager { // private final Map pingAverages = new HashMap(); //<Source,PingSourceStats> private long lastPingRemoval=0; private static final long TIME_BETWEEN_BAD_PING_REMOVALS = 2 * 60 * 1000; // two minutes. private static final long TIME_BETWEEN_SLOW_PING_REMOVALS = 5 * 60 * 1000;// fifteen minutes. private static final long TIME_BETWEEN_FORCED_CYCLE_REMOVALS = 30 * 60 * 1000;// thirty minutes. /** * Determine if we should drop any ping sources. * Sort them, if one significantly higher then the other two. then drop it. * @param sources - SpeedManagerPingSource[] inputs */ public void checkPingSources(SpeedManagerPingSource[] sources){ //if the long term average of one source is 10 the lowest and twice a large as the //two lowest then drop the highest at the moment. Also, don't force sources to //drop to frequently. //no sources. if( sources==null ){ return; } //if we have only two sources then don't do this test. if( sources.length<3 ){ return; } //Test for a very bad ping source. i.e. slowest source is 10x slower then the fastest, if( checkForBadPing(sources) ){ return; } //Test for slower then average source. i.e. slowest source is 3x media. if( checkForSlowSource(sources) ){ return; } //Even if everything is going well then force a change every 30 minutes. forcePingSourceChange(sources); }//checkPingSources /** * If one ping source is twice the fastest then replace it. Otherwise reset the timer. * @param sources - * @return - true is a souce has been changed. */ private boolean forcePingSourceChange(SpeedManagerPingSource[] sources){ //We only apply this rule if nothing has been removed in the past 30 minutes. long currTime = SystemTime.getCurrentTime(); if( currTime<lastPingRemoval+ TIME_BETWEEN_FORCED_CYCLE_REMOVALS){ return false; } if(sources.length<3){ return false; } //just find the slowest ping-source and remove it. SpeedManagerPingSource slowestSource = null; double slowestPing = 0.0; double fastestPing = 10000.0; int len = sources.length; for(int i=0; i<len; i++){ PingSourceStats pss = (PingSourceStats) pingAverages.get(sources[i]); Average ave = pss.getHistory(); double pingTime = ave.getAverage(); //find slowest if( pingTime>slowestPing ){ slowestPing = pingTime; slowestSource=sources[i]; } //find sped of fastest. if( pingTime<fastestPing ){ fastestPing = pingTime; } }//for //regardless of result, resetTimer the timer. resetTimer(); //only replace the slowest if it is twice the fastest. if( slowestPing > 2*fastestPing ){ if(slowestSource!=null){ slowestSource.destroy(); return true; } } return false; }//forcePingSourceChange /** * A slow source is something that is 2x the slower then the two fastest. * @param sources - * @return - true is a source has been removed. */ private boolean checkForSlowSource(SpeedManagerPingSource[] sources){ //We only apply this rule if nothing has been removed in the past 15 minutes. long currTime = SystemTime.getCurrentTime(); if( currTime<lastPingRemoval+ TIME_BETWEEN_SLOW_PING_REMOVALS){ return false; } SpeedManagerPingSource slowestSource = null; if( sources.length<3 ){ return false; } double fastA = 10000.0; double fastB = 10000.0; double slowest = 0.0; int len = sources.length; for(int i=0; i<len; i++){ PingSourceStats pss = (PingSourceStats) pingAverages.get(sources[i]); Average ave = pss.getHistory(); double pingTime = ave.getAverage(); //determine fastest or second fastest. if(pingTime<fastA){ fastB=fastA; fastA=pingTime; }else if(pingTime<fastB){ fastB=pingTime; } //determine slowest. if(pingTime>slowest){ slowest = pingTime; slowestSource = sources[i]; resetTimer(); } }//for double sumFastest = fastA+fastB; boolean removedSource = false; if( sumFastest*2 < slowest ){ //destroy this source. It is a bit too slow. if(slowestSource!=null){ slowestSource.destroy(); SpeedManagerLogger.log("dropping ping source: "+slowestSource.getAddress()+" for being 2x slower then two fastest."); removedSource = true; resetTimer(); } }//if return removedSource; }//checkForSlowSource /** * If the slowest ping in 10x the fastest then remove it. * @param sources - * @return - true is a source has been removed. */ private boolean checkForBadPing(SpeedManagerPingSource[] sources) { //if we just recently removed a ping source then wait. long currTime = SystemTime.getCurrentTime(); if( currTime<lastPingRemoval+ TIME_BETWEEN_BAD_PING_REMOVALS){ return false; } double highestLongTermPing=0.0; SpeedManagerPingSource highestSource=null; double lowestLongTermPing=10000.0; int len = sources.length; for(int i=0; i<len; i++){ PingSourceStats pss = (PingSourceStats) pingAverages.get(sources[i]); if ( pss == null ){ continue; } Average a = pss.getLongTermAve(); double avePingTime = a.getAverage(); //is this a new highest value? if( avePingTime>highestLongTermPing ){ highestLongTermPing = avePingTime; highestSource = sources[i]; } //is this a new lowest value? if( avePingTime<lowestLongTermPing ){ lowestLongTermPing = avePingTime; } }//for boolean removedSource = false; //if the highest value is 8x the lowest then find another source. if( lowestLongTermPing*8 < highestLongTermPing ){ //remove the slow source we will get a new one to replace it. if( highestSource!=null ){ SpeedManagerLogger.log("dropping ping source: "+highestSource.getAddress()+" for being 8x greater then min source."); highestSource.destroy(); removedSource = true; resetTimer(); } }//if return removedSource; } public void pingSourceFound(SpeedManagerPingSource source, boolean is_replacement){ PingSourceStats pss = new PingSourceStats(source); pingAverages.put(source,pss); } public void pingSourceFailed(SpeedManagerPingSource source) { if( pingAverages.remove(source)==null){ SpeedManagerLogger.log("didn't find source: "+source.getAddress().getHostName()); } } public void addPingTime(SpeedManagerPingSource source){ if(source==null){ return; } PingSourceStats pss = (PingSourceStats) pingAverages.get(source); if(pss==null){ pingSourceFound(source,false); pss = (PingSourceStats) pingAverages.get(source); SpeedManagerLogger.trace("added new source from addPingTime."); } int pingTime = source.getPingTime(); if(pingTime>0){ pss.addPingTime( source.getPingTime() ); } }//addPingTime /** * After a ping-source has been removed, need to resetTimer the timer. */ private void resetTimer(){ lastPingRemoval = SystemTime.getCurrentTime(); } }