Created
October 25, 2009 18:58
-
-
Save tav/218191 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Distributed Hash Tables (DHT).""" | |
__all__ = ['chord', 'dictionary', 'kademlia', 'koorde'] | |
__metaclass__ = type | |
class DHT: | |
pass | |
class Dictionary(DHT): | |
""" | |
A simple Dictionary-based Distributed Hash Table (DHT). | |
Way back in early 2001, before the Chord papers were published and DHT | |
became a trendy term, oierw and myself had worked out a similar scheme. We | |
didn't have a funky name for it and simply referred to it as an underlying | |
aspect of the Mesh Routing Protocol. | |
04:07:19 <tav> i presume everyone here is familiar with a dictionary? ;p | |
04:07:26 <sbp> a what? | |
04:07:36 <deltab> sbp: a less detailed ontology | |
04:07:37 * sbp looks up that word in his dictionary | |
04:07:52 <deltab> er, less rigorous | |
The term ``Dictionary`` DHT is used because of the dictionary metaphor that | |
was often employed when explaining the term to others. | |
>>> d = Dictionary() | |
""" | |
pass | |
# the following is from earlier plexdev: | |
# ------------------------------------------------------------------------------ | |
# chord circle math | |
# ------------------------------------------------------------------------------ | |
def between(n, a, b): | |
"""Check if n in interval(a, b) on a circle.""" | |
if a == b: return n != a # n is the only node not in (n, n) | |
elif a < b: return n > a and n < b | |
else: return n > a or n < b | |
def betweenL(n, a, b): | |
"""Check if n in interval[a, b) on a circle.""" | |
if a == b and n == a: return 1 | |
elif a < b: return n >= a and n < b | |
else: return n >= a or n < b | |
def betweenR(n, a, b): | |
"""Check if n in interval(a, b] on a circle.""" | |
if a == b and n == a: return 1 | |
elif a < b: return n > a and n <= b | |
else: return n > a or n <= b | |
# ------------------------------------------------------------------------------ | |
# some konstants | |
# ------------------------------------------------------------------------------ | |
NBIT = 5 # num of bits in identifiers | |
nodeList = [] # list of all nodes | |
class Node: | |
""" | |
An implementation of the Chord_ algorithm in Python. | |
.. _Chord: http://pdos.lcs.mit.edu/chord/ | |
This class creates nodes in a network. | |
# Create a bootstrap node: | |
>>> bootstrapNode = Node(1) | |
>>> bootstrapNode.join(None) | |
>>> import random | |
>>> for i in xrange(5): # Create 5 randomly placed nodes | |
... j = random.randrange(1, 2**NBIT) | |
... x = Node(j) | |
... x.join(bootstrapNode) | |
>>> for i in xrange(2): # fix their finger tables | |
... for node in nodeList: | |
... node.stabilize() | |
... node.fixFingers() | |
>>> for node in nodeList: | |
... print node.f | |
... #node.f.test() | |
<Finger object> | |
<Finger object> | |
<Finger object> | |
<Finger object> | |
<Finger object> | |
<Finger object> | |
>>> nodeList.sort() | |
>>> firstNode = nodeList[0] | |
>>> firstNode.id | |
1 | |
>>> firstNode.fixFingers() | |
Sources: | |
Chord TR <http://pdos.lcs.mit.edu/chord/chord-tn.ps> | |
Chord SIGCOMM Paper <http://pdos.lcs.mit.edu/papers/chord:sigcomm01/> | |
Chord Source Code <http://www.fs.net/cvs/sfsnet/chord/?cvsroot=CFS-CVS> | |
""" | |
def __init__(self, id): | |
BadID = "ValueError: ID is too big, must be less than " + str(2L**NBIT) | |
if id > 2**NBIT: raise BadID | |
self.id = id # Needs to be an integer of NBIT bits | |
self.f = Finger(self) # Keeps track of other nodes | |
global nodeList | |
nodeList.append(self) # Add us to the big nodeList | |
def __repr__(self): | |
"""Return a pretty text representation of a node.""" | |
return "<Node " + `self.id` + ">" | |
def join(self, n2): | |
"""Intialize finger tables of local node. | |
n2 is a node already on the network or None if we're the first.""" | |
if n2: | |
self.f[1] = n2.getSucc(self.id) # Get our successor | |
self.f.pred=self.getPred(self.id) # Get our predecessor | |
for i in xrange(1, NBIT): | |
if betweenL(self.id, # If the last finger | |
self.f.start(i+1), self.f[i].id): # is still good | |
self.f[i+1] = self.f[i] # use it again. | |
else: | |
self.f[i+1] = n2.getSucc( # Get a new one. | |
self.f.start(i+1)) | |
n2.notify(self) # Hi to our 1st friend | |
self.f[1].notify(self) # and say hello. | |
self.f.pred.notify(self) # and say hello. | |
else: # If we're all alone | |
self.f[1] = self # then leave our | |
self.f.pred = self # tables empty. | |
def getSucc(self, id): | |
"""Find the successor of id on the circle. | |
This is the key function of the algorithm and its main interface.""" | |
n2 = self.getPred(id) # Get its predecessor. | |
if n2.f[1]: # If it has a successor | |
return n2.f[1] # use that. | |
else: return n2 # Else, use the pred. | |
def getPred(self, id): | |
"""Find the predecessor of id on the circle.""" | |
if self.f[1] is None: # If we have no succ | |
return self # use ourselves. | |
n2 = self | |
while not betweenR(id, n2.id, n2.f[1].id): # Iteratively find | |
n2 = n2.closestPrecedingFinger(id) # the closest finger | |
return n2 # and use it. | |
def closestPrecedingFinger(self, id): | |
"""Find the closest finger that precedes id. | |
This is the key to the algorithm.""" | |
for i in xrange(NBIT, 0, -1): | |
if not self.f[i]: continue | |
if between(self.f[i].id, self.id, id): | |
return self.f[i] | |
return self | |
def notify(self, n2): | |
"""n2 introduces itself. Add them to finger table.""" | |
self.f.add(n2) | |
def stabilize(self): | |
"""Verify our immediate successor and tell them about us. | |
Call periodically.""" | |
x = self.f[1].f.pred # Find our pred's succ | |
if x is not self: # If it's not us | |
self.f.add(x) # make them a finger. | |
def fixFingers(self): | |
"""Refresh the finger table entries. Call periodically. | |
@@ The current implementation fixes all nodes at once. | |
@@ This is really slow, replace with random or generators.""" | |
for i in xrange(1, len(self.f)): # For every finger | |
self.f[i] = self.getSucc(self.f.start(i)) # find the best node. | |
class Finger(list): | |
"""A node's finger table.""" | |
def __init__(self, parent): | |
list.__init__(self) | |
self.parent = parent # parent node | |
self.id = parent.id # chord ID | |
self.pred = None # predecessor | |
# Fill up the list: | |
self.append('ignore first value') | |
for i in xrange(1, NBIT+1): | |
# @@ This wastes memory. Need to override setattr, getattr. | |
self.append(None) | |
def __repr__(n): | |
"""Make a nice printout of the finger table. Good for debugging.""" | |
return '<Finger object>'# @@ doesn't deal with unfilled predicates and such | |
from cStringIO import StringIO | |
out = StringIO() | |
print >> out, "------------------------" | |
print >> out, " P:", n.pred.id | |
print >> out, " M:", n.id | |
i = 0 | |
for e in n: | |
if type(e) is type(''): continue | |
i += 1 | |
if e is None: | |
print >> out, " "+`i`+": None" | |
continue | |
print >> out, " "+`i`+":", e.id, '|', n.parent.getSucc(n.start(i)).id, '|', n.start(i) | |
print >> out, "------------------------" | |
return out.getvalue() | |
def test(self): | |
"""Makes sure finger tables are correct for a saturated network. | |
@@ Make it work for random networks too.""" | |
i = 0 | |
for e in n.f: | |
if type(e) is type(''): continue | |
i += 1 | |
assert e.id == n.f.start(i) | |
def start(self, k): | |
"""Find the starting point of a finger table entry.""" | |
assert 1 <= k <= NBIT # Check assumptions | |
r = (self.id + 2**(k-1)) % 2**NBIT # Get the number | |
if r == 0: return 2**NBIT # Fix funniness with % | |
else: return r # Return the value | |
def add(self, n): | |
"""Add this new node to our tables. | |
Eventually this should also put it in the node cache.""" | |
results = [] # Stores what we did | |
if (self.pred is self or self.pred is None or # If our pred is empty | |
between(n.id, self.pred.id, self.id)): # or this is better | |
self.pred = n # use it | |
#n.notify(self.parent) # and say hello | |
results.append('pred') # Keep track. | |
if self[1] is None or between(n.id, self.id, # Repeat for successor | |
self[1].id): | |
self[1] = n | |
#n.notify(self.parent) | |
results.append(1) | |
for i in xrange(2, len(self)): # Repeat for the rest | |
if self[i] is None or betweenL(n.id, self.start(i), self[i].id): | |
self[i] = n | |
results.append(i) | |
return results # Return what we did |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment