github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/routing/kbucket/table.go (about) 1 // package kbucket implements a kademlia 'k-bucket' routing table. 2 package kbucket 3 4 import ( 5 "container/list" 6 "fmt" 7 "sort" 8 "sync" 9 "time" 10 11 peer "github.com/jbenet/go-ipfs/peer" 12 u "github.com/jbenet/go-ipfs/util" 13 ) 14 15 var log = u.Logger("table") 16 17 // RoutingTable defines the routing table. 18 type RoutingTable struct { 19 20 // ID of the local peer 21 local ID 22 23 // Blanket lock, refine later for better performance 24 tabLock sync.RWMutex 25 26 // Maximum acceptable latency for peers in this cluster 27 maxLatency time.Duration 28 29 // kBuckets define all the fingers to other nodes. 30 Buckets []*Bucket 31 bucketsize int 32 } 33 34 // NewRoutingTable creates a new routing table with a given bucketsize, local ID, and latency tolerance. 35 func NewRoutingTable(bucketsize int, localID ID, latency time.Duration) *RoutingTable { 36 rt := new(RoutingTable) 37 rt.Buckets = []*Bucket{newBucket()} 38 rt.bucketsize = bucketsize 39 rt.local = localID 40 rt.maxLatency = latency 41 return rt 42 } 43 44 // Update adds or moves the given peer to the front of its respective bucket 45 // If a peer gets removed from a bucket, it is returned 46 func (rt *RoutingTable) Update(p peer.Peer) peer.Peer { 47 rt.tabLock.Lock() 48 defer rt.tabLock.Unlock() 49 peerID := ConvertPeerID(p.ID()) 50 cpl := commonPrefixLen(peerID, rt.local) 51 52 bucketID := cpl 53 if bucketID >= len(rt.Buckets) { 54 bucketID = len(rt.Buckets) - 1 55 } 56 57 bucket := rt.Buckets[bucketID] 58 e := bucket.find(p.ID()) 59 if e == nil { 60 // New peer, add to bucket 61 if p.GetLatency() > rt.maxLatency { 62 // Connection doesnt meet requirements, skip! 63 return nil 64 } 65 bucket.pushFront(p) 66 67 // Are we past the max bucket size? 68 if bucket.len() > rt.bucketsize { 69 // If this bucket is the rightmost bucket, and its full 70 // we need to split it and create a new bucket 71 if bucketID == len(rt.Buckets)-1 { 72 return rt.nextBucket() 73 } else { 74 // If the bucket cant split kick out least active node 75 return bucket.popBack() 76 } 77 } 78 return nil 79 } 80 // If the peer is already in the table, move it to the front. 81 // This signifies that it it "more active" and the less active nodes 82 // Will as a result tend towards the back of the list 83 bucket.moveToFront(e) 84 return nil 85 } 86 87 func (rt *RoutingTable) nextBucket() peer.Peer { 88 bucket := rt.Buckets[len(rt.Buckets)-1] 89 newBucket := bucket.Split(len(rt.Buckets)-1, rt.local) 90 rt.Buckets = append(rt.Buckets, newBucket) 91 if newBucket.len() > rt.bucketsize { 92 return rt.nextBucket() 93 } 94 95 // If all elements were on left side of split... 96 if bucket.len() > rt.bucketsize { 97 return bucket.popBack() 98 } 99 return nil 100 } 101 102 // A helper struct to sort peers by their distance to the local node 103 type peerDistance struct { 104 p peer.Peer 105 distance ID 106 } 107 108 // peerSorterArr implements sort.Interface to sort peers by xor distance 109 type peerSorterArr []*peerDistance 110 111 func (p peerSorterArr) Len() int { return len(p) } 112 func (p peerSorterArr) Swap(a, b int) { p[a], p[b] = p[b], p[a] } 113 func (p peerSorterArr) Less(a, b int) bool { 114 return p[a].distance.less(p[b].distance) 115 } 116 117 // 118 119 func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr { 120 for e := peerList.Front(); e != nil; e = e.Next() { 121 p := e.Value.(peer.Peer) 122 pID := ConvertPeerID(p.ID()) 123 pd := peerDistance{ 124 p: p, 125 distance: xor(target, pID), 126 } 127 peerArr = append(peerArr, &pd) 128 if e == nil { 129 log.Debug("list element was nil") 130 return peerArr 131 } 132 } 133 return peerArr 134 } 135 136 // Find a specific peer by ID or return nil 137 func (rt *RoutingTable) Find(id peer.ID) peer.Peer { 138 srch := rt.NearestPeers(ConvertPeerID(id), 1) 139 if len(srch) == 0 || !srch[0].ID().Equal(id) { 140 return nil 141 } 142 return srch[0] 143 } 144 145 // NearestPeer returns a single peer that is nearest to the given ID 146 func (rt *RoutingTable) NearestPeer(id ID) peer.Peer { 147 peers := rt.NearestPeers(id, 1) 148 if len(peers) > 0 { 149 return peers[0] 150 } 151 152 log.Errorf("NearestPeer: Returning nil, table size = %d", rt.Size()) 153 return nil 154 } 155 156 // NearestPeers returns a list of the 'count' closest peers to the given ID 157 func (rt *RoutingTable) NearestPeers(id ID, count int) []peer.Peer { 158 rt.tabLock.RLock() 159 defer rt.tabLock.RUnlock() 160 cpl := commonPrefixLen(id, rt.local) 161 162 // Get bucket at cpl index or last bucket 163 var bucket *Bucket 164 if cpl >= len(rt.Buckets) { 165 cpl = len(rt.Buckets) - 1 166 } 167 bucket = rt.Buckets[cpl] 168 169 var peerArr peerSorterArr 170 if bucket.len() == 0 { 171 // In the case of an unusual split, one bucket may be empty. 172 // if this happens, search both surrounding buckets for nearest peer 173 if cpl > 0 { 174 plist := rt.Buckets[cpl-1].list 175 peerArr = copyPeersFromList(id, peerArr, plist) 176 } 177 178 if cpl < len(rt.Buckets)-1 { 179 plist := rt.Buckets[cpl+1].list 180 peerArr = copyPeersFromList(id, peerArr, plist) 181 } 182 } else { 183 peerArr = copyPeersFromList(id, peerArr, bucket.list) 184 } 185 186 // Sort by distance to local peer 187 sort.Sort(peerArr) 188 189 var out []peer.Peer 190 for i := 0; i < count && i < peerArr.Len(); i++ { 191 out = append(out, peerArr[i].p) 192 } 193 194 return out 195 } 196 197 // Size returns the total number of peers in the routing table 198 func (rt *RoutingTable) Size() int { 199 var tot int 200 for _, buck := range rt.Buckets { 201 tot += buck.len() 202 } 203 return tot 204 } 205 206 // ListPeers takes a RoutingTable and returns a list of all peers from all buckets in the table. 207 // NOTE: This is potentially unsafe... use at your own risk 208 func (rt *RoutingTable) ListPeers() []peer.Peer { 209 var peers []peer.Peer 210 for _, buck := range rt.Buckets { 211 for e := buck.getIter(); e != nil; e = e.Next() { 212 peers = append(peers, e.Value.(peer.Peer)) 213 } 214 } 215 return peers 216 } 217 218 // Print prints a descriptive statement about the provided RoutingTable 219 func (rt *RoutingTable) Print() { 220 fmt.Printf("Routing Table, bs = %d, Max latency = %d\n", rt.bucketsize, rt.maxLatency) 221 rt.tabLock.RLock() 222 peers := rt.ListPeers() 223 for i, p := range peers { 224 fmt.Printf("%d) %s %s\n", i, p.ID().Pretty(), p.GetLatency().String()) 225 } 226 }