From b1bfba6d93ab07b1b7daa56fcebd575d57c087e5 Mon Sep 17 00:00:00 2001 From: Joshua Kissoon Date: Mon, 28 Apr 2014 20:38:47 +0530 Subject: [PATCH] Kademlia Contact Removal - Contact removal now working well, we only do removal when we need to get in touch with the contact for some reason. - Replacement cache is now setup --- .../operation/ContentLookupOperation.java | 4 +- .../operation/NodeLookupOperation.java | 4 +- src/kademlia/routing/Bucket.java | 8 +- src/kademlia/routing/KadBucket.java | 68 ++++++++-- src/kademlia/routing/RoutingTable.java | 37 +++--- .../tests/RoutingTableStateTesting.java | 122 +++++++++++------- 6 files changed, 160 insertions(+), 83 deletions(-) diff --git a/src/kademlia/operation/ContentLookupOperation.java b/src/kademlia/operation/ContentLookupOperation.java index dab7968..52ccc5b 100644 --- a/src/kademlia/operation/ContentLookupOperation.java +++ b/src/kademlia/operation/ContentLookupOperation.java @@ -284,9 +284,9 @@ public class ContentLookupOperation implements Operation, Receiver throw new UnknownMessageException("Unknown comm: " + comm); } - /* Mark this node as failed */ + /* Mark this node as failed and inform the routing table that it's unresponsive */ this.nodes.put(n, FAILED); - this.localNode.getRoutingTable().remove(n); + this.localNode.getRoutingTable().setUnresponsiveContact(n); this.messagesTransiting.remove(comm); this.askNodesorFinish(); diff --git a/src/kademlia/operation/NodeLookupOperation.java b/src/kademlia/operation/NodeLookupOperation.java index 2f905af..7cb399a 100644 --- a/src/kademlia/operation/NodeLookupOperation.java +++ b/src/kademlia/operation/NodeLookupOperation.java @@ -303,9 +303,9 @@ public class NodeLookupOperation implements Operation, Receiver throw new UnknownMessageException("Unknown comm: " + comm); } - /* Mark this node as failed */ + /* Mark this node as failed and inform the routing table that it is unresponsive */ this.nodes.put(n, FAILED); - this.localNode.getRoutingTable().remove(n); + this.localNode.getRoutingTable().setUnresponsiveContact(n); this.messagesTransiting.remove(comm); this.askNodesorFinish(); diff --git a/src/kademlia/routing/Bucket.java b/src/kademlia/routing/Bucket.java index e98bbd2..606763b 100644 --- a/src/kademlia/routing/Bucket.java +++ b/src/kademlia/routing/Bucket.java @@ -48,15 +48,19 @@ public interface Bucket * Remove a contact from this bucket * * @param c The contact to remove + * + * @return Boolean whether the removal was successful. */ - public void removeContact(Contact c); + public boolean removeContact(Contact c); /** * Remove the contact object related to a node from this bucket * * @param n The node of the contact to remove + * + * @return Boolean whether the removal was successful. */ - public void removeNode(Node n); + public boolean removeNode(Node n); /** * Counts the number of contacts in this bucket. diff --git a/src/kademlia/routing/KadBucket.java b/src/kademlia/routing/KadBucket.java index fa7dbce..bba298e 100644 --- a/src/kademlia/routing/KadBucket.java +++ b/src/kademlia/routing/KadBucket.java @@ -2,8 +2,8 @@ package kademlia.routing; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.TreeMap; +import kademlia.core.KadConfiguration; import kademlia.node.Node; /** @@ -19,10 +19,12 @@ public class KadBucket implements Bucket private final int depth; /* Contacts stored in this routing table */ - private final Map contacts; + private final TreeMap contacts; /* A set of last seen contacts that can replace any current contact that is unresponsive */ - private final Map replacementCache; + private final TreeMap replacementCache; + + private KadConfiguration config; { @@ -31,17 +33,18 @@ public class KadBucket implements Bucket } /** - * @param depth How deep in the routing tree is this bucket + * @param depth How deep in the routing tree is this bucket + * @param config */ - public KadBucket(int depth) + public KadBucket(int depth, KadConfiguration config) { this.depth = depth; + this.config = config; } @Override public void insert(Contact c) { - /* @todo Check if the bucket is filled already and handle the situation */ if (this.contacts.containsKey(c)) { /** @@ -54,7 +57,16 @@ public class KadBucket implements Bucket } else { - contacts.put(c, c); + /* If the bucket is filled, we put the contacts in the replacement cache */ + if (contacts.size() >= this.config.k()) + { + /* Bucket is filled, place this contact in the replacement cache */ + this.insertIntoCache(c); + } + else + { + contacts.put(c, c); + } } } @@ -77,15 +89,31 @@ public class KadBucket implements Bucket } @Override - public void removeContact(Contact c) + public boolean removeContact(Contact c) { + /* If the contact does not exist, then we failed to remove it */ + if (!this.contacts.containsKey(c)) + { + return false; + } + this.contacts.remove(c); + + /* If there are replacement contacts in the replacement cache, lets put them into the bucket */ + if (!this.replacementCache.isEmpty()) + { + Contact replacement = this.replacementCache.firstKey(); + this.contacts.put(replacement, replacement); + this.replacementCache.remove(replacement); + } + + return true; } @Override - public void removeNode(Node n) + public boolean removeNode(Node n) { - this.removeContact(new Contact(n)); + return this.removeContact(new Contact(n)); } @Override @@ -106,6 +134,26 @@ public class KadBucket implements Bucket return (this.contacts.isEmpty()) ? new ArrayList<>() : new ArrayList<>(this.contacts.values()); } + /** + * When the bucket is filled, we keep extra contacts in the replacement cache. + */ + private void insertIntoCache(Contact c) + { + /* Just return if this contact is already in our replacement cache */ + if (this.replacementCache.containsKey(c)) + { + return; + } + + /* if our cache is filled, we remove the least recently seen contact */ + if (this.replacementCache.size() > this.config.k()) + { + this.replacementCache.remove(this.replacementCache.lastKey()); + } + + this.replacementCache.put(c, c); + } + @Override public String toString() { diff --git a/src/kademlia/routing/RoutingTable.java b/src/kademlia/routing/RoutingTable.java index 52c04fe..240798b 100644 --- a/src/kademlia/routing/RoutingTable.java +++ b/src/kademlia/routing/RoutingTable.java @@ -40,7 +40,7 @@ public class RoutingTable this.buckets = new Bucket[NodeId.ID_LENGTH]; for (int i = 0; i < NodeId.ID_LENGTH; i++) { - buckets[i] = new KadBucket(i); + buckets[i] = new KadBucket(i, this.config); } } @@ -64,22 +64,6 @@ public class RoutingTable this.buckets[this.getBucketId(n.getNodeId())].insert(n); } - /** - * Remove a node from the routing table. - * - * @param n The node to remove - */ - public final void remove(Node n) - { - int bucketId = this.getBucketId(n.getNodeId()); - - /* If the bucket has the contact, remove it */ - if (this.buckets[bucketId].containsNode(n)) - { - this.buckets[bucketId].removeNode(n); - } - } - /** * Compute the bucket ID in which a given node should be placed; the bucketId is computed based on how far the node is away from the Local Node. * @@ -247,14 +231,27 @@ public class RoutingTable { return; } - - System.out.println("Unresponsive contacts: "); for (Node n : contacts) { - System.out.println(n); + this.setUnresponsiveContact(n); } } + /** + * Method used by operations to notify the routing table of any contacts that have been unresponsive. + * + * @param n + */ + public synchronized void setUnresponsiveContact(Node n) + { + int bucketId = this.getBucketId(n.getNodeId()); + + //System.out.println(this.localNode + " Removing unresponsive node " + n); + + /* Remove the contact from the bucket */ + this.buckets[bucketId].removeNode(n); + } + @Override public synchronized final String toString() { diff --git a/src/kademlia/tests/RoutingTableStateTesting.java b/src/kademlia/tests/RoutingTableStateTesting.java index 8e39d4e..9a38480 100644 --- a/src/kademlia/tests/RoutingTableStateTesting.java +++ b/src/kademlia/tests/RoutingTableStateTesting.java @@ -1,6 +1,7 @@ package kademlia.tests; import java.io.IOException; +import java.util.Scanner; import kademlia.KademliaNode; import kademlia.dht.KadContent; import kademlia.node.NodeId; @@ -14,33 +15,32 @@ import kademlia.node.NodeId; public class RoutingTableStateTesting { - KademliaNode kad0, kad1, kad2, kad3, kad4, kad5, kad6, kad7, kad8, kad9; + KademliaNode[] kads; + + public int numKads = 10; public RoutingTableStateTesting() { try { - /* Setting up 2 Kad networks */ - kad0 = new KademliaNode("user0", new NodeId("HRF456789SD584567460"), 1334); - kad1 = new KademliaNode("user1", new NodeId("ASF456789475DS567461"), 1209); - kad2 = new KademliaNode("user2", new NodeId("AFG45678947584567462"), 4585); - kad3 = new KademliaNode("user3", new NodeId("FSF45J38947584567463"), 8104); - kad4 = new KademliaNode("user4", new NodeId("ASF45678947584567464"), 8335); - kad5 = new KademliaNode("user5", new NodeId("GHF4567894DR84567465"), 13345); - kad6 = new KademliaNode("user6", new NodeId("ASF45678947584567466"), 12049); - kad7 = new KademliaNode("user7", new NodeId("AE345678947584567467"), 14585); - kad8 = new KademliaNode("user8", new NodeId("ASAA5678947584567468"), 18104); - kad9 = new KademliaNode("user9", new NodeId("ASF456789475845674U9"), 18335); + /* Setting up Kad networks */ + kads = new KademliaNode[numKads]; - kad1.bootstrap(kad0.getNode()); - kad2.bootstrap(kad0.getNode()); - kad3.bootstrap(kad0.getNode()); - kad4.bootstrap(kad0.getNode()); - kad5.bootstrap(kad0.getNode()); - kad6.bootstrap(kad0.getNode()); - kad7.bootstrap(kad0.getNode()); - kad8.bootstrap(kad0.getNode()); - kad9.bootstrap(kad0.getNode()); + kads[0] = new KademliaNode("user0", new NodeId("HRF456789SD584567460"), 1334); + kads[1] = new KademliaNode("user1", new NodeId("ASF456789475DS567461"), 1209); + kads[2] = new KademliaNode("user2", new NodeId("AFG45678947584567462"), 4585); + kads[3] = new KademliaNode("user3", new NodeId("FSF45J38947584567463"), 8104); + kads[4] = new KademliaNode("user4", new NodeId("ASF45678947584567464"), 8335); + kads[5] = new KademliaNode("user5", new NodeId("GHF4567894DR84567465"), 13345); + kads[6] = new KademliaNode("user6", new NodeId("ASF45678947584567466"), 12049); + kads[7] = new KademliaNode("user7", new NodeId("AE345678947584567467"), 14585); + kads[8] = new KademliaNode("user8", new NodeId("ASAA5678947584567468"), 18104); + kads[9] = new KademliaNode("user9", new NodeId("ASF456789475845674U9"), 18335); + + for (int i = 1; i < numKads; i++) + { + kads[i].bootstrap(kads[0].getNode()); + } /* Lets shut down a node and then try putting a content on the network. We'll then see how the un-responsive contacts work */ } @@ -55,7 +55,7 @@ public class RoutingTableStateTesting DHTContentImpl c = null; try { - c = new DHTContentImpl(kad2.getOwnerId(), "Some Data"); + c = new DHTContentImpl(owner.getOwnerId(), "Some Data"); owner.put(c); return c; } @@ -79,43 +79,71 @@ public class RoutingTableStateTesting } } + public void printRoutingTable(int kadId) + { + System.out.println(kads[kadId].getRoutingTable()); + } + public void printRoutingTables() { - System.out.println(kad0.getRoutingTable()); - System.out.println(kad1.getRoutingTable()); - System.out.println(kad2.getRoutingTable()); - System.out.println(kad3.getRoutingTable()); - System.out.println(kad4.getRoutingTable()); - System.out.println(kad5.getRoutingTable()); - System.out.println(kad6.getRoutingTable()); - System.out.println(kad7.getRoutingTable()); - System.out.println(kad8.getRoutingTable()); - System.out.println(kad9.getRoutingTable()); + for (int i = 0; i < numKads; i++) + { + this.printRoutingTable(i); + } + } + + public void printStorage(int kadId) + { + System.out.println(kads[kadId].getDHT()); } public void printStorage() { - System.out.println(kad0.getDHT()); - System.out.println(kad1.getDHT()); - System.out.println(kad2.getDHT()); - System.out.println(kad3.getDHT()); - System.out.println(kad4.getDHT()); - System.out.println(kad5.getDHT()); - System.out.println(kad6.getDHT()); - System.out.println(kad7.getDHT()); - System.out.println(kad8.getDHT()); - System.out.println(kad9.getDHT()); + for (int i = 0; i < numKads; i++) + { + this.printStorage(i); + } } public static void main(String[] args) { + RoutingTableStateTesting rtss = new RoutingTableStateTesting(); - rtss.printRoutingTables(); - /* Lets shut down a node to test the node removal operation */ - rtss.shutdownKad(rtss.kad3); + try + { + rtss.printRoutingTables(); - rtss.putContent("Content owned by kad0", rtss.kad0); - rtss.printStorage(); + /* Lets shut down a node to test the node removal operation */ + rtss.shutdownKad(rtss.kads[3]); + + rtss.putContent("Content owned by kad0", rtss.kads[0]); + rtss.printStorage(); + + Thread.sleep(1000); + + /* kad3 should be removed from their routing tables by now. */ + rtss.printRoutingTables(); + } + catch (InterruptedException ex) + { + + } + + Scanner sc = new Scanner(System.in); + while (true) + { + System.out.println("\n\n ************************* Options **************************** \n"); + System.out.println("1 i - Print routing table of node i"); + int val1 = sc.nextInt(); + int val2 = sc.nextInt(); + + switch (val1) + { + case 1: + rtss.printRoutingTable(val2); + break; + } + } } }