From 083114ce10eb01e9a51fde20d5ef81a7ea148f83 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Tue, 10 Jul 2018 19:04:18 +0100 Subject: [PATCH] wip on HostPool and test --- .../groovy/com/muwire/hostcache/Host.groovy | 11 ++ .../com/muwire/hostcache/HostCache.groovy | 4 + .../com/muwire/hostcache/HostPool.groovy | 62 ++++++- .../com/muwire/hostcache/HostPoolTest.groovy | 158 +++++++++++++++++- 4 files changed, 227 insertions(+), 8 deletions(-) diff --git a/host-cache/src/main/groovy/com/muwire/hostcache/Host.groovy b/host-cache/src/main/groovy/com/muwire/hostcache/Host.groovy index 31239e1e..641154a1 100644 --- a/host-cache/src/main/groovy/com/muwire/hostcache/Host.groovy +++ b/host-cache/src/main/groovy/com/muwire/hostcache/Host.groovy @@ -4,4 +4,15 @@ class Host { def destination def verifyTime + boolean leafSlots + boolean peerSlots + int verificationFailures + + public int hashCode() { + return destination.hashCode() + } + + public boolean equals(other) { + return destination.equals(other.destination) + } } diff --git a/host-cache/src/main/groovy/com/muwire/hostcache/HostCache.groovy b/host-cache/src/main/groovy/com/muwire/hostcache/HostCache.groovy index 62fb9036..aaf06d77 100644 --- a/host-cache/src/main/groovy/com/muwire/hostcache/HostCache.groovy +++ b/host-cache/src/main/groovy/com/muwire/hostcache/HostCache.groovy @@ -83,6 +83,10 @@ public class HostCache { payload = dissector.getPayload() payload = json.parse(payload) + if (payload.type == null) { + println "WARN: type field missing" + return + } switch(payload.type) { case "Ping" : println "Ping" diff --git a/host-cache/src/main/groovy/com/muwire/hostcache/HostPool.groovy b/host-cache/src/main/groovy/com/muwire/hostcache/HostPool.groovy index 1e541730..b01c71ef 100644 --- a/host-cache/src/main/groovy/com/muwire/hostcache/HostPool.groovy +++ b/host-cache/src/main/groovy/com/muwire/hostcache/HostPool.groovy @@ -1,20 +1,72 @@ package com.muwire.hostcache +import java.util.stream.Collectors + class HostPool { + final def maxFailures + final def maxAge + def verified = new HashSet() - - synchronized def getVerified(int max) { - def asList = verified.asList() + def unverified = new HashSet() + + HostPool(maxFailures, maxAge) { + this.maxAge = maxAge + this.maxFailures = maxFailures + } + + synchronized def getVerified(int max, boolean leaf) { + if (verified.isEmpty()) { + return Collections.emptyList() + } + def asList = verified.stream().filter({ it -> leaf ? it.leafSlots : it.peerSlots}).collect(Collectors.toList()) Collections.shuffle(asList) - return asList[0..max].collect { it -> it.destination } + + return asList[0..Math.min(max, asList.size()) -1] } synchronized def addUnverified(host) { - + unverified.add(host) + } + + synchronized def getUnverified(int max) { + if (unverified.isEmpty()) { + return Collections.emptyList() + } + def asList = unverified.asList() + Collections.shuffle(asList) + return asList[0..(Math.min(max, asList.size())-1)] } synchronized def verify(host) { + if (!unverified.remove(host)) + throw new IllegalArgumentException() + host.verifyTime = System.currentTimeMillis(); + host.verificationFailures = 0 + verified.add(host) + } + + synchronized def fail(host) { + if (!unverified.contains(host)) + throw new IllegalArgumentException() + host.verificationFailures++ + } + + synchronized def age() { + final long now = System.currentTimeMillis() + for (Iterator iter = verified.iterator(); iter.hasNext();) { + def host = iter.next() + if (host.verifyTime + maxAge < now) { + iter.remove() + unverified.add(host) + } + } + for (Iterator iter = unverified.iterator(); iter.hasNext();) { + def host = iter.next() + if (host.verificationFailures >= maxFailures) { + iter.remove() + } + } } } diff --git a/host-cache/src/test/groovy/com/muwire/hostcache/HostPoolTest.groovy b/host-cache/src/test/groovy/com/muwire/hostcache/HostPoolTest.groovy index ff562975..e1167952 100644 --- a/host-cache/src/test/groovy/com/muwire/hostcache/HostPoolTest.groovy +++ b/host-cache/src/test/groovy/com/muwire/hostcache/HostPoolTest.groovy @@ -1,11 +1,163 @@ package com.muwire.hostcache -import org.junit.Test +import org.junit.* class HostPoolTest { - @Test - def testGetVerified() { + def hp + Host freeLeafs, freePeers, freeBoth, freeNone + + @Before + void before() { + hp = new HostPool(3, 10) + freeLeafs = new Host() + freeLeafs.destination = "freeLeafs" + freeLeafs.leafSlots = true + + freePeers = new Host() + freePeers.destination = "freePeers" + freePeers.peerSlots = true + + freeBoth = new Host() + freeBoth.destination = "freeBoth" + freeBoth.leafSlots = true + freeBoth.peerSlots = true + + freeNone = new Host() + freeNone.destination = "freeNone" + } + + def addAllUnverified() { + hp.addUnverified(freeLeafs) + hp.addUnverified(freePeers) + hp.addUnverified(freeBoth) + hp.addUnverified(freeNone) + } + + def verifyAll() { + hp.verify(freeBoth) + hp.verify(freeLeafs) + hp.verify(freePeers) + hp.verify(freeNone) + } + + @Test + void testNoVerified() { + assert hp.getVerified(1, true).isEmpty() + assert hp.getVerified(1, false).isEmpty() + + addAllUnverified() + + assert hp.getVerified(1, true).isEmpty() + assert hp.getVerified(1, false).isEmpty() + } + + @Test + void testOneVerified() { + addAllUnverified() + hp.verify(freeBoth) + + assert hp.getVerified(10, true).contains(freeBoth) + assert hp.getVerified(10, false).contains(freeBoth) + } + + @Test + void testFilterByType() { + addAllUnverified() + verifyAll() + def verifiedLeafSlots = hp.getVerified(10, true) + assert verifiedLeafSlots.size() == 2 + assert verifiedLeafSlots.contains(freeBoth) + assert verifiedLeafSlots.contains(freeLeafs) + + def verifiedPeerSlots = hp.getVerified(10, false) + assert verifiedPeerSlots.size() == 2 + assert verifiedPeerSlots.contains(freeBoth) + assert verifiedPeerSlots.contains(freePeers) + } + + @Test + void getFewerThanAvailable() { + addAllUnverified() + verifyAll() + def verifiedLeafSlots = hp.getVerified(1, true) + assert verifiedLeafSlots.size() == 1 + assert verifiedLeafSlots.contains(freeBoth) || verifiedLeafSlots.contains(freeLeafs) + } + + @Test + void getUnverified() { + assert hp.getUnverified().isEmpty() + + addAllUnverified() + + def allUnverified = hp.getUnverified(10) + assert allUnverified.size() == 4 + assert allUnverified.contains(freeLeafs) + assert allUnverified.contains(freePeers) + assert allUnverified.contains(freeBoth) + assert allUnverified.contains(freeNone) + + def twoUnverified = hp.getUnverified(2) + assert twoUnverified.size() == 2 + + def oneUnverified = hp.getUnverified(1) + assert oneUnverified.size() == 1 + assert oneUnverified.contains(freeLeafs) || oneUnverified.contains(freePeers) || + oneUnverified.contains(freeBoth) || oneUnverified.contains(freeNone) + } + + @Test + void testFailHost() { + hp.addUnverified(freeBoth) + assert hp.getUnverified(10).size() == 1 + + hp.fail(freeBoth) + hp.age() + assert hp.getUnverified(10).size() == 1 + + hp.fail(freeBoth) + hp.age() + assert hp.getUnverified(10).size() == 1 + + hp.fail(freeBoth) + hp.age() + assert hp.getUnverified(10).isEmpty() + assert hp.getVerified(10, true).isEmpty() + } + + @Test + void verifyResetsFailures() { + hp.addUnverified(freeBoth) + assert hp.getUnverified(10).size() == 1 + + hp.fail(freeBoth) + hp.age() + assert hp.getUnverified(10).size() == 1 + + hp.fail(freeBoth) + hp.age() + assert hp.getUnverified(10).size() == 1 + + hp.verify(freeBoth) + hp.age() + assert hp.getUnverified(10).isEmpty() + assert hp.getVerified(10, true).size() == 1 + } + + @Test + void ageHost() { + hp.addUnverified(freeBoth) + hp.verify(freeBoth) + + hp.age() + assert hp.getVerified(10,true).size() == 1 + assert hp.getUnverified(10).isEmpty() + + Thread.sleep(20) + hp.age() + assert hp.getVerified(10,true).isEmpty() + assert hp.getUnverified(10).size() == 1 } }