github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/swarm/network/kademlia/kademlia.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package kademlia 13 14 import ( 15 "fmt" 16 "sort" 17 "strings" 18 "sync" 19 "time" 20 21 "github.com/Sberex/go-sberex/log" 22 "github.com/Sberex/go-sberex/metrics" 23 ) 24 25 //metrics variables 26 //For metrics, we want to count how many times peers are added/removed 27 //at a certain index. Thus we do that with an array of counters with 28 //entry for each index 29 var ( 30 bucketAddIndexCount []metrics.Counter 31 bucketRmIndexCount []metrics.Counter 32 ) 33 34 const ( 35 bucketSize = 4 36 proxBinSize = 2 37 maxProx = 8 38 connRetryExp = 2 39 maxPeers = 100 40 ) 41 42 var ( 43 purgeInterval = 42 * time.Hour 44 initialRetryInterval = 42 * time.Millisecond 45 maxIdleInterval = 42 * 1000 * time.Millisecond 46 // maxIdleInterval = 42 * 10 0 * time.Millisecond 47 ) 48 49 type KadParams struct { 50 // adjustable parameters 51 MaxProx int 52 ProxBinSize int 53 BucketSize int 54 PurgeInterval time.Duration 55 InitialRetryInterval time.Duration 56 MaxIdleInterval time.Duration 57 ConnRetryExp int 58 } 59 60 func NewDefaultKadParams() *KadParams { 61 return &KadParams{ 62 MaxProx: maxProx, 63 ProxBinSize: proxBinSize, 64 BucketSize: bucketSize, 65 PurgeInterval: purgeInterval, 66 InitialRetryInterval: initialRetryInterval, 67 MaxIdleInterval: maxIdleInterval, 68 ConnRetryExp: connRetryExp, 69 } 70 } 71 72 // Kademlia is a table of active nodes 73 type Kademlia struct { 74 addr Address // immutable baseaddress of the table 75 *KadParams // Kademlia configuration parameters 76 proxLimit int // state, the PO of the first row of the most proximate bin 77 proxSize int // state, the number of peers in the most proximate bin 78 count int // number of active peers (w live connection) 79 buckets [][]Node // the actual bins 80 db *KadDb // kaddb, node record database 81 lock sync.RWMutex // mutex to access buckets 82 } 83 84 type Node interface { 85 Addr() Address 86 Url() string 87 LastActive() time.Time 88 Drop() 89 } 90 91 // public constructor 92 // add is the base address of the table 93 // params is KadParams configuration 94 func New(addr Address, params *KadParams) *Kademlia { 95 buckets := make([][]Node, params.MaxProx+1) 96 kad := &Kademlia{ 97 addr: addr, 98 KadParams: params, 99 buckets: buckets, 100 db: newKadDb(addr, params), 101 } 102 kad.initMetricsVariables() 103 return kad 104 } 105 106 // accessor for KAD base address 107 func (self *Kademlia) Addr() Address { 108 return self.addr 109 } 110 111 // accessor for KAD active node count 112 func (self *Kademlia) Count() int { 113 defer self.lock.Unlock() 114 self.lock.Lock() 115 return self.count 116 } 117 118 // accessor for KAD active node count 119 func (self *Kademlia) DBCount() int { 120 return self.db.count() 121 } 122 123 // On is the entry point called when a new nodes is added 124 // unsafe in that node is not checked to be already active node (to be called once) 125 func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) { 126 log.Debug(fmt.Sprintf("%v", self)) 127 defer self.lock.Unlock() 128 self.lock.Lock() 129 130 index := self.proximityBin(node.Addr()) 131 record := self.db.findOrCreate(index, node.Addr(), node.Url()) 132 133 if cb != nil { 134 err = cb(record, node) 135 log.Trace(fmt.Sprintf("cb(%v, %v) ->%v", record, node, err)) 136 if err != nil { 137 return fmt.Errorf("unable to add node %v, callback error: %v", node.Addr(), err) 138 } 139 log.Debug(fmt.Sprintf("add node record %v with node %v", record, node)) 140 } 141 142 // insert in kademlia table of active nodes 143 bucket := self.buckets[index] 144 // if bucket is full insertion replaces the worst node 145 // TODO: give priority to peers with active traffic 146 if len(bucket) < self.BucketSize { // >= allows us to add peers beyond the bucketsize limitation 147 self.buckets[index] = append(bucket, node) 148 bucketAddIndexCount[index].Inc(1) 149 log.Debug(fmt.Sprintf("add node %v to table", node)) 150 self.setProxLimit(index, true) 151 record.node = node 152 self.count++ 153 return nil 154 } 155 156 // always rotate peers 157 idle := self.MaxIdleInterval 158 var pos int 159 var replaced Node 160 for i, p := range bucket { 161 idleInt := time.Since(p.LastActive()) 162 if idleInt > idle { 163 idle = idleInt 164 pos = i 165 replaced = p 166 } 167 } 168 if replaced == nil { 169 log.Debug(fmt.Sprintf("all peers wanted, PO%03d bucket full", index)) 170 return fmt.Errorf("bucket full") 171 } 172 log.Debug(fmt.Sprintf("node %v replaced by %v (idle for %v > %v)", replaced, node, idle, self.MaxIdleInterval)) 173 replaced.Drop() 174 // actually replace in the row. When off(node) is called, the peer is no longer in the row 175 bucket[pos] = node 176 // there is no change in bucket cardinalities so no prox limit adjustment is needed 177 record.node = node 178 self.count++ 179 return nil 180 181 } 182 183 // Off is the called when a node is taken offline (from the protocol main loop exit) 184 func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) { 185 self.lock.Lock() 186 defer self.lock.Unlock() 187 188 index := self.proximityBin(node.Addr()) 189 bucketRmIndexCount[index].Inc(1) 190 bucket := self.buckets[index] 191 for i := 0; i < len(bucket); i++ { 192 if node.Addr() == bucket[i].Addr() { 193 self.buckets[index] = append(bucket[:i], bucket[(i+1):]...) 194 self.setProxLimit(index, false) 195 break 196 } 197 } 198 199 record := self.db.index[node.Addr()] 200 // callback on remove 201 if cb != nil { 202 cb(record, record.node) 203 } 204 record.node = nil 205 self.count-- 206 log.Debug(fmt.Sprintf("remove node %v from table, population now is %v", node, self.count)) 207 208 return 209 } 210 211 // proxLimit is dynamically adjusted so that 212 // 1) there is no empty buckets in bin < proxLimit and 213 // 2) the sum of all items are the minimum possible but higher than ProxBinSize 214 // adjust Prox (proxLimit and proxSize after an insertion/removal of nodes) 215 // caller holds the lock 216 func (self *Kademlia) setProxLimit(r int, on bool) { 217 // if the change is outside the core (PO lower) 218 // and the change does not leave a bucket empty then 219 // no adjustment needed 220 if r < self.proxLimit && len(self.buckets[r]) > 0 { 221 return 222 } 223 // if on=a node was added, then r must be within prox limit so increment cardinality 224 if on { 225 self.proxSize++ 226 curr := len(self.buckets[self.proxLimit]) 227 // if now core is big enough without the furthest bucket, then contract 228 // this can result in more than one bucket change 229 for self.proxSize >= self.ProxBinSize+curr && curr > 0 { 230 self.proxSize -= curr 231 self.proxLimit++ 232 curr = len(self.buckets[self.proxLimit]) 233 234 log.Trace(fmt.Sprintf("proxbin contraction (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r)) 235 } 236 return 237 } 238 // otherwise 239 if r >= self.proxLimit { 240 self.proxSize-- 241 } 242 // expand core by lowering prox limit until hit zero or cover the empty bucket or reached target cardinality 243 for (self.proxSize < self.ProxBinSize || r < self.proxLimit) && 244 self.proxLimit > 0 { 245 // 246 self.proxLimit-- 247 self.proxSize += len(self.buckets[self.proxLimit]) 248 log.Trace(fmt.Sprintf("proxbin expansion (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r)) 249 } 250 } 251 252 /* 253 returns the list of nodes belonging to the same proximity bin 254 as the target. The most proximate bin will be the union of the bins between 255 proxLimit and MaxProx. 256 */ 257 func (self *Kademlia) FindClosest(target Address, max int) []Node { 258 self.lock.Lock() 259 defer self.lock.Unlock() 260 261 r := nodesByDistance{ 262 target: target, 263 } 264 265 po := self.proximityBin(target) 266 index := po 267 step := 1 268 log.Trace(fmt.Sprintf("serving %v nodes at %v (PO%02d)", max, index, po)) 269 270 // if max is set to 0, just want a full bucket, dynamic number 271 min := max 272 // set limit to max 273 limit := max 274 if max == 0 { 275 min = 1 276 limit = maxPeers 277 } 278 279 var n int 280 for index >= 0 { 281 // add entire bucket 282 for _, p := range self.buckets[index] { 283 r.push(p, limit) 284 n++ 285 } 286 // terminate if index reached the bottom or enough peers > min 287 log.Trace(fmt.Sprintf("add %v -> %v (PO%02d, PO%03d)", len(self.buckets[index]), n, index, po)) 288 if n >= min && (step < 0 || max == 0) { 289 break 290 } 291 // reach top most non-empty PO bucket, turn around 292 if index == self.MaxProx { 293 index = po 294 step = -1 295 } 296 index += step 297 } 298 log.Trace(fmt.Sprintf("serve %d (<=%d) nodes for target lookup %v (PO%03d)", n, max, target, po)) 299 return r.nodes 300 } 301 302 func (self *Kademlia) Suggest() (*NodeRecord, bool, int) { 303 defer self.lock.RUnlock() 304 self.lock.RLock() 305 return self.db.findBest(self.BucketSize, func(i int) int { return len(self.buckets[i]) }) 306 } 307 308 // adds node records to kaddb (persisted node record db) 309 func (self *Kademlia) Add(nrs []*NodeRecord) { 310 self.db.add(nrs, self.proximityBin) 311 } 312 313 // nodesByDistance is a list of nodes, ordered by distance to target. 314 type nodesByDistance struct { 315 nodes []Node 316 target Address 317 } 318 319 func sortedByDistanceTo(target Address, slice []Node) bool { 320 var last Address 321 for i, node := range slice { 322 if i > 0 { 323 if target.ProxCmp(node.Addr(), last) < 0 { 324 return false 325 } 326 } 327 last = node.Addr() 328 } 329 return true 330 } 331 332 // push(node, max) adds the given node to the list, keeping the total size 333 // below max elements. 334 func (h *nodesByDistance) push(node Node, max int) { 335 // returns the firt index ix such that func(i) returns true 336 ix := sort.Search(len(h.nodes), func(i int) bool { 337 return h.target.ProxCmp(h.nodes[i].Addr(), node.Addr()) >= 0 338 }) 339 340 if len(h.nodes) < max { 341 h.nodes = append(h.nodes, node) 342 } 343 if ix < len(h.nodes) { 344 copy(h.nodes[ix+1:], h.nodes[ix:]) 345 h.nodes[ix] = node 346 } 347 } 348 349 /* 350 Taking the proximity order relative to a fix point x classifies the points in 351 the space (n byte long byte sequences) into bins. Items in each are at 352 most half as distant from x as items in the previous bin. Given a sample of 353 uniformly distributed items (a hash function over arbitrary sequence) the 354 proximity scale maps onto series of subsets with cardinalities on a negative 355 exponential scale. 356 357 It also has the property that any two item belonging to the same bin are at 358 most half as distant from each other as they are from x. 359 360 If we think of random sample of items in the bins as connections in a network of interconnected nodes than relative proximity can serve as the basis for local 361 decisions for graph traversal where the task is to find a route between two 362 points. Since in every hop, the finite distance halves, there is 363 a guaranteed constant maximum limit on the number of hops needed to reach one 364 node from the other. 365 */ 366 367 func (self *Kademlia) proximityBin(other Address) (ret int) { 368 ret = proximity(self.addr, other) 369 if ret > self.MaxProx { 370 ret = self.MaxProx 371 } 372 return 373 } 374 375 // provides keyrange for chunk db iteration 376 func (self *Kademlia) KeyRange(other Address) (start, stop Address) { 377 defer self.lock.RUnlock() 378 self.lock.RLock() 379 return KeyRange(self.addr, other, self.proxLimit) 380 } 381 382 // save persists kaddb on disk (written to file on path in json format. 383 func (self *Kademlia) Save(path string, cb func(*NodeRecord, Node)) error { 384 return self.db.save(path, cb) 385 } 386 387 // Load(path) loads the node record database (kaddb) from file on path. 388 func (self *Kademlia) Load(path string, cb func(*NodeRecord, Node) error) (err error) { 389 return self.db.load(path, cb) 390 } 391 392 // kademlia table + kaddb table displayed with ascii 393 func (self *Kademlia) String() string { 394 defer self.lock.RUnlock() 395 self.lock.RLock() 396 defer self.db.lock.RUnlock() 397 self.db.lock.RLock() 398 399 var rows []string 400 rows = append(rows, "=========================================================================") 401 rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self.addr.String()[:6])) 402 rows = append(rows, fmt.Sprintf("population: %d (%d), proxLimit: %d, proxSize: %d", self.count, len(self.db.index), self.proxLimit, self.proxSize)) 403 rows = append(rows, fmt.Sprintf("MaxProx: %d, ProxBinSize: %d, BucketSize: %d", self.MaxProx, self.ProxBinSize, self.BucketSize)) 404 405 for i, bucket := range self.buckets { 406 407 if i == self.proxLimit { 408 rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i)) 409 } 410 row := []string{fmt.Sprintf("%03d", i), fmt.Sprintf("%2d", len(bucket))} 411 var k int 412 c := self.db.cursors[i] 413 for ; k < len(bucket); k++ { 414 p := bucket[(c+k)%len(bucket)] 415 row = append(row, p.Addr().String()[:6]) 416 if k == 4 { 417 break 418 } 419 } 420 for ; k < 4; k++ { 421 row = append(row, " ") 422 } 423 row = append(row, fmt.Sprintf("| %2d %2d", len(self.db.Nodes[i]), self.db.cursors[i])) 424 425 for j, p := range self.db.Nodes[i] { 426 row = append(row, p.Addr.String()[:6]) 427 if j == 3 { 428 break 429 } 430 } 431 rows = append(rows, strings.Join(row, " ")) 432 if i == self.MaxProx { 433 } 434 } 435 rows = append(rows, "=========================================================================") 436 return strings.Join(rows, "\n") 437 } 438 439 //We have to build up the array of counters for each index 440 func (self *Kademlia) initMetricsVariables() { 441 //create the arrays 442 bucketAddIndexCount = make([]metrics.Counter, self.MaxProx+1) 443 bucketRmIndexCount = make([]metrics.Counter, self.MaxProx+1) 444 //at each index create a metrics counter 445 for i := 0; i < (self.KadParams.MaxProx + 1); i++ { 446 bucketAddIndexCount[i] = metrics.NewRegisteredCounter(fmt.Sprintf("network.kademlia.bucket.add.%d.index", i), nil) 447 bucketRmIndexCount[i] = metrics.NewRegisteredCounter(fmt.Sprintf("network.kademlia.bucket.rm.%d.index", i), nil) 448 } 449 }