FileDocCategorySizeDatePackage
PeerDatabase.javaAPI DocAzureus 3.0.3.412409Mon Jun 11 13:14:12 BST 2007com.aelitis.azureus.core.peermanager.peerdb

PeerDatabase

public class PeerDatabase extends Object

Fields Summary
private static final int
MIN_REBUILD_WAIT_TIME
private static final int
MAX_DISCOVERED_PEERS
private static final int
BLOOM_ROTATION_PERIOD
private static final int
BLOOM_FILTER_SIZE
private final HashMap
peer_connections
private final LinkedList
discovered_peers
private final AEMonitor
map_mon
private PeerItem[]
cached_peer_popularities
private int
popularity_pos
private long
last_rebuild_time
private long
last_rotation_time
private PeerItem
self_peer
private BloomFilter
filter_one
private BloomFilter
filter_two
Constructors Summary
protected PeerDatabase()

  
  
  
  
  
    
    /* nothing */
  
Methods Summary
public voidaddDiscoveredPeer(PeerItem peer)
Add a potential peer obtained via tracker announce, DHT announce, plugin, etc.

param
peer to add

    try{  map_mon.enter();
      for( Iterator it = peer_connections.values().iterator(); it.hasNext(); ) {  //check to make sure we dont already know about this peer
        PeerExchangerItem connection = (PeerExchangerItem)it.next();
        if( connection.isConnectedToPeer( peer ) )  return;  //we already know about this peer via exchange, so ignore discovery
      }
      
      if( !discovered_peers.contains( peer ) ) {
        discovered_peers.addLast( peer );  //add unknown peer

        int max_cache_size = PeerUtils.MAX_CONNECTIONS_PER_TORRENT;
        if( max_cache_size < 1 || max_cache_size > MAX_DISCOVERED_PEERS )  max_cache_size = MAX_DISCOVERED_PEERS;
        
        if( discovered_peers.size() > max_cache_size ) {
          discovered_peers.removeFirst();
        }
      }
    }
    finally{  map_mon.exit();  }
  
protected voidderegisterPeerConnection(PeerItem base_peer_key)

    try{  map_mon.enter();
      peer_connections.remove( base_peer_key );

      //update connection drops
      for( Iterator it = peer_connections.values().iterator(); it.hasNext(); ) {  //go through all remaining connections
        PeerExchangerItem old_connection = (PeerExchangerItem)it.next();
        
        //dont skip seed2seed drop notification, as the dropped peer may not have been seeding initially
        old_connection.notifyDropped( base_peer_key );  //notify existing connection of drop
      } 
    }
    finally{  map_mon.exit();  }
  
public intgetDiscoveredPeerCount()

	  try{  
		  map_mon.enter();
	  
		  return( discovered_peers.size());
		  
	  }finally{  
		
		  map_mon.exit();  
	  } 
  
public PeerItem[]getDiscoveredPeers()

	  try{  
		  map_mon.enter();
	  
		  return((PeerItem[])discovered_peers.toArray( new PeerItem[discovered_peers.size()] ));
		  
	  }finally{  
		
		  map_mon.exit();  
	  }
  
private PeerItem[]getExchangedPeersSortedByLeastPopularFirst()

    HashMap popularity_counts = new HashMap();
    
    try{  map_mon.enter();
      //count popularity of all known peers
      for( Iterator it = peer_connections.values().iterator(); it.hasNext(); ) { 
        PeerExchangerItem connection = (PeerExchangerItem)it.next();
        PeerItem[] peers = connection.getConnectedPeers();
        
        for( int i=0; i < peers.length; i++ ) {
          PeerItem peer = peers[i];
          Integer count = (Integer)popularity_counts.get( peer );
          
          if( count == null ) {
            count = new Integer( 1 );
          }
          else {
            count = new Integer( count.intValue() + 1 );
          }
          
          popularity_counts.put( peer, count );
        }
      }
    }
    finally{  map_mon.exit();  }
    
    if( popularity_counts.isEmpty() )  return null;
        
    //now sort by popularity    
    Map.Entry[] sorted_entries = new Map.Entry[ popularity_counts.size() ];
    popularity_counts.entrySet().toArray( sorted_entries );

    Arrays.sort( sorted_entries, new Comparator() {
      public int compare( Object obj1, Object obj2 ) {
        Map.Entry en1 = (Map.Entry)obj1;
        Map.Entry en2 = (Map.Entry)obj2;
        return ((Integer)en1.getValue()).compareTo( (Integer)en2.getValue() );  //we want least popular in front
      }
    });
    
    PeerItem[] sorted_peers = new PeerItem[ sorted_entries.length ];
      
    for( int i=0; i < sorted_entries.length; i++ ) {
      Map.Entry entry = sorted_entries[i];
      sorted_peers[i] = (PeerItem)entry.getKey();
    } 
    
    return sorted_peers;
  
public PeerItemgetNextOptimisticConnectPeer()
Get the next potential peer for optimistic connect.

return
peer to connect, or null of no optimistic peer available

	  return(getNextOptimisticConnectPeer(0));
  
