/* * Mojito Distributed Hash Table (Mojito DHT) * Copyright (C) 2006-2007 LimeWire LLC * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.limewire.mojito.db.impl; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.limewire.mojito.KUID; import org.limewire.mojito.db.DHTValueEntity; import org.limewire.mojito.db.Database; import org.limewire.mojito.settings.DatabaseSettings; /** * Stores a {@link DHTValueEntity} in a map. */ public class DHTValueEntityBag implements Serializable { private static final long serialVersionUID = -439172537233232473L; private static final float SMOOTHING_FACTOR = DatabaseSettings.VALUE_REQUEST_LOAD_SMOOTHING_FACTOR.getValue(); private static final int LOAD_NULLING_DELAY = DatabaseSettings.VALUE_REQUEST_LOAD_NULLING_DELAY.getValue(); private final DatabaseImpl database; private final KUID primaryKey; private final Map<KUID, DHTValueEntity> values = new HashMap<KUID, DHTValueEntity>(); /** * The request load associated with this DHT value bag */ private transient float requestLoad = 0f; /** * The time the request load was last updated */ private transient long lastRequestTime; DHTValueEntityBag(DatabaseImpl database, KUID primaryKey) { this.database = database; this.primaryKey = primaryKey; } public Database getDatabase() { return database; } public KUID getPrimaryKey() { return primaryKey; } public float getRequestLoad(boolean incrementLoad) { if (incrementLoad) { incrementRequestLoad(); } return requestLoad; } public float incrementRequestLoad() { //Use Exponentially weighted moving average (EMA) to compute load //on this particular value bag. //See http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average long now = System.currentTimeMillis(); if(lastRequestTime == 0L) { lastRequestTime = now; requestLoad = 0f; return requestLoad; } //we don't want to skew the results with delays that are too small! float delay = Math.max((now - lastRequestTime)/1000f, 0.01f); //in sec if (delay <= 0f || delay > LOAD_NULLING_DELAY) { lastRequestTime = now; requestLoad = 0f; //we can't trust the value anymore return requestLoad; } //The new requests per second is always a percentage of the //increase over the old one: //newValue = SF*newLoad + (1 - SF)*previousValue //Which is equal to: //newValue = previousValue + SF * (newLoad - previousValue) requestLoad = requestLoad + SMOOTHING_FACTOR*((1f/delay) - requestLoad); lastRequestTime = now; return requestLoad; } public boolean add(DHTValueEntity entity) { if (entity == null) { throw new NullPointerException("DHTValueEntity is null"); } if (!primaryKey.equals(entity.getPrimaryKey())) { throw new IllegalArgumentException(); } values.put(entity.getSecondaryKey(), entity); return true; } public DHTValueEntity get(KUID secondaryKey) { return values.get(secondaryKey); } public DHTValueEntity remove(KUID secondaryKey) { return values.remove(secondaryKey); } public boolean contains(KUID secondaryKey) { return values.containsKey(secondaryKey); } public int size() { return values.size(); } public boolean isEmpty() { return values.isEmpty(); } @Override public int hashCode() { return primaryKey.hashCode(); } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof DHTValueEntityBag)) { return false; } return primaryKey.equals(((DHTValueEntityBag)o).primaryKey); } public Map<KUID, DHTValueEntity> getValues(boolean copy) { if (copy) { return Collections.unmodifiableMap( new HashMap<KUID, DHTValueEntity>(values)); } else { return Collections.unmodifiableMap(values); } } @Override public String toString() { StringBuilder buffer = new StringBuilder(); buffer.append("Bag: ").append(getPrimaryKey()).append("\n"); buffer.append("Load: ").append(getRequestLoad(false)).append("\n"); buffer.append("Values:").append("\n"); for(DHTValueEntity entity : values.values()) { buffer.append(entity).append("\n"); } return buffer.toString(); } }