Package plugins.Freetalk

Source Code of plugins.Freetalk.IdentityStatistics

/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package plugins.Freetalk;

import plugins.Freetalk.MessageList.MessageListID;
import plugins.Freetalk.exceptions.NoSuchMessageListException;
import freenet.support.Logger;

/**
* This class stores statistics about a single identity.
* It is used for {@link MessageList} and {@link Message} download decisions.
* It also speeds up Freetalk by caching the results of complex database queries.
*
* @author xor (xor@freenetproject.org)
*/
public final class IdentityStatistics extends Persistent {
 
  @IndexedField
  private final Identity mIdentity;

  /**
   * The index of the latest message list which is available for this identity.
   */
  private long mHighestFetchedMessageListIndex = -1;
 
  /**
   * The lowest index of which the message list is fetched and all message lists after that index are fetched as well.
   */
  private long mLowestFetchedMessageListIndex = -1;
 
 
  /* These booleans are used for preventing the construction of log-strings if logging is disabled (for saving some cpu cycles) */
 
  private static transient volatile boolean logDEBUG = false;
  private static transient volatile boolean logMINOR = false;
 
  static {
    Logger.registerClass(IdentityStatistics.class);
  }
 
 
  protected IdentityStatistics(final Identity myIdentity) {
    mIdentity = myIdentity;
  }
 
  @Override
  public void databaseIntegrityTest() throws Exception {
    checkedActivate(1);
   
    if(mIdentity == null)
      throw new NullPointerException("mIdentity==null");
   
    if((mLowestFetchedMessageListIndex == -1) ^ (mHighestFetchedMessageListIndex == -1))
      throw new IllegalStateException("mLowestFetchedMessageListIndex==" + mLowestFetchedMessageListIndex
          + "; mHighestFetchedMessageListIndex==" + mHighestFetchedMessageListIndex);
   
    if(mLowestFetchedMessageListIndex >= 0) {
      final MessageManager messageManager = mFreetalk.getMessageManager();
     
      for(long i = mLowestFetchedMessageListIndex; i <= mHighestFetchedMessageListIndex; ++i) {
        try {
          messageManager.getMessageList(MessageListID.construct(getIdentity(), i).toString());
        } catch(NoSuchMessageListException e) {
          throw new IllegalStateException("Missing index " + i + "; mLowestFetchedMessageListIndex==" + mLowestFetchedMessageListIndex
              + "; mHighestFetchedMessageListIndex==" + mHighestFetchedMessageListIndex);
        }
      }
    }
   
    // TODO: If lowest==highest==-1, check whether index 0 is available.. if it is, they should be 0 at least...
  }
 
  public final Identity getIdentity() {
    checkedActivate(1);
    if(mIdentity instanceof Persistent) ((Persistent)mIdentity).initializeTransient(mFreetalk);
    return mIdentity;
  }
 

  /**
   * Get the the message list index number such that all message lists with index x are fetched for:<br>
   * <code>getIndexOfOldestAvailableMessageList() <= x <= getIndexOfLatestAvailableMessageList()</code><br><br>
   *
   * In other words: It is guaranteed that no slot between the latest and oldest reported index is empty.
   * If there is a slot missing, the statistics object tries to chose the latest/lowest numbers biased towards the higher available
   * fragment instead of the lower one.
   *
   * It is NOT guaranteed that all message lists below the returned number are not fetched, some might exist in the database already!
   * - The numbers returned by this function are supposed to be used for message list fetching, its not bad if we fetch the same message list
   * twice.
   */
  public final synchronized long getIndexOfLatestAvailableMessageList() throws NoSuchMessageListException {
    checkedActivate(1);
   
    if(mHighestFetchedMessageListIndex == -1)
      throw new NoSuchMessageListException(null);
   
    return mHighestFetchedMessageListIndex;
  }
 
  private final synchronized void setIndexOfLatestAvailableMessageList(long index) {
    checkedActivate(1);
   
    if(index < mLowestFetchedMessageListIndex)
      throw new RuntimeException("Illegal index: "+ index);
   
    mHighestFetchedMessageListIndex = index;
    if(mLowestFetchedMessageListIndex == -1) mLowestFetchedMessageListIndex = index;
  }
 
