69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
"""A deterministic Chord routing exercise, not a network implementation."""
|
|
import argparse
|
|
from bisect import bisect_left
|
|
|
|
SIZE = 32
|
|
NODES = [2, 7, 13, 20, 27]
|
|
|
|
|
|
def successor(nodes, key):
|
|
return nodes[bisect_left(nodes, key) % len(nodes)]
|
|
|
|
|
|
def fingers(nodes, node):
|
|
return [(point, successor(nodes, point))
|
|
for point in ((node + (1 << i)) % SIZE for i in range(5))]
|
|
|
|
|
|
def lookup(nodes, start, key, fast=True):
|
|
current, route = start, [start]
|
|
for _ in range(len(nodes) + 1):
|
|
if key == current or len(nodes) == 1:
|
|
return current, route
|
|
next_node = nodes[(nodes.index(current) + 1) % len(nodes)]
|
|
if (key - current) % SIZE <= (next_node - current) % SIZE:
|
|
return next_node, route
|
|
candidates = [n for _, n in fingers(nodes, current)
|
|
if 0 < (n - current) % SIZE < (key - current) % SIZE]
|
|
current = max(candidates, key=lambda n: (n - current) % SIZE) if fast and candidates else next_node
|
|
route.append(current)
|
|
raise RuntimeError("Routing loop")
|
|
|
|
|
|
def check():
|
|
for nodes in (NODES, sorted(NODES + [24]), [2, 7, 13, 27], [2], [0, 31]):
|
|
for start in nodes:
|
|
for key in range(SIZE):
|
|
for fast in (False, True):
|
|
owner, _ = lookup(nodes, start, key, fast)
|
|
assert owner == successor(nodes, key), (nodes, start, key, fast)
|
|
print("PASS: all keys and start nodes, before/after join/failure, singleton and wraparound")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--check", action="store_true")
|
|
args = parser.parse_args()
|
|
if args.check:
|
|
check()
|
|
return
|
|
print("Ring 0..31; nodes:", NODES)
|
|
for key in (0, 18, 26):
|
|
print(f"key {key:2} -> owner {successor(NODES, key)}")
|
|
print("\nFinger table of node 2 (start -> successor):")
|
|
for point, node in fingers(NODES, 2):
|
|
print(f"{point:2} -> {node:2}")
|
|
for fast in (False, True):
|
|
owner, route = lookup(NODES, 2, 26, fast)
|
|
print(f"{'fingers' if fast else 'neighbors'}: route {route}; result {owner}; "
|
|
f"forwarded requests {len(route) - 1}")
|
|
joined = sorted(NODES + [24])
|
|
changed = [key for key in range(SIZE) if successor(NODES, key) != successor(joined, key)]
|
|
print("\nJoin node 24; transferred keys:", changed)
|
|
print("Fail node 24; after repairing the ring the owner is 27.")
|
|
print("Routing references do not recreate lost values; replicas are a separate concern.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|