les: minCapacity calculation based on cpu and bandwidth limits

This commit is contained in:
Zsolt Felfoldi 2019-04-06 16:37:59 +02:00
parent 9bf2d758f3
commit 0f3222aaf0
5 changed files with 43 additions and 24 deletions

View file

@ -206,12 +206,12 @@ var (
LightBandwidthInFlag = cli.IntFlag{ LightBandwidthInFlag = cli.IntFlag{
Name: "lightbwin", Name: "lightbwin",
Usage: "Incoming bandwidth limit for light server (1000 bytes/sec, 0 = unlimited)", Usage: "Incoming bandwidth limit for light server (1000 bytes/sec, 0 = unlimited)",
Value: 1000, Value: 0,
} }
LightBandwidthOutFlag = cli.IntFlag{ LightBandwidthOutFlag = cli.IntFlag{
Name: "lightbwout", Name: "lightbwout",
Usage: "Outgoing bandwidth limit for light server (1000 bytes/sec, 0 = unlimited)", Usage: "Outgoing bandwidth limit for light server (1000 bytes/sec, 0 = unlimited)",
Value: 5000, Value: 0,
} }
LightPeersFlag = cli.IntFlag{ LightPeersFlag = cli.IntFlag{
Name: "lightpeers", Name: "lightpeers",

View file

@ -101,7 +101,7 @@ func (s tcSubs) send(tc uint64, underrun bool) {
// MinimumCapacity queries minimum assignable capacity for a single client // MinimumCapacity queries minimum assignable capacity for a single client
func (api *PrivateLightServerAPI) MinimumCapacity() hexutil.Uint64 { func (api *PrivateLightServerAPI) MinimumCapacity() hexutil.Uint64 {
return hexutil.Uint64(minCapacity) return hexutil.Uint64(api.server.minCapacity)
} }
// FreeClientCapacity queries the capacity provided for free clients // FreeClientCapacity queries the capacity provided for free clients
@ -117,7 +117,7 @@ func (api *PrivateLightServerAPI) FreeClientCapacity() hexutil.Uint64 {
// Note: assigned capacity can be changed while the client is connected with // Note: assigned capacity can be changed while the client is connected with
// immediate effect. // immediate effect.
func (api *PrivateLightServerAPI) SetClientCapacity(id enode.ID, cap uint64) error { func (api *PrivateLightServerAPI) SetClientCapacity(id enode.ID, cap uint64) error {
if cap != 0 && cap < minCapacity { if cap != 0 && cap < api.server.minCapacity {
return ErrMinCap return ErrMinCap
} }
return api.server.priorityClientPool.setClientCapacity(id, cap) return api.server.priorityClientPool.setClientCapacity(id, cap)

View file

@ -54,7 +54,7 @@ var (
GetCodeMsg: {0, 80}, GetCodeMsg: {0, 80},
GetProofsV2Msg: {0, 80}, GetProofsV2Msg: {0, 80},
GetHelperTrieProofsMsg: {0, 20}, GetHelperTrieProofsMsg: {0, 20},
SendTxV2Msg: {0, 66000}, SendTxV2Msg: {0, 16500},
GetTxStatusMsg: {0, 50}, GetTxStatusMsg: {0, 50},
} }
// maximum outgoing message size estimates // maximum outgoing message size estimates
@ -68,8 +68,18 @@ var (
SendTxV2Msg: {0, 100}, SendTxV2Msg: {0, 100},
GetTxStatusMsg: {0, 100}, GetTxStatusMsg: {0, 100},
} }
minBufLimit = uint64(50000000 * maxCostFactor) // minimum buffer limit allowed for a client // request amounts that have to fit into the minimum buffer size minBufferMultiplier times
minCapacity = (minBufLimit-1)/bufLimitRatio + 1 // minimum capacity allowed for a client minBufferReqAmount = map[uint64]uint64{
GetBlockHeadersMsg: 192,
GetBlockBodiesMsg: 1,
GetReceiptsMsg: 1,
GetCodeMsg: 1,
GetProofsV2Msg: 1,
GetHelperTrieProofsMsg: 16,
SendTxV2Msg: 8,
GetTxStatusMsg: 64,
}
minBufferMultiplier = 3
) )
const ( const (
@ -107,7 +117,7 @@ type costTracker struct {
} }
// newCostTracker creates a cost tracker and loads the cost factor statistics from the database // newCostTracker creates a cost tracker and loads the cost factor statistics from the database
func newCostTracker(db ethdb.Database, config *eth.Config, logger *csvlogger.Logger) *costTracker { func newCostTracker(db ethdb.Database, config *eth.Config, logger *csvlogger.Logger) (*costTracker, uint64) {
utilTarget := float64(config.LightServ) * flowcontrol.FixedPointMultiplier / 100 utilTarget := float64(config.LightServ) * flowcontrol.FixedPointMultiplier / 100
ct := &costTracker{ ct := &costTracker{
db: db, db: db,
@ -131,7 +141,18 @@ func newCostTracker(db ethdb.Database, config *eth.Config, logger *csvlogger.Log
} }
} }
ct.gfLoop() ct.gfLoop()
return ct costList := ct.makeCostList(ct.globalFactor() * 1.25)
var minBufLimit uint64
for _, c := range costList {
amount := minBufferReqAmount[c.MsgCode]
cost := c.BaseCost + amount*c.ReqCost
if cost > minBufLimit {
minBufLimit = cost
}
}
minBufLimit *= uint64(minBufferMultiplier)
minCapacity := (minBufLimit-1)/bufLimitRatio + 1
return ct, minCapacity
} }
// stop stops the cost tracker and saves the cost factor statistics to the database // stop stops the cost tracker and saves the cost factor statistics to the database
@ -146,16 +167,14 @@ func (ct *costTracker) stop() {
// makeCostList returns upper cost estimates based on the hardcoded cost estimate // makeCostList returns upper cost estimates based on the hardcoded cost estimate
// tables and the optionally specified incoming/outgoing bandwidth limits // tables and the optionally specified incoming/outgoing bandwidth limits
func (ct *costTracker) makeCostList() RequestCostList { func (ct *costTracker) makeCostList(globalFactor float64) RequestCostList {
maxCost := func(avgTime, inSize, outSize uint64) uint64 { maxCost := func(avgTime, inSize, outSize uint64) uint64 {
globalFactor := ct.globalFactor()
cost := avgTime * maxCostFactor cost := avgTime * maxCostFactor
inSizeCost := uint64(float64(inSize) * ct.inSizeFactor * globalFactor * maxCostFactor) inSizeCost := uint64(float64(inSize) * ct.inSizeFactor * globalFactor)
if inSizeCost > cost { if inSizeCost > cost {
cost = inSizeCost cost = inSizeCost
} }
outSizeCost := uint64(float64(outSize) * ct.outSizeFactor * globalFactor * maxCostFactor) outSizeCost := uint64(float64(outSize) * ct.outSizeFactor * globalFactor)
if outSizeCost > cost { if outSizeCost > cost {
cost = outSizeCost cost = outSizeCost
} }

View file

@ -58,7 +58,7 @@ const (
// if the total encoded size of a sent transaction batch is over txSizeCostLimit // if the total encoded size of a sent transaction batch is over txSizeCostLimit
// per transaction then the request cost is calculated as proportional to the // per transaction then the request cost is calculated as proportional to the
// encoded size instead of the transaction count // encoded size instead of the transaction count
const txSizeCostLimit = 0x10000 const txSizeCostLimit = 0x4000
const ( const (
announceTypeNone = iota announceTypeNone = iota
@ -537,7 +537,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
send = send.add("flowControl/MRR", server.defParams.MinRecharge) send = send.add("flowControl/MRR", server.defParams.MinRecharge)
var costList RequestCostList var costList RequestCostList
if server.costTracker != nil { if server.costTracker != nil {
costList = server.costTracker.makeCostList() costList = server.costTracker.makeCostList(server.costTracker.globalFactor())
} else { } else {
costList = testCostList() costList = testCostList()
} }

View file

@ -63,10 +63,10 @@ type LesServer struct {
thcNormal, thcBlockProcessing int // serving thread count for normal operation and block processing mode thcNormal, thcBlockProcessing int // serving thread count for normal operation and block processing mode
maxPeers int maxPeers int
freeClientCap uint64 minCapacity, freeClientCap uint64
freeClientPool *freeClientPool freeClientPool *freeClientPool
priorityClientPool *priorityClientPool priorityClientPool *priorityClientPool
} }
func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
@ -119,13 +119,13 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
bloomTrieIndexer: light.NewBloomTrieIndexer(eth.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency), bloomTrieIndexer: light.NewBloomTrieIndexer(eth.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency),
protocolManager: pm, protocolManager: pm,
}, },
costTracker: newCostTracker(eth.ChainDb(), config, requestLogger),
quitSync: quitSync, quitSync: quitSync,
lesTopics: lesTopics, lesTopics: lesTopics,
onlyAnnounce: config.OnlyAnnounce, onlyAnnounce: config.OnlyAnnounce,
csvLogger: csvLogger, csvLogger: csvLogger,
logTotalCap: requestLogger.NewChannel("totalCapacity", 0.01), logTotalCap: requestLogger.NewChannel("totalCapacity", 0.01),
} }
srv.costTracker, srv.minCapacity = newCostTracker(eth.ChainDb(), config, requestLogger)
logger := log.New() logger := log.New()
pm.server = srv pm.server = srv
@ -229,9 +229,9 @@ func (s *LesServer) Start(srvr *p2p.Server) {
s.maxPeers = s.config.LightPeers s.maxPeers = s.config.LightPeers
totalRecharge := s.costTracker.totalRecharge() totalRecharge := s.costTracker.totalRecharge()
if s.maxPeers > 0 { if s.maxPeers > 0 {
s.freeClientCap = minCapacity //totalRecharge / uint64(s.maxPeers) s.freeClientCap = s.minCapacity //totalRecharge / uint64(s.maxPeers)
if s.freeClientCap < minCapacity { if s.freeClientCap < s.minCapacity {
s.freeClientCap = minCapacity s.freeClientCap = s.minCapacity
} }
if s.freeClientCap > 0 { if s.freeClientCap > 0 {
s.defParams = flowcontrol.ServerParams{ s.defParams = flowcontrol.ServerParams{