  /**
   * Get the the message list index number such that all message lists with index x are fetched for:<br>
   * <code>getIndexOfOldestAvailableMessageList() <= x <= getIndexOfLatestAvailableMessageList()</code><br><br>
   *
   * In other words: It is guaranteed that no slot between the latest and oldest reported index is empty.
   * If there is a slot missing, the statistics object tries to chose the latest/lowest numbers biased towards the higher available
   * fragment instead of the lower one.
   *
   * It is NOT guaranteed that all message lists below the returned number are not fetched, some might exist in the database already!
   * - The numbers returned by this function are supposed to be used for message list fetching, its not bad if we fetch the same message list
   * twice.
   */
  public final synchronized long getIndexOfOldestAvailableMessageList() throws NoSuchMessageListException {
    checkedActivate(1);
   
    if(mLowestFetchedMessageListIndex == -1)
      throw new NoSuchMessageListException(null);
   
    return mLowestFetchedMessageListIndex;
  }
 
  private final synchronized void setIndexOfOldestAvailableMessageList(long index) {
    checkedActivate(1);
   
    if(index > mHighestFetchedMessageListIndex)
      throw new RuntimeException("Illegal index: " + index);
   
    mLowestFetchedMessageListIndex = index;
    if(mHighestFetchedMessageListIndex == -1) mHighestFetchedMessageListIndex = index;
  }
 
  private final void expandHighestAvailableMessageListIndex() {
    final MessageManager messageManager = mFreetalk.getMessageManager();
   
    // TODO: Optimization: We should observe the typical amount of lists which are skipped by this function. If it is high then it
    // might be faster to query a sorted set of the message lists with higher index instead of skipping them one by one....
   
    checkedActivate(1);
   
    long higherIndex;
   
    do
      higherIndex = mHighestFetchedMessageListIndex+1;
     
      try {
        messageManager.getMessageList(MessageListID.construct(getIdentity(), higherIndex).toString());
        mHighestFetchedMessageListIndex = higherIndex;
      } catch(NoSuchMessageListException e) { }
     
    } while(mHighestFetchedMessageListIndex == higherIndex);
   
