package freenet.node; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import freenet.keys.NodeCHK; import freenet.support.Logger; import freenet.support.Ticker; public class RequestTracker { private static volatile boolean logMINOR; private static volatile boolean logDEBUG; static { Logger.registerClass(RequestTracker.class); } // The runningLocal* are secondary. That is, we take the lock on the // corresponding running* when accessing runningLocal*. Local requests // have a tag in *both*. private final HashMap<Long,RequestTag> runningCHKGetUIDsBulk; private final HashMap<Long,RequestTag> runningLocalCHKGetUIDsBulk; private final HashMap<Long,RequestTag> runningSSKGetUIDsBulk; private final HashMap<Long,RequestTag> runningLocalSSKGetUIDsBulk; private final HashMap<Long,InsertTag> runningCHKPutUIDsBulk; private final HashMap<Long,InsertTag> runningLocalCHKPutUIDsBulk; private final HashMap<Long,InsertTag> runningSSKPutUIDsBulk; private final HashMap<Long,InsertTag> runningLocalSSKPutUIDsBulk; private final HashMap<Long,OfferReplyTag> runningCHKOfferReplyUIDsBulk; private final HashMap<Long,OfferReplyTag> runningSSKOfferReplyUIDsBulk; private final HashMap<Long,RequestTag> runningCHKGetUIDsRT; private final HashMap<Long,RequestTag> runningLocalCHKGetUIDsRT; private final HashMap<Long,RequestTag> runningSSKGetUIDsRT; private final HashMap<Long,RequestTag> runningLocalSSKGetUIDsRT; private final HashMap<Long,InsertTag> runningCHKPutUIDsRT; private final HashMap<Long,InsertTag> runningLocalCHKPutUIDsRT; private final HashMap<Long,InsertTag> runningSSKPutUIDsRT; private final HashMap<Long,InsertTag> runningLocalSSKPutUIDsRT; private final HashMap<Long,OfferReplyTag> runningCHKOfferReplyUIDsRT; private final HashMap<Long,OfferReplyTag> runningSSKOfferReplyUIDsRT; private final PeerManager peers; private final Ticker ticker; /** RequestSender's currently transferring, by key */ private final HashMap<NodeCHK, RequestSender> transferringRequestSendersRT; private final HashMap<NodeCHK, RequestSender> transferringRequestSendersBulk; /** UIDs of RequestHandler's currently transferring */ private final HashSet<Long> transferringRequestHandlers; RequestTracker(PeerManager peers, Ticker ticker) { this.peers = peers; this.ticker = ticker; runningCHKGetUIDsRT = new HashMap<Long,RequestTag>(); runningLocalCHKGetUIDsRT = new HashMap<Long,RequestTag>(); runningSSKGetUIDsRT = new HashMap<Long,RequestTag>(); runningLocalSSKGetUIDsRT = new HashMap<Long,RequestTag>(); runningCHKPutUIDsRT = new HashMap<Long,InsertTag>(); runningLocalCHKPutUIDsRT = new HashMap<Long,InsertTag>(); runningSSKPutUIDsRT = new HashMap<Long,InsertTag>(); runningLocalSSKPutUIDsRT = new HashMap<Long,InsertTag>(); runningCHKOfferReplyUIDsRT = new HashMap<Long,OfferReplyTag>(); runningSSKOfferReplyUIDsRT = new HashMap<Long,OfferReplyTag>(); runningCHKGetUIDsBulk = new HashMap<Long,RequestTag>(); runningLocalCHKGetUIDsBulk = new HashMap<Long,RequestTag>(); runningSSKGetUIDsBulk = new HashMap<Long,RequestTag>(); runningLocalSSKGetUIDsBulk = new HashMap<Long,RequestTag>(); runningCHKPutUIDsBulk = new HashMap<Long,InsertTag>(); runningLocalCHKPutUIDsBulk = new HashMap<Long,InsertTag>(); runningSSKPutUIDsBulk = new HashMap<Long,InsertTag>(); runningLocalSSKPutUIDsBulk = new HashMap<Long,InsertTag>(); runningCHKOfferReplyUIDsBulk = new HashMap<Long,OfferReplyTag>(); runningSSKOfferReplyUIDsBulk = new HashMap<Long,OfferReplyTag>(); transferringRequestSendersRT = new HashMap<NodeCHK, RequestSender>(); transferringRequestSendersBulk = new HashMap<NodeCHK, RequestSender>(); transferringRequestHandlers = new HashSet<Long>(); } public boolean lockUID(UIDTag tag) { return lockUID(tag.uid, tag.isSSK(), tag.isInsert(), tag.isOfferReply(), tag.wasLocal(), tag.realTimeFlag, tag); } public boolean lockUID(long uid, boolean ssk, boolean insert, boolean offerReply, boolean local, boolean realTimeFlag, UIDTag tag) { // If these are switched around, we must remember to remove from both. if(offerReply) { // local irrelevant for OfferReplyTag's. HashMap<Long,OfferReplyTag> map = getOfferTracker(ssk, realTimeFlag); return innerLock(map, null, (OfferReplyTag)tag, uid, ssk, insert, offerReply, false); } else if(insert) { HashMap<Long,InsertTag> overallMap = getInsertTracker(ssk, false, realTimeFlag); HashMap<Long,InsertTag> localMap = local ? getInsertTracker(ssk, local, realTimeFlag) : null; return innerLock(overallMap, localMap, (InsertTag)tag, uid, ssk, insert, offerReply, local); } else { HashMap<Long,RequestTag> overallMap = getRequestTracker(ssk,false, realTimeFlag); HashMap<Long,RequestTag> localMap = local ? getRequestTracker(ssk,local, realTimeFlag) : null; return innerLock(overallMap, localMap, (RequestTag)tag, uid, ssk, insert, offerReply, local); } } private<T extends UIDTag> boolean innerLock(HashMap<Long, T> overallMap, HashMap<Long, T> localMap, T tag, Long uid, boolean ssk, boolean insert, boolean offerReply, boolean local) { synchronized(overallMap) { if(logMINOR) Logger.minor(this, "Locking "+uid+" ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" size="+overallMap.size(), new Exception("debug")); T oldTag = overallMap.get(uid); if(oldTag != null) { if(oldTag == tag) { Logger.error(this, "Tag already registered: "+tag, new Exception("debug")); } else { return false; } } overallMap.put(uid, tag); if(logMINOR) Logger.minor(this, "Locked "+uid+" ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" size="+overallMap.size()); if(local) { if(logMINOR) Logger.minor(this, "Locking (local) "+uid+" ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" size="+localMap.size(), new Exception("debug")); oldTag = localMap.get(uid); if(oldTag != null) { if(oldTag == tag) { Logger.error(this, "Tag already registered (local): "+tag, new Exception("debug")); } else { // Violates the invariant that local requests are always registered on the main (non-local) map too. Logger.error(this, "Different tag already registered (local) EVEN THOUGH NOT ON MAIN MAP: "+tag, new Exception("debug")); overallMap.remove(uid); return false; } } localMap.put(uid, tag); if(logMINOR) Logger.minor(this, "Locked (local) "+uid+" ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" size="+localMap.size()); } } return true; } /** Only used by UIDTag. */ void unlockUID(UIDTag tag, boolean canFail, boolean noRecord) { unlockUID(tag.uid, tag.isSSK(), tag.isInsert(), canFail, tag.isOfferReply(), tag.wasLocal(), tag.realTimeFlag, tag, noRecord); } protected void unlockUID(long uid, boolean ssk, boolean insert, boolean canFail, boolean offerReply, boolean local, boolean realTimeFlag, UIDTag tag, boolean noRecord) { if(!noRecord) completed(uid); if(offerReply) { HashMap<Long,OfferReplyTag> map = getOfferTracker(ssk, realTimeFlag); innerUnlock(map, null, (OfferReplyTag)tag, uid, ssk, insert, offerReply, false, canFail); } else if(insert) { HashMap<Long,InsertTag> overallMap = getInsertTracker(ssk, false, realTimeFlag); HashMap<Long,InsertTag> localMap = local ? getInsertTracker(ssk,local, realTimeFlag) : null; innerUnlock(overallMap, localMap, (InsertTag)tag, uid, ssk, insert, offerReply, local, canFail); } else { HashMap<Long,RequestTag> overallMap = getRequestTracker(ssk, false, realTimeFlag); HashMap<Long,RequestTag> localMap = local ? getRequestTracker(ssk,local, realTimeFlag) : null; innerUnlock(overallMap, localMap, (RequestTag)tag, uid, ssk, insert, offerReply, local, canFail); } } /** * Do the actual unlock. * @param <T> The type of the tag. * @param overallMap The overall map for this group of requests. LOCKING: * We use the overallMap as lock for both. * @param localMap The local map if any. We check on overallMap and then * remove from both. * @param tag The tag to remove. * @param uid The UID of the tag. * @param ssk Whether it is an SSK. * @param insert Whether it is an insert. * @param offerReply Whether it is an offer reply. * @param local Whether it is local. If it is local we use both maps. If * it is not we expect the latter to be null. * @param canFail */ private<T extends UIDTag> void innerUnlock(HashMap<Long, T> overallMap, HashMap<Long, T> localMap, T tag, Long uid, boolean ssk, boolean insert, boolean offerReply, boolean local, boolean canFail) { synchronized(overallMap) { if(logMINOR) Logger.minor(this, "Unlocking "+uid+" ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" size="+overallMap.size(), new Exception("debug")); if(overallMap.get(uid) != tag) { if(canFail) { if(logMINOR) Logger.minor(this, "Can fail and did fail: removing "+tag+" got "+overallMap.get(uid)+" for "+uid); } else { Logger.error(this, "Removing "+tag+" for "+uid+" returned "+overallMap.get(uid)); } } else overallMap.remove(uid); if(logMINOR) Logger.minor(this, "Unlocked "+uid+" ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" size="+overallMap.size()); if(local) { if(localMap.get(uid) != tag) { if(canFail) { if(logMINOR) Logger.minor(this, "Can fail and did fail (local): removing "+tag+" got "+localMap.get(uid)+" for "+uid); } else { Logger.error(this, "Removing "+tag+" for "+uid+" returned (local) "+localMap.get(uid)); } } else localMap.remove(uid); if(logMINOR) Logger.minor(this, "Unlocked (local) "+uid+" ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" size="+localMap.size()); } else { assert(localMap == null); } } } public static class CountedRequests { private int total; private int expectedTransfersOut; private int expectedTransfersIn; public int total() { return total; } public int expectedTransfersOut() { return expectedTransfersOut; } public int expectedTransfersIn() { return expectedTransfersIn; } } /** Count all requests running globally which match particular parameters. * @param local If true, only include requests which originated locally. * @param ssk If true, count SSK requests, if false, count CHK requests. * @param insert If true, count inserts, otherwise count requests. * @param offer If true, count offer replies (takes precedence over insert). * @param realTimeFlag If true, count real-time requests, if false, count bulk requests. * @param transfersPerInsert Assume that any insert will cause this many outgoing transfers. * This is not predictable, so we use an average. * @param ignoreLocalVsRemote If true, pretend that the request is remote even if it's local * (that is, count imaginary onward transfers etc depending on the request type). * @param counter Transfer counts for all requests will be added to this counter object. * @param counterSourceRestarted Transfer counts for requests whose source restarted (and so * are counted as local) will be added to this counter object. */ public void countRequests(boolean local, boolean ssk, boolean insert, boolean offer, boolean realTimeFlag, int transfersPerInsert, boolean ignoreLocalVsRemote, CountedRequests counter, CountedRequests counterSourceRestarted) { HashMap<Long, ? extends UIDTag> map = getTracker(local, ssk, insert, offer, realTimeFlag); // Map is locked by the non-local version, although we're counting from the local version. HashMap<Long, ? extends UIDTag> mapLock = map; if(local) mapLock = getTracker(false, ssk, insert, offer, realTimeFlag); synchronized(mapLock) { int count = 0; int transfersOut = 0; int transfersIn = 0; int countSR = 0; int transfersOutSR = 0; int transfersInSR = 0; for(Map.Entry<Long, ? extends UIDTag> entry : map.entrySet()) { UIDTag tag = entry.getValue(); // The overall running* map can include local. But the local map can't include non-local. if((!local) && tag.wasLocal) continue; int out = tag.expectedTransfersOut(ignoreLocalVsRemote, transfersPerInsert, true); int in = tag.expectedTransfersIn(ignoreLocalVsRemote, transfersPerInsert, true); count++; transfersOut += out; transfersIn += in; if(counterSourceRestarted != null && tag.countAsSourceRestarted()) { countSR++; transfersOutSR += out; transfersInSR += in; } if(logDEBUG) Logger.debug(this, "UID "+entry.getKey()+" : out "+transfersOut+" in "+transfersIn); } counter.total += count; counter.expectedTransfersIn += transfersIn; counter.expectedTransfersOut += transfersOut; if(counterSourceRestarted != null) { counterSourceRestarted.total += countSR; counterSourceRestarted.expectedTransfersIn += transfersInSR; counterSourceRestarted.expectedTransfersOut += transfersOutSR; } } } /** * Count requests routed to a peer, or accepted from a peer, that match the specified criteria. * PERFORMANCE: There is a map for all requests of a given type (local, ssk, etc). However this * is not divided up by node. FIXME ideally we would countRequests for all PeerNode's * simultaneously when we need data on more than one. FIXME it would be even better if we could * just store the status on the PeerNode's, but the memory usage might be an issue and * synchronization would likely be problematic. * @param source The peer the requests were accepted from or routed to. * @param requestsToNode If true, count requests sent to the node and currently * running. If false, count requests originated by the node. * @param local If true, only include requests which originated locally. * @param ssk If true, count SSK requests, if false, count CHK requests. * @param insert If true, count inserts, otherwise count requests. * @param offer If true, count offer replies (takes precedence over insert). * @param realTimeFlag If true, count real-time requests, if false, count bulk requests. * @param transfersPerInsert Assume that any insert will cause this many outgoing transfers. * This is not predictable, so we use an average. * @param ignoreLocalVsRemote If true, pretend that the request is remote even if it's local * (that is, count imaginary onward transfers etc depending on the request type). * @param counter Transfer counts for all requests will be added to this counter object. * @param counterSR Transfer counts for requests whose source restarted (and so * are counted as local) will be added to this counter object. */ public void countRequests(PeerNode source, boolean requestsToNode, boolean local, boolean ssk, boolean insert, boolean offer, boolean realTimeFlag, int transfersPerInsert, boolean ignoreLocalVsRemote, CountedRequests counter, CountedRequests counterSR) { HashMap<Long, ? extends UIDTag> map = getTracker(local, ssk, insert, offer, realTimeFlag); // Map is locked by the non-local version, although we're counting from the local version. HashMap<Long, ? extends UIDTag> mapLock = map; if(local) mapLock = getTracker(false, ssk, insert, offer, realTimeFlag); synchronized(mapLock) { int count = 0; int transfersOut = 0; int transfersIn = 0; int countSR = 0; int transfersOutSR = 0; int transfersInSR = 0; if(!requestsToNode) { // If a request is adopted by us as a result of a timeout, it can be in the // remote map despite having source == null. However, if a request is in the // local map it will always have source == null. if(source != null && local) return; for(Map.Entry<Long, ? extends UIDTag> entry : map.entrySet()) { UIDTag tag = entry.getValue(); // The overall running* map can include local. But the local map can't include non-local. if((!local) && tag.wasLocal) continue; if(tag.getSource() == source) { int out = tag.expectedTransfersOut(ignoreLocalVsRemote, transfersPerInsert, true); int in = tag.expectedTransfersIn(ignoreLocalVsRemote, transfersPerInsert, true); count++; transfersOut += out; transfersIn += in; if(counterSR != null && tag.countAsSourceRestarted()) { countSR++; transfersOutSR += out; transfersInSR += in; } if(logMINOR) Logger.minor(this, "Counting "+tag+" from "+entry.getKey()+" from "+source+" count now "+count+" out now "+transfersOut+" in now "+transfersIn); } else if(logDEBUG) Logger.debug(this, "Not counting "+entry.getKey()); } if(logMINOR) Logger.minor(this, "Returning count: "+count+" in: "+transfersIn+" out: "+transfersOut); counter.total += count; counter.expectedTransfersIn += transfersIn; counter.expectedTransfersOut += transfersOut; if(counterSR != null) { counterSR.total += countSR; counterSR.expectedTransfersIn += transfersInSR; counterSR.expectedTransfersOut += transfersOutSR; } } else { // hasSourceRestarted is irrelevant for requests *to* a node. // FIXME improve efficiency! for(Map.Entry<Long, ? extends UIDTag> entry : map.entrySet()) { UIDTag tag = entry.getValue(); // The overall running* map can include local. But the local map can't include non-local. if((!local) && tag.wasLocal) continue; // Ordinary requests can be routed to an offered key. // So we *DO NOT* care whether it's an ordinary routed relayed request or a GetOfferedKey, if we are counting outgoing requests. if(tag.currentlyFetchingOfferedKeyFrom(source)) { if(logMINOR) Logger.minor(this, "Counting "+tag+" to "+entry.getKey()); transfersOut += tag.expectedTransfersOut(ignoreLocalVsRemote, transfersPerInsert, false); transfersIn += tag.expectedTransfersIn(ignoreLocalVsRemote, transfersPerInsert, false); count++; } else if(tag.currentlyRoutingTo(source)) { if(logMINOR) Logger.minor(this, "Counting "+tag+" to "+entry.getKey()); transfersOut += tag.expectedTransfersOut(ignoreLocalVsRemote, transfersPerInsert, false); transfersIn += tag.expectedTransfersIn(ignoreLocalVsRemote, transfersPerInsert, false); count++; } else if(logDEBUG) Logger.debug(this, "Not counting "+entry.getKey()); } if(logMINOR) Logger.minor(this, "Counted for "+(local?"local":"remote")+" "+(ssk?"ssk":"chk")+" "+(insert?"insert":"request")+" "+(offer?"offer":"")+" : "+count+" of "+map.size()+" for "+source); counter.total += count; counter.expectedTransfersIn += transfersIn; counter.expectedTransfersOut += transfersOut; } } } /** * Count all requests, by the peer which originated the request. * @param source The peer the requests were accepted from or routed to. * @param local If true, only include requests which originated locally. * @param ssk If true, count SSK requests, if false, count CHK requests. * @param insert If true, count inserts, otherwise count requests. * @param offer If true, count offer replies (takes precedence over insert). * @param realTimeFlag If true, count real-time requests, if false, count bulk requests. * @param transfersPerInsert Assume that any insert will cause this many outgoing transfers. * This is not predictable, so we use an average. * @param ignoreLocalVsRemote If true, pretend that the request is remote even if it's local * (that is, count imaginary onward transfers etc depending on the request type). * @param counterMap Map from PeerNode to CountedRequests counters. We will use "null" for * various cases: local requests, requested that have been adopted because their originator * restarted, requests where the originator PeerNode has been removed from the routing table * etc. */ public void countAllRequestsByIncomingPeer(boolean requestsToNode, boolean local, boolean ssk, boolean insert, boolean offer, boolean realTimeFlag, int transfersPerInsert, boolean ignoreLocalVsRemote, Map<PeerNode, CountedRequests> counterMap) { HashMap<Long, ? extends UIDTag> map = getTracker(local, ssk, insert, offer, realTimeFlag); // Map is locked by the non-local version, although we're counting from the local version. HashMap<Long, ? extends UIDTag> mapLock = map; if(local) mapLock = getTracker(false, ssk, insert, offer, realTimeFlag); synchronized(mapLock) { if(!requestsToNode) { // If a request is adopted by us as a result of a timeout, it can be in the // remote map despite having source == null. However, if a request is in the // local map it will always have source == null. for(Map.Entry<Long, ? extends UIDTag> entry : map.entrySet()) { UIDTag tag = entry.getValue(); // The overall running* map can include local. But the local map can't include non-local. if((!local) && tag.wasLocal) continue; PeerNode source = tag.getSource(); // Can be null in various cases CountedRequests counter = counterMap.get(source); if(counter == null) { counter = new CountedRequests(); counterMap.put(source, counter); } int out = tag.expectedTransfersOut(ignoreLocalVsRemote, transfersPerInsert, true); int in = tag.expectedTransfersIn(ignoreLocalVsRemote, transfersPerInsert, true); counter.total++; counter.expectedTransfersIn += in; counter.expectedTransfersOut += out; } } } } public class WaitingForSlots { int local; int remote; } /** * @return [0] is the number of local requests waiting for slots, [1] is the * number of remote requests waiting for slots. */ public WaitingForSlots countRequestsWaitingForSlots() { WaitingForSlots slots = new WaitingForSlots(); countRequestsWaitingForSlots(runningSSKGetUIDsRT, slots); countRequestsWaitingForSlots(runningCHKGetUIDsRT, slots); countRequestsWaitingForSlots(runningSSKPutUIDsRT, slots); countRequestsWaitingForSlots(runningCHKPutUIDsRT, slots); countRequestsWaitingForSlots(runningSSKOfferReplyUIDsRT, slots); countRequestsWaitingForSlots(runningCHKOfferReplyUIDsRT, slots); countRequestsWaitingForSlots(runningSSKGetUIDsBulk, slots); countRequestsWaitingForSlots(runningCHKGetUIDsBulk, slots); countRequestsWaitingForSlots(runningSSKPutUIDsBulk, slots); countRequestsWaitingForSlots(runningCHKPutUIDsBulk, slots); return slots; } private void countRequestsWaitingForSlots(HashMap<Long, ? extends UIDTag> runningUIDs, WaitingForSlots slots) { // FIXME use a counter, but that means make sure it always removes it when something bad happens. synchronized(runningUIDs) { for(UIDTag tag : runningUIDs.values()) { if(!tag.isWaitingForSlot()) continue; if(tag.isLocal()) slots.local++; else slots.remote++; } } } void reassignTagToSelf(UIDTag tag) { // The tag remains remote, but we flag it as adopted. tag.reassignToSelf(); } private HashMap<Long, ? extends UIDTag> getTracker(boolean local, boolean ssk, boolean insert, boolean offer, boolean realTimeFlag) { if(offer) return getOfferTracker(ssk, realTimeFlag); else if(insert) return getInsertTracker(ssk, local, realTimeFlag); else return getRequestTracker(ssk, local, realTimeFlag); } private HashMap<Long, RequestTag> getRequestTracker(boolean ssk, boolean local, boolean realTimeFlag) { if(realTimeFlag) { if(ssk) { return local ? runningLocalSSKGetUIDsRT : runningSSKGetUIDsRT; } else { return local ? runningLocalCHKGetUIDsRT : runningCHKGetUIDsRT; } } else { if(ssk) { return local ? runningLocalSSKGetUIDsBulk : runningSSKGetUIDsBulk; } else { return local ? runningLocalCHKGetUIDsBulk : runningCHKGetUIDsBulk; } } } private HashMap<Long, InsertTag> getInsertTracker(boolean ssk, boolean local, boolean realTimeFlag) { if(realTimeFlag) { if(ssk) { return local ? runningLocalSSKPutUIDsRT : runningSSKPutUIDsRT; } else { return local ? runningLocalCHKPutUIDsRT : runningCHKPutUIDsRT; } } else { if(ssk) { return local ? runningLocalSSKPutUIDsBulk : runningSSKPutUIDsBulk; } else { return local ? runningLocalCHKPutUIDsBulk : runningCHKPutUIDsBulk; } } } private HashMap<Long, OfferReplyTag> getOfferTracker(boolean ssk, boolean realTimeFlag) { if(realTimeFlag) return ssk ? runningSSKOfferReplyUIDsRT : runningCHKOfferReplyUIDsRT; else return ssk ? runningSSKOfferReplyUIDsBulk : runningCHKOfferReplyUIDsBulk; } // Must include bulk inserts so fairly long. // 21 minutes is enough for a fatal timeout. static final long TIMEOUT = MINUTES.toMillis(21); void startDeadUIDChecker() { ticker.queueTimedJob(deadUIDChecker, TIMEOUT); } private Runnable deadUIDChecker = new Runnable() { @Override public void run() { try { checkUIDs(runningSSKGetUIDsRT); checkUIDs(runningCHKGetUIDsRT); checkUIDs(runningSSKPutUIDsRT); checkUIDs(runningCHKPutUIDsRT); checkUIDs(runningSSKOfferReplyUIDsRT); checkUIDs(runningCHKOfferReplyUIDsRT); checkUIDs(runningSSKGetUIDsBulk); checkUIDs(runningCHKGetUIDsBulk); checkUIDs(runningSSKPutUIDsBulk); checkUIDs(runningCHKPutUIDsBulk); checkUIDs(runningSSKOfferReplyUIDsBulk); checkUIDs(runningCHKOfferReplyUIDsBulk); } finally { ticker.queueTimedJob(this, SECONDS.toMillis(60)); } } private void checkUIDs(HashMap<Long, ? extends UIDTag> map) { Long[] uids; UIDTag[] tags; synchronized(map) { uids = map.keySet().toArray(new Long[map.size()]); tags = map.values().toArray(new UIDTag[map.size()]); } long now = System.currentTimeMillis(); for(int i=0;i<uids.length;i++) { tags[i].maybeLogStillPresent(now, uids[i]); } } }; public void onRestartOrDisconnect(PeerNode pn) { onRestartOrDisconnect(pn, runningSSKGetUIDsRT); onRestartOrDisconnect(pn, runningCHKGetUIDsRT); onRestartOrDisconnect(pn, runningSSKPutUIDsRT); onRestartOrDisconnect(pn, runningCHKPutUIDsRT); onRestartOrDisconnect(pn, runningSSKOfferReplyUIDsRT); onRestartOrDisconnect(pn, runningCHKOfferReplyUIDsRT); onRestartOrDisconnect(pn, runningSSKGetUIDsBulk); onRestartOrDisconnect(pn, runningCHKGetUIDsBulk); onRestartOrDisconnect(pn, runningSSKPutUIDsBulk); onRestartOrDisconnect(pn, runningCHKPutUIDsBulk); onRestartOrDisconnect(pn, runningSSKOfferReplyUIDsBulk); onRestartOrDisconnect(pn, runningCHKOfferReplyUIDsBulk); } private void onRestartOrDisconnect(PeerNode pn, HashMap<Long, ? extends UIDTag> uids) { synchronized(uids) { for(UIDTag tag : uids.values()) { if(tag.isSource(pn)) tag.onRestartOrDisconnectSource(); } } } public int getNumSSKRequests() { int total = 0; // running* include all requests, local and remote. synchronized(runningSSKGetUIDsBulk) { total += runningSSKGetUIDsBulk.size(); } synchronized(runningSSKGetUIDsRT) { total += runningSSKGetUIDsRT.size(); } return total; } public int getNumCHKRequests() { int total = 0; synchronized(runningCHKGetUIDsBulk) { total += runningCHKGetUIDsBulk.size(); } synchronized(runningCHKGetUIDsRT) { total += runningCHKGetUIDsRT.size(); } return total; } public int getNumSSKInserts() { int total = 0; synchronized(runningSSKPutUIDsBulk) { total += runningSSKPutUIDsBulk.size(); } synchronized(runningSSKPutUIDsRT) { total += runningSSKPutUIDsRT.size(); } return total; } public int getNumCHKInserts() { int total = 0; synchronized(runningCHKPutUIDsBulk) { total += runningCHKPutUIDsBulk.size(); } synchronized(runningCHKPutUIDsRT) { total += runningCHKPutUIDsRT.size(); } return total; } public int getNumLocalSSKRequests() { int total = 0; synchronized(runningSSKGetUIDsBulk) { total += runningLocalSSKGetUIDsBulk.size(); } synchronized(runningSSKGetUIDsRT) { total += runningLocalSSKGetUIDsRT.size(); } return total; } public int getNumLocalCHKRequests() { int total = 0; synchronized(runningCHKGetUIDsBulk) { total += runningLocalCHKGetUIDsBulk.size(); } synchronized(runningCHKGetUIDsRT) { total += runningLocalCHKGetUIDsRT.size(); } return total; } public int getNumRemoteCHKRequests() { int total = 0; synchronized(runningCHKGetUIDsBulk) { total += runningCHKGetUIDsBulk.size(); total -= runningLocalCHKGetUIDsBulk.size(); } synchronized(runningCHKGetUIDsRT) { total += runningCHKGetUIDsRT.size(); total -= runningLocalCHKGetUIDsRT.size(); } return total; } public int getNumRemoteSSKRequests() { int total = 0; synchronized(runningSSKGetUIDsBulk) { total += runningSSKGetUIDsBulk.size(); total -= runningLocalSSKGetUIDsBulk.size(); } synchronized(runningSSKGetUIDsRT) { total += runningSSKGetUIDsRT.size(); total -= runningLocalSSKGetUIDsRT.size(); } return total; } public int getNumLocalCHKInserts() { int total = 0; synchronized(runningCHKPutUIDsBulk) { total += runningLocalCHKPutUIDsBulk.size(); } synchronized(runningCHKPutUIDsRT) { total += runningLocalCHKPutUIDsRT.size(); } return total; } public int getNumLocalSSKInserts() { int total = 0; synchronized(runningSSKPutUIDsBulk) { total += runningLocalSSKPutUIDsBulk.size(); } synchronized(runningSSKPutUIDsRT) { total += runningLocalSSKPutUIDsRT.size(); } return total; } public int getNumRemoteCHKInserts() { int total = 0; synchronized(runningCHKPutUIDsBulk) { total += runningCHKPutUIDsBulk.size() - runningLocalCHKPutUIDsBulk.size(); } synchronized(runningCHKPutUIDsRT) { total += runningCHKPutUIDsRT.size() - runningLocalCHKPutUIDsRT.size(); } return total; } public int getNumRemoteSSKInserts() { int total = 0; synchronized(runningSSKPutUIDsRT) { total += runningSSKPutUIDsRT.size() - runningLocalSSKPutUIDsRT.size(); } synchronized(runningSSKPutUIDsBulk) { total += runningSSKPutUIDsBulk.size() - runningLocalSSKPutUIDsBulk.size(); } return total; } public int getNumSSKOfferReplies() { int total = 0; synchronized(runningSSKOfferReplyUIDsRT) { total += runningSSKOfferReplyUIDsRT.size(); } synchronized(runningSSKOfferReplyUIDsBulk) { total += runningSSKOfferReplyUIDsBulk.size(); } return total; } public int getNumCHKOfferReplies() { int total = 0; synchronized(runningCHKOfferReplyUIDsRT) { total += runningCHKOfferReplyUIDsRT.size(); } synchronized(runningCHKOfferReplyUIDsBulk) { total += runningCHKOfferReplyUIDsBulk.size(); } return total; } public int getNumSSKOfferReplies(boolean realTimeFlag) { return realTimeFlag ? runningSSKOfferReplyUIDsRT.size() : runningSSKOfferReplyUIDsBulk.size(); } public int getNumCHKOfferReplies(boolean realTimeFlag) { return realTimeFlag ? runningCHKOfferReplyUIDsRT.size() : runningCHKOfferReplyUIDsBulk.size(); } public void addRunningUIDs(List<Long> list) { addRunningUIDs(runningSSKGetUIDsRT, list); addRunningUIDs(runningCHKGetUIDsRT, list); addRunningUIDs(runningSSKPutUIDsRT, list); addRunningUIDs(runningCHKPutUIDsRT, list); addRunningUIDs(runningSSKOfferReplyUIDsRT, list); addRunningUIDs(runningCHKOfferReplyUIDsRT, list); addRunningUIDs(runningSSKGetUIDsBulk, list); addRunningUIDs(runningCHKGetUIDsBulk, list); addRunningUIDs(runningSSKPutUIDsBulk, list); addRunningUIDs(runningCHKPutUIDsBulk, list); addRunningUIDs(runningSSKOfferReplyUIDsBulk, list); addRunningUIDs(runningCHKOfferReplyUIDsBulk, list); } private void addRunningUIDs(HashMap<Long, ? extends UIDTag> runningUIDs, List<Long> list) { synchronized(runningUIDs) { list.addAll(runningUIDs.keySet()); } } public int getTotalRunningUIDsAlt() { return this.runningCHKGetUIDsRT.size() + this.runningCHKPutUIDsRT.size() + this.runningSSKGetUIDsRT.size() + this.runningSSKPutUIDsRT.size() + this.runningSSKOfferReplyUIDsRT.size() + this.runningCHKOfferReplyUIDsRT.size() + this.runningCHKGetUIDsBulk.size() + this.runningCHKPutUIDsBulk.size() + this.runningSSKGetUIDsBulk.size() + this.runningSSKPutUIDsBulk.size() + this.runningSSKOfferReplyUIDsBulk.size() + this.runningCHKOfferReplyUIDsBulk.size(); } private ArrayList<Long> completedBuffer = new ArrayList<Long>(); // Every this many slots, we tell all the PeerMessageQueue's to remove the old Items for the ID's in question. // This prevents memory DoS amongst other things. static final int COMPLETED_THRESHOLD = 128; /** * A request completed (regardless of success). */ void completed(long id) { Long[] list; synchronized (completedBuffer) { completedBuffer.add(id); if(completedBuffer.size() < COMPLETED_THRESHOLD) return; list = completedBuffer.toArray(new Long[completedBuffer.size()]); completedBuffer.clear(); } for(PeerNode pn : peers.myPeers()) { if(!pn.isRoutingCompatible()) continue; pn.removeUIDsFromMessageQueues(list); } } public RequestSender getTransferringRequestSenderByKey(NodeCHK key, boolean realTimeFlag) { HashMap<NodeCHK, RequestSender> transferringRequestSenders = realTimeFlag ? transferringRequestSendersRT : transferringRequestSendersBulk; synchronized(transferringRequestSenders) { return transferringRequestSenders.get(key); } } /** * Add a transferring RequestSender to our HashMap. * Should only be called by UIDTag. */ public void addTransferringSender(NodeCHK key, RequestSender sender) { HashMap<NodeCHK, RequestSender> transferringRequestSenders = sender.realTimeFlag ? transferringRequestSendersRT : transferringRequestSendersBulk; synchronized(transferringRequestSenders) { transferringRequestSenders.put(key, sender); } } /** Should only be called by RequestTag. */ void addTransferringRequestHandler(long id) { synchronized(transferringRequestHandlers) { transferringRequestHandlers.add(id); } } /** Should only be called by RequestTag. */ void removeTransferringRequestHandler(long id) { synchronized(transferringRequestHandlers) { transferringRequestHandlers.remove(id); } } /** * Remove a sender from the set of currently transferring senders. */ public void removeTransferringSender(NodeCHK key, RequestSender sender) { HashMap<NodeCHK, RequestSender> transferringRequestSenders = sender.realTimeFlag ? transferringRequestSendersRT : transferringRequestSendersBulk; synchronized(transferringRequestSenders) { // RequestSender rs = (RequestSender) transferringRequestSenders.remove(key); // if(rs != sender) { // Logger.error(this, "Removed "+rs+" should be "+sender+" for "+key+" in removeTransferringSender"); // } // Since there is no request coalescing, we only remove it if it matches, // and don't complain if it doesn't. if(transferringRequestSenders.get(key) == sender) transferringRequestSenders.remove(key); } } public int getNumTransferringRequestSenders() { int total = 0; synchronized(transferringRequestSendersRT) { total += transferringRequestSendersRT.size(); } synchronized(transferringRequestSendersBulk) { total += transferringRequestSendersBulk.size(); } return total; } public int getNumTransferringRequestHandlers() { synchronized(transferringRequestHandlers) { return transferringRequestHandlers.size(); } } }