protected PeerItemgetNextOptimisticConnectPeer(int recursion_count)

    PeerItem peer = null;
    boolean	 discovered_peer = false;
    
    //first see if there are any unknown peers to try
    try{  map_mon.enter();
      if( !discovered_peers.isEmpty() ) {
        peer = (PeerItem)discovered_peers.removeFirst();
        
        discovered_peer	= true;
      }
    }
    finally{  map_mon.exit();  }
    
    //pick one from those obtained via peer exchange if needed
    if( peer == null ) {
      if( cached_peer_popularities == null || popularity_pos == cached_peer_popularities.length ) {  //rebuild needed
        cached_peer_popularities = null;  //clear cache
        
        long time_since_rebuild = SystemTime.getCurrentTime() - last_rebuild_time;
        //only allow exchange list rebuild every few min, otherwise we'll spam attempts endlessly
        if( time_since_rebuild > MIN_REBUILD_WAIT_TIME || time_since_rebuild < 0 ) {
          cached_peer_popularities = getExchangedPeersSortedByLeastPopularFirst();
          popularity_pos = 0;
          last_rebuild_time = SystemTime.getCurrentTime();
        }
      }
      
      if( cached_peer_popularities != null && cached_peer_popularities.length > 0 ) {
        peer = cached_peer_popularities[ popularity_pos ];
        popularity_pos++;
        last_rebuild_time = SystemTime.getCurrentTime();  //ensure rebuild waits min rebuild time after the cache is depleted before trying attempts again
      }
    }

    //to reduce the number of wasted outgoing attempts, we limit how frequently we hand out the same optimistic peer in a given time period
    if( peer != null ) {
    	//check if it's time to rotate the bloom filters
    	
      long diff = SystemTime.getCurrentTime() - last_rotation_time;
      
      if( diff < 0 || diff > BLOOM_ROTATION_PERIOD ) {
        filter_one = filter_two;
        filter_two = BloomFilterFactory.createAddOnly( BLOOM_FILTER_SIZE );
        last_rotation_time = SystemTime.getCurrentTime();
      }
      
      	//check to see if we've already given this peer out optimistically in the last 5-10min
      
      boolean	already_recorded = false;
      
      byte[]	peer_serialisation = peer.getSerialization();
      
      if( filter_one.contains( peer_serialisation ) && recursion_count < 100 ) {
    	  
    	  	// we've recently given this peer, so recursively find another peer to try
      
    	  PeerItem next_peer = getNextOptimisticConnectPeer( recursion_count + 1);
    	  
    	  if ( next_peer != null ){
    		  
    		  	// we've found a better peer that this one. If the existing peer was discovered (as opposed to PEX)
    		  	// then we'll save it for later as it might come in useful
    		  
    		  if ( discovered_peer ){
    			  
    		    try{  map_mon.enter();
  
    		    	discovered_peers.addLast( peer );
   
   			    }finally{  
   			    	map_mon.exit();  
   			    }
    		  }
    		  
    		  peer	= next_peer;
    		  
    		  already_recorded	= true;	// this peer's already in the bloom filters as it has been returned
    		  							// by a recursive call
    	  }
      }
      
      if( !already_recorded ) {  //we've found a suitable peer
        
        filter_one.add( peer_serialisation );
        filter_two.add( peer_serialisation );
      }
    }
    
    return peer;
  
public PeerItemgetSelfPeer()
Get the peer item that represents ourself.

return
self peer, or null if unknown

    /*
    //disabled for now, as getExternalIpAddress() will potential run a full version check every 60s
    if( self_peer == null ) {
      //determine our 'self' info from config
      String ip = VersionCheckClient.getSingleton().getExternalIpAddress();
      if( ip != null && ip.length() > 0 ) {
        self_peer = PeerItemFactory.createPeerItem( ip, NetworkManager.getSingleton().getTCPListeningPortNumber(), 0 );
      }
    }
    */
    
    return self_peer;
  
public java.lang.StringgetString()

	  return("pc=" + peer_connections.size() + ",dp=" + discovered_peers.size());
  
public PeerExchangerItemregisterPeerConnection(PeerItem base_peer_item, PeerExchangerItem.Helper helper)
Register a new peer connection with the database.

param
base_peer_item key
return
registered connection

    try{  map_mon.enter();
      PeerExchangerItem new_connection = new PeerExchangerItem( this, base_peer_item, helper );
      
      //update connection adds
      for( Iterator it = peer_connections.entrySet().iterator(); it.hasNext(); ) {  //go through all existing connections
        Map.Entry entry = (Map.Entry)it.next();
        PeerItem old_key = (PeerItem)entry.getKey();
        PeerExchangerItem old_connection = (PeerExchangerItem)entry.getValue();
        
        if( old_connection.getHelper().isSeed() && new_connection.getHelper().isSeed() ) {
          continue;  //dont exchange seed peers to other seeds
        }
        
        old_connection.notifyAdded( base_peer_item );  //notify existing connection of new one
        new_connection.notifyAdded( old_key );  //notify new connection of existing one for initial exchange
      }

      peer_connections.put( base_peer_item, new_connection );
      return new_connection;
    }
    finally{  map_mon.exit();  }
  
public voidseedStatusChanged(PeerExchangerItem item)

	  	// only bother with non-seed -> seed transitions, the opposite are too rate to bother with
	  
	  if ( item.getHelper().isSeed()){
		 
		  try{  
			  map_mon.enter();
		  
			  for ( Iterator it = peer_connections.values().iterator(); it.hasNext(); ){
				  		  
				  PeerExchangerItem connection = (PeerExchangerItem)it.next();
				  
				  if ( connection != item && connection.getHelper().isSeed()){
					  
					  // System.out.println( "seedStatusChanged: dropping: originator= " + item.getBasePeer().getAddressString() + ",target=" + connection.getBasePeer().getAddressString());
					  
					  connection.notifyDropped( item.getBasePeer() );
					  
					  item.notifyDropped( connection.getBasePeer() );
				  }
			  }
	      }finally{ 
	    	  
	    	  map_mon.exit();
	      }
	  }
  
public voidsetSelfPeer(PeerItem self)
Mark the given peer as ourself.

param
self peer

  self_peer = self;