func (a *AddrBook) expireNew(bucketIdx int) {
	for addrStr, ka := range a.addrNew[bucketIdx] {
		if ka.isBad() {
			a.slogger.Infof("expiring bad address %v", addrStr)
			a.removeFromBucket(ka, bucketTypeNew, bucketIdx)
			return
		}
	}
	oldest := a.pickOldest(bucketTypeNew, bucketIdx)
	a.removeFromBucket(oldest, bucketTypeNew, bucketIdx)
}
Make space in the new buckets by expiring the really bad entries.
If no bad entries are available we remove the oldest.
package p2p
import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"
	"go.uber.org/zap"
	. "github.com/annchain/annchain/module/lib/go-common"
	"github.com/annchain/annchain/module/lib/go-crypto"
)
const (
	needAddressThreshold = 1000
	dumpAddressInterval = time.Minute * 2
	oldBucketSize = 64
	oldBucketCount = 64
	newBucketSize = 64
	newBucketCount = 256
	oldBucketsPerGroup = 4
	newBucketsPerGroup = 32
	maxNewBucketsPerAddress = 4
	numMissingDays = 30
	numRetries = 3
	maxFailures = 10
	minBadDays = 7
	getSelectionPercent = 23
	minGetSelection = 32
	maxGetSelection = 250
	serializationVersion = 1
)
type AddrBook struct {
	BaseService
	mtx               sync.Mutex
	filePath          string
	routabilityStrict bool
	rand              *rand.Rand
	key               string
	ourAddrs          map[string]*NetAddress
	addrLookup        map[string]*knownAddress  
	addrNew           []map[string]*knownAddress
	addrOld           []map[string]*knownAddress
	wg                sync.WaitGroup
	nOld              int
	nNew              int
	logger  *zap.Logger
	slogger *zap.SugaredLogger
}
const (
	bucketTypeNew = 0x01
	bucketTypeOld = 0x02
)
func NewAddrBook(logger *zap.Logger, filePath string, routabilityStrict bool) *AddrBook {
	am := &AddrBook{
		rand:              rand.New(rand.NewSource(time.Now().UnixNano())),
		ourAddrs:          make(map[string]*NetAddress),
		addrLookup:        make(map[string]*knownAddress),
		filePath:          filePath,
		routabilityStrict: routabilityStrict,
		logger:            logger,
		slogger:           logger.Sugar(),
	}
	am.init()
	am.BaseService = *NewBaseService(logger, "AddrBook", am)
	return am
}
func (a *AddrBook) init() {
	a.key = crypto.CRandHex(24)  
	a.addrNew = make([]map[string]*knownAddress, newBucketCount)
	for i := range a.addrNew {
		a.addrNew[i] = make(map[string]*knownAddress)
	}
	a.addrOld = make([]map[string]*knownAddress, oldBucketCount)
	for i := range a.addrOld {
		a.addrOld[i] = make(map[string]*knownAddress)
	}
}
func (a *AddrBook) OnStart() error {
	a.BaseService.OnStart()
	a.loadFromFile(a.filePath)
	a.wg.Add(1)
	go a.saveRoutine()
	return nil
}
func (a *AddrBook) OnStop() {
	a.BaseService.OnStop()
	a.wg.Wait()
}
func (a *AddrBook) AddOurAddress(addr *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	a.slogger.Debugw("Add our address to book", "addr", addr)
	a.ourAddrs[addr.String()] = addr
}
func (a *AddrBook) OurAddresses() []*NetAddress {
	addrs := []*NetAddress{}
	for _, addr := range a.ourAddrs {
		addrs = append(addrs, addr)
	}
	return addrs
}
func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	a.slogger.Debugw("Add address to book", "addr", addr, "src", src)
	a.addAddress(addr, src)
}
func (a *AddrBook) NeedMoreAddrs() bool {
	return a.Size() < needAddressThreshold
}
func (a *AddrBook) Size() int {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	return a.size()
}
func (a *AddrBook) size() int {
	return a.nNew + a.nOld
}
func (a *AddrBook) PickAddress(newBias int) *NetAddress {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	if a.size() == 0 {
		return nil
	}
	if newBias > 100 {
		newBias = 100
	}
	if newBias < 0 {
		newBias = 0
	}
	oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(newBias))
	newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias)
	if (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation {
		var bucket map[string]*knownAddress = nil
		for len(bucket) == 0 {
			bucket = a.addrOld[a.rand.Intn(len(a.addrOld))]
		}
		randIndex := a.rand.Intn(len(bucket))
		for _, ka := range bucket {
			if randIndex == 0 {
				return ka.Addr
			}
			randIndex--
		}
		PanicSanity("Should not happen")
	} else {
		var bucket map[string]*knownAddress = nil
		for len(bucket) == 0 {
			bucket = a.addrNew[a.rand.Intn(len(a.addrNew))]
		}
		randIndex := a.rand.Intn(len(bucket))
		for _, ka := range bucket {
			if randIndex == 0 {
				return ka.Addr
			}
			randIndex--
		}
		PanicSanity("Should not happen")
	}
	return nil
}
func (a *AddrBook) MarkGood(addr *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	ka := a.addrLookup[addr.String()]
	if ka == nil {
		return
	}
	ka.markGood()
	if ka.isNew() {
		a.moveToOld(ka)
	}
}
func (a *AddrBook) MarkAttempt(addr *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	ka := a.addrLookup[addr.String()]
	if ka == nil {
		return
	}
	ka.markAttempt()
}
func (a *AddrBook) MarkBad(addr *NetAddress) {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	ka := a.addrLookup[addr.String()]
	if ka == nil {
		return
	}
	a.removeFromAllBuckets(ka)
}
func (a *AddrBook) GetSelection() []*NetAddress {
	a.mtx.Lock()
	defer a.mtx.Unlock()
	if a.size() == 0 {
		return nil
	}
	allAddr := make([]*NetAddress, a.size())
	i := 0
	for _, v := range a.addrLookup {
		allAddr[i] = v.Addr
		i++
	}
	numAddresses := MaxInt(
		MinInt(minGetSelection, len(allAddr)),
		len(allAddr)*getSelectionPercent/100)
	numAddresses = MinInt(maxGetSelection, numAddresses)
	for i := 0; i < numAddresses; i++ {
		j := rand.Intn(len(allAddr)-i) + i
		allAddr[i], allAddr[j] = allAddr[j], allAddr[i]
	}
	return allAddr[:numAddresses]
}
type addrBookJSON struct {
	Key   string
	Addrs []*knownAddress
}
func (a *AddrBook) saveToFile(filePath string) {
	addrs := []*knownAddress{}
	for _, ka := range a.addrLookup {
		addrs = append(addrs, ka)
	}
	aJSON := &addrBookJSON{
		Key:   a.key,
		Addrs: addrs,
	}
	jsonBytes, err := json.MarshalIndent(aJSON, "", "\t")
	if err != nil {
		a.logger.Error("Failed to save AddrBook to file", zap.String("err", err.Error()))
		return
	}
	err = WriteFileAtomic(filePath, jsonBytes, 0644)
	if err != nil {
		a.logger.Error("Failed to save AddrBook to file", zap.String("file", filePath), zap.String("error", err.Error()))
	}
}
func (a *AddrBook) loadFromFile(filePath string) bool {
	_, err := os.Stat(filePath)
	if os.IsNotExist(err) {
		return false
	}
	r, err := os.Open(filePath)
	if err != nil {
		PanicCrisis(Fmt("Error opening file %s: %v", filePath, err))
	}
	defer r.Close()
	aJSON := &addrBookJSON{}
	dec := json.NewDecoder(r)
	err = dec.Decode(aJSON)
	if err != nil {
		PanicCrisis(Fmt("Error reading file %s: %v", filePath, err))
	}
	a.key = aJSON.Key
	for _, ka := range aJSON.Addrs {
		for _, bucketIndex := range ka.Buckets {
			bucket := a.getBucket(ka.BucketType, bucketIndex)
			bucket[ka.Addr.String()] = ka
		}
		a.addrLookup[ka.Addr.String()] = ka
		if ka.BucketType == bucketTypeNew {
			a.nNew++
		} else {
			a.nOld++
		}
	}
	return true
}
func (a *AddrBook) Save() {
	a.logger.Info("Saving AddrBook to file", zap.Int("size", a.Size()))
	a.saveToFile(a.filePath)
}
func (a *AddrBook) saveRoutine() {
	dumpAddressTicker := time.NewTicker(dumpAddressInterval)
out:
	for {
		select {
		case <-dumpAddressTicker.C:
			a.logger.Debug("Saving AddrBook to file", zap.Int("size", a.Size()))
			a.saveToFile(a.filePath)
		case <-a.Quit:
			break out
		}
	}
	dumpAddressTicker.Stop()
	a.saveToFile(a.filePath)
	a.wg.Done()
	a.logger.Debug("Address handler done")
}
func (a *AddrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAddress {
	switch bucketType {
	case bucketTypeNew:
		return a.addrNew[bucketIdx]
	case bucketTypeOld:
		return a.addrOld[bucketIdx]
	default:
		PanicSanity("Should not happen")
		return nil
	}
}
func (a *AddrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool {
	if ka.isOld() {
		a.slogger.Warnf("Cannot add address already in old bucket to a new bucket: %v", ka)
		return false
	}
	addrStr := ka.Addr.String()
	bucket := a.getBucket(bucketTypeNew, bucketIdx)
	if _, ok := bucket[addrStr]; ok {
		return true
	}
	if len(bucket) > newBucketSize {
		a.logger.Info("new bucket is full, expiring old ")
		a.expireNew(bucketIdx)
	}
	bucket[addrStr] = ka
	if ka.addBucketRef(bucketIdx) == 1 {
		a.nNew++
	}
	a.addrLookup[addrStr] = ka
	return true
}
func (a *AddrBook) addToOldBucket(ka *knownAddress, bucketIdx int) bool {
	if ka.isNew() {
		a.slogger.Warnf("Cannot add new address to old bucket: %v", ka)
		return false
	}
	if len(ka.Buckets) != 0 {
		a.slogger.Warnf("Cannot add already old address to another old bucket: %v", ka)
		return false
	}
	addrStr := ka.Addr.String()
	bucket := a.getBucket(bucketTypeNew, bucketIdx)
	if _, ok := bucket[addrStr]; ok {
		return true
	}
	if len(bucket) > oldBucketSize {
		return false
	}
	bucket[addrStr] = ka
	if ka.addBucketRef(bucketIdx) == 1 {
		a.nOld++
	}
	a.addrLookup[addrStr] = ka
	return true
}
func (a *AddrBook) removeFromBucket(ka *knownAddress, bucketType byte, bucketIdx int) {
	if ka.BucketType != bucketType {
		a.slogger.Warnf("Bucket type mismatch: %v", ka)
		return
	}
	bucket := a.getBucket(bucketType, bucketIdx)
	delete(bucket, ka.Addr.String())
	if ka.removeBucketRef(bucketIdx) == 0 {
		if bucketType == bucketTypeNew {
			a.nNew--
		} else {
			a.nOld--
		}
		delete(a.addrLookup, ka.Addr.String())
	}
}
func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) {
	for _, bucketIdx := range ka.Buckets {
		bucket := a.getBucket(ka.BucketType, bucketIdx)
		delete(bucket, ka.Addr.String())
	}
	ka.Buckets = nil
	if ka.BucketType == bucketTypeNew {
		a.nNew--
	} else {
		a.nOld--
	}
	delete(a.addrLookup, ka.Addr.String())
}
func (a *AddrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress {
	bucket := a.getBucket(bucketType, bucketIdx)
	var oldest *knownAddress
	for _, ka := range bucket {
		if oldest == nil || ka.LastAttempt.Before(oldest.LastAttempt) {
			oldest = ka
		}
	}
	return oldest
}
func (a *AddrBook) String() string {
	addrs := ""
	for k, _ := range a.addrLookup {
		if len(addrs) == 0 {
			addrs = k
			continue
		}
		addrs = fmt.Sprintf("%v,%v", addrs, k)
	}
	return addrs
}
func (a *AddrBook) addAddress(addr, src *NetAddress) {
	if a.routabilityStrict && !addr.Routable() {
		a.slogger.Warnf("Cannot add non-routable address %v", addr)
		return
	}
	if _, ok := a.ourAddrs[addr.String()]; ok {
		return
	}
	ka := a.addrLookup[addr.String()]
	if ka != nil {
		if ka.isOld() {
			return
		}
		if len(ka.Buckets) == maxNewBucketsPerAddress {
			return
		}
		factor := int32(2 * len(ka.Buckets))
		if a.rand.Int31n(factor) != 0 {
			return
		}
	} else {
		ka = newKnownAddress(a.slogger, addr, src)
	}
	bucket := a.calcNewBucket(addr, src)
	a.addToNewBucket(ka, bucket)
	a.slogger.Debugw("Added new address", "address", addr, "total", a.size())
}