    assert(messageListIndicesAreValid());
  }
 
  private final void expandLowestAvailableMessageListIndex() {
    final MessageManager messageManager = mFreetalk.getMessageManager();
   
    // TODO: Optimization: We should observe the typical amount of lists which are skipped by this function. If it is high then it
    // might be faster to query a sorted set of the message lists with higher index instead of skipping them one by one....
   
    checkedActivate(1);
   
    long lowerIndex;
   
    do {
      lowerIndex = mLowestFetchedMessageListIndex-1;
      if(lowerIndex < 0)
        break;
     
      try {
        messageManager.getMessageList(MessageListID.construct(getIdentity(), lowerIndex).toString());
        mLowestFetchedMessageListIndex = lowerIndex;
      } catch(NoSuchMessageListException e) { }
     
    } while(mLowestFetchedMessageListIndex == lowerIndex);
   
    assert(messageListIndicesAreValid());
  }
 
  // TODO: This is public since we need it in WoTMessageManager. It could be made private if WoTMessageManager.onMessageListFetchFailed
  // called a method of MessageManager for calling this function instead of calling it directly.
  public final void onMessageListFetched(final MessageList messageList) {
    if(messageList instanceof OwnMessageList)
      throw new RuntimeException("OwnMessageList are not allowed: " + messageList);
   
    checkedActivate(1);
   
    final long newIndex = messageList.getIndex();
   
    if(newIndex >= mLowestFetchedMessageListIndex && newIndex <= mHighestFetchedMessageListIndex) {
      Logger.warning(this, "MessageList was fetched already: " + messageList);
      return;
    }
   
    if(newIndex > mHighestFetchedMessageListIndex+1) {
      // When the list of fetched indices is fragmented, there are two possible biases which this function can have:
      // Set its boundaries to the lowest available fragment or to the highest available fragment.
      // - We set the boundaries to the highest available fragment so the message list fetcher fetches new message lists fast.
      setIndexOfLatestAvailableMessageList(newIndex);
      setIndexOfOldestAvailableMessageList(newIndex);
      assert(messageListIndicesAreValid());
      expandHighestAvailableMessageListIndex();
      expandLowestAvailableMessageListIndex();
      return;
    }
   
    if(newIndex == mHighestFetchedMessageListIndex+1) {
      setIndexOfLatestAvailableMessageList(newIndex);
      assert(messageListIndicesAreValid());
      expandHighestAvailableMessageListIndex();
      return;
    }
   
    if(newIndex == mLowestFetchedMessageListIndex-1) {
      setIndexOfOldestAvailableMessageList(newIndex);
      assert(messageListIndicesAreValid());
      expandLowestAvailableMessageListIndex();
      return;
    }
   
    if(newIndex < mLowestFetchedMessageListIndex-1) {
      // The mLowestFetchedMessageListIndex-1 index will definitely not be available, no need to check
      // expandLowestAvailableMessageListIndex();
      return;
    }
   
    // This should not happen:
    throw new RuntimeException("Unhandled case: lowest index=" + mLowestFetchedMessageListIndex + "; "
        + "highest index=" + mHighestFetchedMessageListIndex + "; "
        + "new index=" + newIndex);
  }
 
  protected final void onMessageListDeleted(final MessageList messageList) {
    if(messageList instanceof OwnMessageList)
      throw new RuntimeException("OwnMessageList are not allowed: " + messageList);
   
    checkedActivate(1);
   
    final long deletedIndex = messageList.getIndex();
   
    if(deletedIndex < mLowestFetchedMessageListIndex || deletedIndex > mHighestFetchedMessageListIndex)
      return;
   
    if(mLowestFetchedMessageListIndex == mHighestFetchedMessageListIndex) {
      // TODO: Optimization: If this happens often and in a specific order during identity deletion it might cause O(n*n)...
      // Check whether we need to do something against that.
      setIndexOfOldestAvailableMessageList(-1);
      setIndexOfLatestAvailableMessageList(-1);
      assert(messageListIndicesAreValid());
      expandHighestAvailableMessageListIndex();
      return;
    }

    if(deletedIndex == mHighestFetchedMessageListIndex) {
      setIndexOfLatestAvailableMessageList(deletedIndex-1);
      assert(messageListIndicesAreValid());
      return;
    }
   
    if(deletedIndex >= mLowestFetchedMessageListIndex) {
      setIndexOfOldestAvailableMessageList(deletedIndex+1);
      assert(messageListIndicesAreValid());
      return;
    }
 
    // This should not happen:
    throw new RuntimeException("Unhandled case: lowest index=" + mLowestFetchedMessageListIndex + "; "
        + "highest index=" + mHighestFetchedMessageListIndex + "; "
        + "deleted index=" + deletedIndex);
  }
 
  private final boolean messageListIndicesAreValid() {
    final MessageManager messageManager = mFreetalk.getMessageManager();
   
    checkedActivate(1);
   
    if((mLowestFetchedMessageListIndex == -1) && (mHighestFetchedMessageListIndex == -1))
      return true;
   
    if((mLowestFetchedMessageListIndex == -1) ^ (mHighestFetchedMessageListIndex == -1)) {
      // Does not make sense to have only one of them be -1
      Logger.error(this, "Wrong index numbers found: lowest==" + mLowestFetchedMessageListIndex +
          "; highest==" + mHighestFetchedMessageListIndex);
      return false;
    }
   
    for(long i = mLowestFetchedMessageListIndex; i <= mHighestFetchedMessageListIndex; ++i) {
      try  {
        messageManager.getMessageList(MessageListID.construct(getIdentity(), i).toString());
      } catch(NoSuchMessageListException e) {
        Logger.error(this, "Wrong index numbers found: lowest==" + mLowestFetchedMessageListIndex +
            "; highest==" + mHighestFetchedMessageListIndex);
        return false;
      }
    }
   
    return true;
  }

  // TODO: This is public since we need it in WoTMessageManager. It could be made private if WoTMessageManager.onMessageListFetchFailed
  // called a method of MessageManager for calling this function instead of calling it directly.
  public final void storeWithoutCommit() {
    try {   
      // 1 is the maximal depth of all getter functions. You have to adjust this when introducing new member variables.
      checkedActivate(1);
     
      throwIfNotStored(mIdentity);
     
      if(logDEBUG) Logger.debug(this, "Storing for " + getIdentity() + " with mLowestFetchedMessageListIndex == " + mLowestFetchedMessageListIndex
          + "; mHighestFetchedMessageListIndex == " + mHighestFetchedMessageListIndex);
     
      checkedStore();
    }
    catch(final RuntimeException e) {
      checkedRollbackAndThrow(e);
    }
  }

}
TOP

Related Classes of plugins.Freetalk.IdentityStatistics

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.