From 066de7c0c96b8838d6c39adca83f7f75d82c9410 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 6 Jan 2022 12:57:37 +0200 Subject: [PATCH] refactor limiters --- limit.go | 289 ++++++++++++----------------------------------- limit_dynamic.go | 91 +++++++++++++++ limit_static.go | 77 +++++++++++++ 3 files changed, 241 insertions(+), 216 deletions(-) create mode 100644 limit_dynamic.go create mode 100644 limit_static.go diff --git a/limit.go b/limit.go index a8359f8..0cda52b 100644 --- a/limit.go +++ b/limit.go @@ -4,9 +4,6 @@ import ( "github.com/libp2p/go-libp2p-core/network" "github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/protocol" - - "github.com/elastic/gosigar" - "github.com/pbnjay/memory" ) // Limit is an object that specifies basic resource limits. @@ -28,37 +25,6 @@ type Limiter interface { GetConnLimits() Limit } -type BaseLimit struct { - StreamsInbound int - StreamsOutbound int - ConnsInbound int - ConnsOutbound int - FD int -} - -// StaticLimit is a limit with static values. -type StaticLimit struct { - BaseLimit - Memory int64 -} - -var _ Limit = (*StaticLimit)(nil) - -// DynamicLimit is a limit with dynamic memory values, based on available memory -type DynamicLimit struct { - BaseLimit - - // MinMemory is the minimum memory for this limit - MinMemory int64 - // MaxMemory is the maximum memory for this limit - MaxMemory int64 - // MemoryFraction is the fraction of available memory allowed for this limit, - // bounded by [MinMemory, MaxMemory] - MemoryFraction int -} - -var _ Limit = (*DynamicLimit)(nil) - // BasicLimiter is a limiter with fixed limits. type BasicLimiter struct { SystemLimits Limit @@ -75,188 +41,13 @@ type BasicLimiter struct { var _ Limiter = (*BasicLimiter)(nil) -// NewStaticLimiter creates a limiter with default limits and a system memory cap; if the -// system memory cap is 0, then 1/8th of the total memory is used. -func NewStaticLimiter(memoryCap int64) *BasicLimiter { - if memoryCap == 0 { - memoryCap = int64(memory.TotalMemory() / 8) - } - - system := &StaticLimit{ - Memory: memoryCap, - BaseLimit: BaseLimit{ - StreamsInbound: 4096, - StreamsOutbound: 16384, - ConnsInbound: 256, - ConnsOutbound: 512, - FD: 512, - }, - } - transient := &StaticLimit{ - Memory: memoryCap / 16, - BaseLimit: BaseLimit{ - StreamsInbound: 128, - StreamsOutbound: 512, - ConnsInbound: 32, - ConnsOutbound: 128, - FD: 128, - }, - } - svc := &StaticLimit{ - Memory: memoryCap / 2, - BaseLimit: BaseLimit{ - StreamsInbound: 2048, - StreamsOutbound: 8192, - }, - } - proto := &StaticLimit{ - Memory: memoryCap / 4, - BaseLimit: BaseLimit{ - StreamsInbound: 1024, - StreamsOutbound: 4096, - }, - } - peer := &StaticLimit{ - Memory: memoryCap / 16, - BaseLimit: BaseLimit{ - StreamsInbound: 512, - StreamsOutbound: 2048, - ConnsInbound: 8, - ConnsOutbound: 16, - FD: 8, - }, - } - conn := &StaticLimit{ - Memory: 16 << 20, - BaseLimit: BaseLimit{ - ConnsInbound: 1, - ConnsOutbound: 1, - FD: 1, - }, - } - stream := &StaticLimit{ - Memory: 16 << 20, - BaseLimit: BaseLimit{ - StreamsInbound: 1, - StreamsOutbound: 1, - }, - } - - return &BasicLimiter{ - SystemLimits: system, - TransientLimits: transient, - DefaultServiceLimits: svc, - DefaultProtocolLimits: proto, - DefaultPeerLimits: peer, - ConnLimits: conn, - StreamLimits: stream, - } -} - -// NewDynamicLimiter creates a limiter with default limits and a memory cap dynamically computed -// based on available memory. minMemory and maxMemory specify the system memory bounds, -// while memFraction specifies the fraction of available memory available for the system, within -// the specified bounds. -func NewDynamicLimiter(minMemory, maxMemory int64, memFraction int) *BasicLimiter { - system := &DynamicLimit{ - MinMemory: minMemory, - MaxMemory: maxMemory, - MemoryFraction: memFraction, - BaseLimit: BaseLimit{ - StreamsInbound: 4096, - StreamsOutbound: 16384, - ConnsInbound: 256, - ConnsOutbound: 512, - FD: 512, - }, - } - transient := &DynamicLimit{ - MinMemory: minMemory / 16, - MaxMemory: maxMemory / 16, - MemoryFraction: memFraction * 16, - BaseLimit: BaseLimit{ - StreamsInbound: 128, - StreamsOutbound: 512, - ConnsInbound: 32, - ConnsOutbound: 128, - FD: 128, - }, - } - svc := &DynamicLimit{ - MinMemory: minMemory / 2, - MaxMemory: maxMemory / 2, - MemoryFraction: memFraction * 2, - BaseLimit: BaseLimit{ - StreamsInbound: 2048, - StreamsOutbound: 8192, - }, - } - proto := &DynamicLimit{ - MinMemory: minMemory / 4, - MaxMemory: maxMemory / 4, - MemoryFraction: memFraction * 4, - BaseLimit: BaseLimit{ - StreamsInbound: 1024, - StreamsOutbound: 4096, - }, - } - peer := &DynamicLimit{ - MinMemory: minMemory / 16, - MaxMemory: maxMemory / 16, - MemoryFraction: memFraction * 16, - BaseLimit: BaseLimit{ - StreamsInbound: 512, - StreamsOutbound: 2048, - ConnsInbound: 8, - ConnsOutbound: 16, - FD: 8, - }, - } - conn := &StaticLimit{ - Memory: 16 << 20, - BaseLimit: BaseLimit{ - ConnsInbound: 1, - ConnsOutbound: 1, - FD: 1, - }, - } - stream := &StaticLimit{ - Memory: 16 << 20, - BaseLimit: BaseLimit{ - StreamsInbound: 1, - StreamsOutbound: 1, - }, - } - - return &BasicLimiter{ - SystemLimits: system, - TransientLimits: transient, - DefaultServiceLimits: svc, - DefaultProtocolLimits: proto, - DefaultPeerLimits: peer, - ConnLimits: conn, - StreamLimits: stream, - } -} - -func (l *StaticLimit) GetMemoryLimit() int64 { - return l.Memory -} - -func (l *DynamicLimit) GetMemoryLimit() int64 { - var mem gosigar.Mem - if err := mem.Get(); err != nil { - panic(err) - } - - limit := int64(mem.ActualFree) / int64(l.MemoryFraction) - if limit < l.MinMemory { - limit = l.MinMemory - } else if limit > l.MaxMemory { - limit = l.MaxMemory - } - - return limit +// BaseLimit is a mixin type for basic resource limits. +type BaseLimit struct { + StreamsInbound int + StreamsOutbound int + ConnsInbound int + ConnsOutbound int + FD int } func (l *BaseLimit) GetStreamLimit(dir network.Direction) int { @@ -318,3 +109,69 @@ func (l *BasicLimiter) GetStreamLimits(p peer.ID) Limit { func (l *BasicLimiter) GetConnLimits() Limit { return l.ConnLimits } + +// DefaultSystemBaseLimit returns the default BaseLimit for the System Scope. +func DefaultSystemBaseLimit() BaseLimit { + return BaseLimit{ + StreamsInbound: 4096, + StreamsOutbound: 16384, + ConnsInbound: 256, + ConnsOutbound: 512, + FD: 512, + } +} + +// DefaultTransientBaseLimit returns the default BaseLimit for the Transient Scope. +func DefaultTransientBaseLimit() BaseLimit { + return BaseLimit{ + StreamsInbound: 128, + StreamsOutbound: 512, + ConnsInbound: 32, + ConnsOutbound: 128, + FD: 128, + } +} + +// DefaultServiceBaseLimit returns the default BaseLimit for Service Scopes. +func DefaultServiceBaseLimit() BaseLimit { + return BaseLimit{ + StreamsInbound: 2048, + StreamsOutbound: 8192, + } +} + +// DefaultProtocolBaseLimit returns the default BaseLimit for Protocol Scopes. +func DefaultProtocolBaseLimit() BaseLimit { + return BaseLimit{ + StreamsInbound: 1024, + StreamsOutbound: 4096, + } +} + +// DefaultPeerBaseLimit returns the default BaseLimit for Peer Scopes. +func DefaultPeerBaseLimit() BaseLimit { + return BaseLimit{ + StreamsInbound: 512, + StreamsOutbound: 2048, + ConnsInbound: 8, + ConnsOutbound: 16, + FD: 8, + } +} + +// ConnBaseLimit returns the BaseLimit for Connection Scopes. +func ConnBaseLimit() BaseLimit { + return BaseLimit{ + ConnsInbound: 1, + ConnsOutbound: 1, + FD: 1, + } +} + +// StreamBaseLimit returns the BaseLimit for Stream Scopes. +func StreamBaseLimit() BaseLimit { + return BaseLimit{ + StreamsInbound: 1, + StreamsOutbound: 1, + } +} diff --git a/limit_dynamic.go b/limit_dynamic.go new file mode 100644 index 0000000..11fe914 --- /dev/null +++ b/limit_dynamic.go @@ -0,0 +1,91 @@ +package rcmgr + +import ( + "github.com/elastic/gosigar" +) + +// DynamicLimit is a limit with dynamic memory values, based on available (free) memory +type DynamicLimit struct { + BaseLimit + + // MinMemory is the minimum memory for this limit + MinMemory int64 + // MaxMemory is the maximum memory for this limit + MaxMemory int64 + // MemoryFraction is the fraction of available memory allowed for this limit, + // bounded by [MinMemory, MaxMemory] + MemoryFraction float64 +} + +var _ Limit = (*DynamicLimit)(nil) + +func (l *DynamicLimit) GetMemoryLimit() int64 { + var mem gosigar.Mem + if err := mem.Get(); err != nil { + panic(err) + } + + limit := int64(float64(mem.ActualFree) * l.MemoryFraction) + if limit < l.MinMemory { + limit = l.MinMemory + } else if limit > l.MaxMemory { + limit = l.MaxMemory + } + + return limit +} + +// NewDynamicLimiter creates a limiter with default limits and a memory cap dynamically computed +// based on available memory. minMemory and maxMemory specify the system memory bounds, +// while memFraction specifies the fraction of available memory available for the system, within +// the specified bounds. +func NewDynamicLimiter(memFraction float64, minMemory, maxMemory int64) *BasicLimiter { + system := &DynamicLimit{ + MinMemory: minMemory, + MaxMemory: maxMemory, + MemoryFraction: memFraction, + BaseLimit: DefaultSystemBaseLimit(), + } + transient := &DynamicLimit{ + MinMemory: minMemory / 16, + MaxMemory: maxMemory / 16, + MemoryFraction: memFraction / 16, + BaseLimit: DefaultTransientBaseLimit(), + } + svc := &DynamicLimit{ + MinMemory: minMemory / 2, + MaxMemory: maxMemory / 2, + MemoryFraction: memFraction / 2, + BaseLimit: DefaultServiceBaseLimit(), + } + proto := &DynamicLimit{ + MinMemory: minMemory / 4, + MaxMemory: maxMemory / 4, + MemoryFraction: memFraction / 4, + BaseLimit: DefaultProtocolBaseLimit(), + } + peer := &DynamicLimit{ + MinMemory: minMemory / 16, + MaxMemory: maxMemory / 16, + MemoryFraction: memFraction / 16, + BaseLimit: DefaultPeerBaseLimit(), + } + conn := &StaticLimit{ + Memory: 16 << 20, + BaseLimit: ConnBaseLimit(), + } + stream := &StaticLimit{ + Memory: 16 << 20, + BaseLimit: StreamBaseLimit(), + } + + return &BasicLimiter{ + SystemLimits: system, + TransientLimits: transient, + DefaultServiceLimits: svc, + DefaultProtocolLimits: proto, + DefaultPeerLimits: peer, + ConnLimits: conn, + StreamLimits: stream, + } +} diff --git a/limit_static.go b/limit_static.go new file mode 100644 index 0000000..7cf076a --- /dev/null +++ b/limit_static.go @@ -0,0 +1,77 @@ +package rcmgr + +import ( + "github.com/pbnjay/memory" +) + +// StaticLimit is a limit with static values. +type StaticLimit struct { + BaseLimit + Memory int64 +} + +var _ Limit = (*StaticLimit)(nil) + +func (l *StaticLimit) GetMemoryLimit() int64 { + return l.Memory +} + +// NewStaticLimiter creates a limiter with default base limits and a system memory cap specified as +// a fraction of total system memory. The assigned memory will not be less than minMemory or more +// than maxMemory. +func NewStaticLimiter(memFraction float64, minMemory, maxMemory int64) *BasicLimiter { + memoryCap := int64(float64(memory.TotalMemory()) * memFraction) + switch { + case memoryCap < minMemory: + memoryCap = minMemory + case memoryCap > maxMemory: + memoryCap = maxMemory + } + return newDefaultStaticLimiter(memoryCap) +} + +// NewFixedLimiter creates a limiter with default base limits and a specified system memory cap. +func NewFixedLimiter(memoryCap int64) *BasicLimiter { + return newDefaultStaticLimiter(memoryCap) +} + +func newDefaultStaticLimiter(memoryCap int64) *BasicLimiter { + system := &StaticLimit{ + Memory: memoryCap, + BaseLimit: DefaultSystemBaseLimit(), + } + transient := &StaticLimit{ + Memory: memoryCap / 16, + BaseLimit: DefaultTransientBaseLimit(), + } + svc := &StaticLimit{ + Memory: memoryCap / 2, + BaseLimit: DefaultServiceBaseLimit(), + } + proto := &StaticLimit{ + Memory: memoryCap / 4, + BaseLimit: DefaultProtocolBaseLimit(), + } + peer := &StaticLimit{ + Memory: memoryCap / 16, + BaseLimit: DefaultPeerBaseLimit(), + } + conn := &StaticLimit{ + Memory: 16 << 20, + BaseLimit: ConnBaseLimit(), + } + stream := &StaticLimit{ + Memory: 16 << 20, + BaseLimit: StreamBaseLimit(), + } + + return &BasicLimiter{ + SystemLimits: system, + TransientLimits: transient, + DefaultServiceLimits: svc, + DefaultProtocolLimits: proto, + DefaultPeerLimits: peer, + ConnLimits: conn, + StreamLimits: stream, + } +}