ceremonyclient/go-libp2p/p2p/host/resource-manager/limit_defaults.go
2023-08-20 23:07:43 -05:00

880 lines
27 KiB
Go

package rcmgr
import (
"encoding/json"
"fmt"
"math"
"strconv"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/pbnjay/memory"
)
type baseLimitConfig struct {
BaseLimit BaseLimit
BaseLimitIncrease BaseLimitIncrease
}
// ScalingLimitConfig is a struct for configuring default limits.
// {}BaseLimit is the limits that Apply for a minimal node (128 MB of memory for libp2p) and 256 file descriptors.
// {}LimitIncrease is the additional limit granted for every additional 1 GB of RAM.
type ScalingLimitConfig struct {
SystemBaseLimit BaseLimit
SystemLimitIncrease BaseLimitIncrease
TransientBaseLimit BaseLimit
TransientLimitIncrease BaseLimitIncrease
AllowlistedSystemBaseLimit BaseLimit
AllowlistedSystemLimitIncrease BaseLimitIncrease
AllowlistedTransientBaseLimit BaseLimit
AllowlistedTransientLimitIncrease BaseLimitIncrease
ServiceBaseLimit BaseLimit
ServiceLimitIncrease BaseLimitIncrease
ServiceLimits map[string]baseLimitConfig // use AddServiceLimit to modify
ServicePeerBaseLimit BaseLimit
ServicePeerLimitIncrease BaseLimitIncrease
ServicePeerLimits map[string]baseLimitConfig // use AddServicePeerLimit to modify
ProtocolBaseLimit BaseLimit
ProtocolLimitIncrease BaseLimitIncrease
ProtocolLimits map[protocol.ID]baseLimitConfig // use AddProtocolLimit to modify
ProtocolPeerBaseLimit BaseLimit
ProtocolPeerLimitIncrease BaseLimitIncrease
ProtocolPeerLimits map[protocol.ID]baseLimitConfig // use AddProtocolPeerLimit to modify
PeerBaseLimit BaseLimit
PeerLimitIncrease BaseLimitIncrease
PeerLimits map[peer.ID]baseLimitConfig // use AddPeerLimit to modify
ConnBaseLimit BaseLimit
ConnLimitIncrease BaseLimitIncrease
StreamBaseLimit BaseLimit
StreamLimitIncrease BaseLimitIncrease
}
func (cfg *ScalingLimitConfig) AddServiceLimit(svc string, base BaseLimit, inc BaseLimitIncrease) {
if cfg.ServiceLimits == nil {
cfg.ServiceLimits = make(map[string]baseLimitConfig)
}
cfg.ServiceLimits[svc] = baseLimitConfig{
BaseLimit: base,
BaseLimitIncrease: inc,
}
}
func (cfg *ScalingLimitConfig) AddProtocolLimit(proto protocol.ID, base BaseLimit, inc BaseLimitIncrease) {
if cfg.ProtocolLimits == nil {
cfg.ProtocolLimits = make(map[protocol.ID]baseLimitConfig)
}
cfg.ProtocolLimits[proto] = baseLimitConfig{
BaseLimit: base,
BaseLimitIncrease: inc,
}
}
func (cfg *ScalingLimitConfig) AddPeerLimit(p peer.ID, base BaseLimit, inc BaseLimitIncrease) {
if cfg.PeerLimits == nil {
cfg.PeerLimits = make(map[peer.ID]baseLimitConfig)
}
cfg.PeerLimits[p] = baseLimitConfig{
BaseLimit: base,
BaseLimitIncrease: inc,
}
}
func (cfg *ScalingLimitConfig) AddServicePeerLimit(svc string, base BaseLimit, inc BaseLimitIncrease) {
if cfg.ServicePeerLimits == nil {
cfg.ServicePeerLimits = make(map[string]baseLimitConfig)
}
cfg.ServicePeerLimits[svc] = baseLimitConfig{
BaseLimit: base,
BaseLimitIncrease: inc,
}
}
func (cfg *ScalingLimitConfig) AddProtocolPeerLimit(proto protocol.ID, base BaseLimit, inc BaseLimitIncrease) {
if cfg.ProtocolPeerLimits == nil {
cfg.ProtocolPeerLimits = make(map[protocol.ID]baseLimitConfig)
}
cfg.ProtocolPeerLimits[proto] = baseLimitConfig{
BaseLimit: base,
BaseLimitIncrease: inc,
}
}
type LimitVal int
const (
// DefaultLimit is the default value for resources. The exact value depends on the context, but will get values from `DefaultLimits`.
DefaultLimit LimitVal = 0
// Unlimited is the value for unlimited resources. An arbitrarily high number will also work.
Unlimited LimitVal = -1
// BlockAllLimit is the LimitVal for allowing no amount of resources.
BlockAllLimit LimitVal = -2
)
func (l LimitVal) MarshalJSON() ([]byte, error) {
if l == Unlimited {
return json.Marshal("unlimited")
} else if l == DefaultLimit {
return json.Marshal("default")
} else if l == BlockAllLimit {
return json.Marshal("blockAll")
}
return json.Marshal(int(l))
}
func (l *LimitVal) UnmarshalJSON(b []byte) error {
if string(b) == `"default"` {
*l = DefaultLimit
return nil
} else if string(b) == `"unlimited"` {
*l = Unlimited
return nil
} else if string(b) == `"blockAll"` {
*l = BlockAllLimit
return nil
}
var val int
if err := json.Unmarshal(b, &val); err != nil {
return err
}
if val == 0 {
// If there is an explicit 0 in the JSON we should interpret this as block all.
*l = BlockAllLimit
return nil
}
*l = LimitVal(val)
return nil
}
func (l LimitVal) Build(defaultVal int) int {
if l == DefaultLimit {
return defaultVal
}
if l == Unlimited {
return math.MaxInt
}
if l == BlockAllLimit {
return 0
}
return int(l)
}
type LimitVal64 int64
const (
// Default is the default value for resources.
DefaultLimit64 LimitVal64 = 0
// Unlimited is the value for unlimited resources.
Unlimited64 LimitVal64 = -1
// BlockAllLimit64 is the LimitVal for allowing no amount of resources.
BlockAllLimit64 LimitVal64 = -2
)
func (l LimitVal64) MarshalJSON() ([]byte, error) {
if l == Unlimited64 {
return json.Marshal("unlimited")
} else if l == DefaultLimit64 {
return json.Marshal("default")
} else if l == BlockAllLimit64 {
return json.Marshal("blockAll")
}
// Convert this to a string because JSON doesn't support 64-bit integers.
return json.Marshal(strconv.FormatInt(int64(l), 10))
}
func (l *LimitVal64) UnmarshalJSON(b []byte) error {
if string(b) == `"default"` {
*l = DefaultLimit64
return nil
} else if string(b) == `"unlimited"` {
*l = Unlimited64
return nil
} else if string(b) == `"blockAll"` {
*l = BlockAllLimit64
return nil
}
var val string
if err := json.Unmarshal(b, &val); err != nil {
// Is this an integer? Possible because of backwards compatibility.
var val int
if err := json.Unmarshal(b, &val); err != nil {
return fmt.Errorf("failed to unmarshal limit value: %w", err)
}
if val == 0 {
// If there is an explicit 0 in the JSON we should interpret this as block all.
*l = BlockAllLimit64
return nil
}
*l = LimitVal64(val)
return nil
}
i, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
}
if i == 0 {
// If there is an explicit 0 in the JSON we should interpret this as block all.
*l = BlockAllLimit64
return nil
}
*l = LimitVal64(i)
return nil
}
func (l LimitVal64) Build(defaultVal int64) int64 {
if l == DefaultLimit64 {
return defaultVal
}
if l == Unlimited64 {
return math.MaxInt64
}
if l == BlockAllLimit64 {
return 0
}
return int64(l)
}
// ResourceLimits is the type for basic resource limits.
type ResourceLimits struct {
Streams LimitVal `json:",omitempty"`
StreamsInbound LimitVal `json:",omitempty"`
StreamsOutbound LimitVal `json:",omitempty"`
Conns LimitVal `json:",omitempty"`
ConnsInbound LimitVal `json:",omitempty"`
ConnsOutbound LimitVal `json:",omitempty"`
FD LimitVal `json:",omitempty"`
Memory LimitVal64 `json:",omitempty"`
}
func (l *ResourceLimits) IsDefault() bool {
if l == nil {
return true
}
if l.Streams == DefaultLimit &&
l.StreamsInbound == DefaultLimit &&
l.StreamsOutbound == DefaultLimit &&
l.Conns == DefaultLimit &&
l.ConnsInbound == DefaultLimit &&
l.ConnsOutbound == DefaultLimit &&
l.FD == DefaultLimit &&
l.Memory == DefaultLimit64 {
return true
}
return false
}
func (l *ResourceLimits) ToMaybeNilPtr() *ResourceLimits {
if l.IsDefault() {
return nil
}
return l
}
// Apply overwrites all default limits with the values of l2
func (l *ResourceLimits) Apply(l2 ResourceLimits) {
if l.Streams == DefaultLimit {
l.Streams = l2.Streams
}
if l.StreamsInbound == DefaultLimit {
l.StreamsInbound = l2.StreamsInbound
}
if l.StreamsOutbound == DefaultLimit {
l.StreamsOutbound = l2.StreamsOutbound
}
if l.Conns == DefaultLimit {
l.Conns = l2.Conns
}
if l.ConnsInbound == DefaultLimit {
l.ConnsInbound = l2.ConnsInbound
}
if l.ConnsOutbound == DefaultLimit {
l.ConnsOutbound = l2.ConnsOutbound
}
if l.FD == DefaultLimit {
l.FD = l2.FD
}
if l.Memory == DefaultLimit64 {
l.Memory = l2.Memory
}
}
func (l *ResourceLimits) Build(defaults Limit) BaseLimit {
if l == nil {
return BaseLimit{
Streams: defaults.GetStreamTotalLimit(),
StreamsInbound: defaults.GetStreamLimit(network.DirInbound),
StreamsOutbound: defaults.GetStreamLimit(network.DirOutbound),
Conns: defaults.GetConnTotalLimit(),
ConnsInbound: defaults.GetConnLimit(network.DirInbound),
ConnsOutbound: defaults.GetConnLimit(network.DirOutbound),
FD: defaults.GetFDLimit(),
Memory: defaults.GetMemoryLimit(),
}
}
return BaseLimit{
Streams: l.Streams.Build(defaults.GetStreamTotalLimit()),
StreamsInbound: l.StreamsInbound.Build(defaults.GetStreamLimit(network.DirInbound)),
StreamsOutbound: l.StreamsOutbound.Build(defaults.GetStreamLimit(network.DirOutbound)),
Conns: l.Conns.Build(defaults.GetConnTotalLimit()),
ConnsInbound: l.ConnsInbound.Build(defaults.GetConnLimit(network.DirInbound)),
ConnsOutbound: l.ConnsOutbound.Build(defaults.GetConnLimit(network.DirOutbound)),
FD: l.FD.Build(defaults.GetFDLimit()),
Memory: l.Memory.Build(defaults.GetMemoryLimit()),
}
}
type PartialLimitConfig struct {
System ResourceLimits `json:",omitempty"`
Transient ResourceLimits `json:",omitempty"`
// Limits that are applied to resources with an allowlisted multiaddr.
// These will only be used if the normal System & Transient limits are
// reached.
AllowlistedSystem ResourceLimits `json:",omitempty"`
AllowlistedTransient ResourceLimits `json:",omitempty"`
ServiceDefault ResourceLimits `json:",omitempty"`
Service map[string]ResourceLimits `json:",omitempty"`
ServicePeerDefault ResourceLimits `json:",omitempty"`
ServicePeer map[string]ResourceLimits `json:",omitempty"`
ProtocolDefault ResourceLimits `json:",omitempty"`
Protocol map[protocol.ID]ResourceLimits `json:",omitempty"`
ProtocolPeerDefault ResourceLimits `json:",omitempty"`
ProtocolPeer map[protocol.ID]ResourceLimits `json:",omitempty"`
PeerDefault ResourceLimits `json:",omitempty"`
Peer map[peer.ID]ResourceLimits `json:",omitempty"`
Conn ResourceLimits `json:",omitempty"`
Stream ResourceLimits `json:",omitempty"`
}
func (cfg *PartialLimitConfig) MarshalJSON() ([]byte, error) {
// we want to marshal the encoded peer id
encodedPeerMap := make(map[string]ResourceLimits, len(cfg.Peer))
for p, v := range cfg.Peer {
encodedPeerMap[p.String()] = v
}
type Alias PartialLimitConfig
return json.Marshal(&struct {
*Alias
// String so we can have the properly marshalled peer id
Peer map[string]ResourceLimits `json:",omitempty"`
// The rest of the fields as pointers so that we omit empty values in the serialized result
System *ResourceLimits `json:",omitempty"`
Transient *ResourceLimits `json:",omitempty"`
AllowlistedSystem *ResourceLimits `json:",omitempty"`
AllowlistedTransient *ResourceLimits `json:",omitempty"`
ServiceDefault *ResourceLimits `json:",omitempty"`
ServicePeerDefault *ResourceLimits `json:",omitempty"`
ProtocolDefault *ResourceLimits `json:",omitempty"`
ProtocolPeerDefault *ResourceLimits `json:",omitempty"`
PeerDefault *ResourceLimits `json:",omitempty"`
Conn *ResourceLimits `json:",omitempty"`
Stream *ResourceLimits `json:",omitempty"`
}{
Alias: (*Alias)(cfg),
Peer: encodedPeerMap,
System: cfg.System.ToMaybeNilPtr(),
Transient: cfg.Transient.ToMaybeNilPtr(),
AllowlistedSystem: cfg.AllowlistedSystem.ToMaybeNilPtr(),
AllowlistedTransient: cfg.AllowlistedTransient.ToMaybeNilPtr(),
ServiceDefault: cfg.ServiceDefault.ToMaybeNilPtr(),
ServicePeerDefault: cfg.ServicePeerDefault.ToMaybeNilPtr(),
ProtocolDefault: cfg.ProtocolDefault.ToMaybeNilPtr(),
ProtocolPeerDefault: cfg.ProtocolPeerDefault.ToMaybeNilPtr(),
PeerDefault: cfg.PeerDefault.ToMaybeNilPtr(),
Conn: cfg.Conn.ToMaybeNilPtr(),
Stream: cfg.Stream.ToMaybeNilPtr(),
})
}
func applyResourceLimitsMap[K comparable](this *map[K]ResourceLimits, other map[K]ResourceLimits, fallbackDefault ResourceLimits) {
for k, l := range *this {
r := fallbackDefault
if l2, ok := other[k]; ok {
r = l2
}
l.Apply(r)
(*this)[k] = l
}
if *this == nil && other != nil {
*this = make(map[K]ResourceLimits)
}
for k, l := range other {
if _, ok := (*this)[k]; !ok {
(*this)[k] = l
}
}
}
func (cfg *PartialLimitConfig) Apply(c PartialLimitConfig) {
cfg.System.Apply(c.System)
cfg.Transient.Apply(c.Transient)
cfg.AllowlistedSystem.Apply(c.AllowlistedSystem)
cfg.AllowlistedTransient.Apply(c.AllowlistedTransient)
cfg.ServiceDefault.Apply(c.ServiceDefault)
cfg.ServicePeerDefault.Apply(c.ServicePeerDefault)
cfg.ProtocolDefault.Apply(c.ProtocolDefault)
cfg.ProtocolPeerDefault.Apply(c.ProtocolPeerDefault)
cfg.PeerDefault.Apply(c.PeerDefault)
cfg.Conn.Apply(c.Conn)
cfg.Stream.Apply(c.Stream)
applyResourceLimitsMap(&cfg.Service, c.Service, cfg.ServiceDefault)
applyResourceLimitsMap(&cfg.ServicePeer, c.ServicePeer, cfg.ServicePeerDefault)
applyResourceLimitsMap(&cfg.Protocol, c.Protocol, cfg.ProtocolDefault)
applyResourceLimitsMap(&cfg.ProtocolPeer, c.ProtocolPeer, cfg.ProtocolPeerDefault)
applyResourceLimitsMap(&cfg.Peer, c.Peer, cfg.PeerDefault)
}
func (cfg PartialLimitConfig) Build(defaults ConcreteLimitConfig) ConcreteLimitConfig {
out := defaults
out.system = cfg.System.Build(defaults.system)
out.transient = cfg.Transient.Build(defaults.transient)
out.allowlistedSystem = cfg.AllowlistedSystem.Build(defaults.allowlistedSystem)
out.allowlistedTransient = cfg.AllowlistedTransient.Build(defaults.allowlistedTransient)
out.serviceDefault = cfg.ServiceDefault.Build(defaults.serviceDefault)
out.servicePeerDefault = cfg.ServicePeerDefault.Build(defaults.servicePeerDefault)
out.protocolDefault = cfg.ProtocolDefault.Build(defaults.protocolDefault)
out.protocolPeerDefault = cfg.ProtocolPeerDefault.Build(defaults.protocolPeerDefault)
out.peerDefault = cfg.PeerDefault.Build(defaults.peerDefault)
out.conn = cfg.Conn.Build(defaults.conn)
out.stream = cfg.Stream.Build(defaults.stream)
out.service = buildMapWithDefault(cfg.Service, defaults.service, out.serviceDefault)
out.servicePeer = buildMapWithDefault(cfg.ServicePeer, defaults.servicePeer, out.servicePeerDefault)
out.protocol = buildMapWithDefault(cfg.Protocol, defaults.protocol, out.protocolDefault)
out.protocolPeer = buildMapWithDefault(cfg.ProtocolPeer, defaults.protocolPeer, out.protocolPeerDefault)
out.peer = buildMapWithDefault(cfg.Peer, defaults.peer, out.peerDefault)
return out
}
func buildMapWithDefault[K comparable](definedLimits map[K]ResourceLimits, defaults map[K]BaseLimit, fallbackDefault BaseLimit) map[K]BaseLimit {
if definedLimits == nil && defaults == nil {
return nil
}
out := make(map[K]BaseLimit)
for k, l := range defaults {
out[k] = l
}
for k, l := range definedLimits {
if defaultForKey, ok := out[k]; ok {
out[k] = l.Build(defaultForKey)
} else {
out[k] = l.Build(fallbackDefault)
}
}
return out
}
// ConcreteLimitConfig is similar to PartialLimitConfig, but all values are defined.
// There is no unset "default" value. Commonly constructed by calling
// PartialLimitConfig.Build(rcmgr.DefaultLimits.AutoScale())
type ConcreteLimitConfig struct {
system BaseLimit
transient BaseLimit
// Limits that are applied to resources with an allowlisted multiaddr.
// These will only be used if the normal System & Transient limits are
// reached.
allowlistedSystem BaseLimit
allowlistedTransient BaseLimit
serviceDefault BaseLimit
service map[string]BaseLimit
servicePeerDefault BaseLimit
servicePeer map[string]BaseLimit
protocolDefault BaseLimit
protocol map[protocol.ID]BaseLimit
protocolPeerDefault BaseLimit
protocolPeer map[protocol.ID]BaseLimit
peerDefault BaseLimit
peer map[peer.ID]BaseLimit
conn BaseLimit
stream BaseLimit
}
func resourceLimitsMapFromBaseLimitMap[K comparable](baseLimitMap map[K]BaseLimit) map[K]ResourceLimits {
if baseLimitMap == nil {
return nil
}
out := make(map[K]ResourceLimits)
for k, l := range baseLimitMap {
out[k] = l.ToResourceLimits()
}
return out
}
// ToPartialLimitConfig converts a ConcreteLimitConfig to a PartialLimitConfig.
// The returned PartialLimitConfig will have no default values.
func (cfg ConcreteLimitConfig) ToPartialLimitConfig() PartialLimitConfig {
return PartialLimitConfig{
System: cfg.system.ToResourceLimits(),
Transient: cfg.transient.ToResourceLimits(),
AllowlistedSystem: cfg.allowlistedSystem.ToResourceLimits(),
AllowlistedTransient: cfg.allowlistedTransient.ToResourceLimits(),
ServiceDefault: cfg.serviceDefault.ToResourceLimits(),
Service: resourceLimitsMapFromBaseLimitMap(cfg.service),
ServicePeerDefault: cfg.servicePeerDefault.ToResourceLimits(),
ServicePeer: resourceLimitsMapFromBaseLimitMap(cfg.servicePeer),
ProtocolDefault: cfg.protocolDefault.ToResourceLimits(),
Protocol: resourceLimitsMapFromBaseLimitMap(cfg.protocol),
ProtocolPeerDefault: cfg.protocolPeerDefault.ToResourceLimits(),
ProtocolPeer: resourceLimitsMapFromBaseLimitMap(cfg.protocolPeer),
PeerDefault: cfg.peerDefault.ToResourceLimits(),
Peer: resourceLimitsMapFromBaseLimitMap(cfg.peer),
Conn: cfg.conn.ToResourceLimits(),
Stream: cfg.stream.ToResourceLimits(),
}
}
// Scale scales up a limit configuration.
// memory is the amount of memory that the stack is allowed to consume,
// for a dedicated node it's recommended to use 1/8 of the installed system memory.
// If memory is smaller than 128 MB, the base configuration will be used.
func (cfg *ScalingLimitConfig) Scale(memory int64, numFD int) ConcreteLimitConfig {
lc := ConcreteLimitConfig{
system: scale(cfg.SystemBaseLimit, cfg.SystemLimitIncrease, memory, numFD),
transient: scale(cfg.TransientBaseLimit, cfg.TransientLimitIncrease, memory, numFD),
allowlistedSystem: scale(cfg.AllowlistedSystemBaseLimit, cfg.AllowlistedSystemLimitIncrease, memory, numFD),
allowlistedTransient: scale(cfg.AllowlistedTransientBaseLimit, cfg.AllowlistedTransientLimitIncrease, memory, numFD),
serviceDefault: scale(cfg.ServiceBaseLimit, cfg.ServiceLimitIncrease, memory, numFD),
servicePeerDefault: scale(cfg.ServicePeerBaseLimit, cfg.ServicePeerLimitIncrease, memory, numFD),
protocolDefault: scale(cfg.ProtocolBaseLimit, cfg.ProtocolLimitIncrease, memory, numFD),
protocolPeerDefault: scale(cfg.ProtocolPeerBaseLimit, cfg.ProtocolPeerLimitIncrease, memory, numFD),
peerDefault: scale(cfg.PeerBaseLimit, cfg.PeerLimitIncrease, memory, numFD),
conn: scale(cfg.ConnBaseLimit, cfg.ConnLimitIncrease, memory, numFD),
stream: scale(cfg.StreamBaseLimit, cfg.ConnLimitIncrease, memory, numFD),
}
if cfg.ServiceLimits != nil {
lc.service = make(map[string]BaseLimit)
for svc, l := range cfg.ServiceLimits {
lc.service[svc] = scale(l.BaseLimit, l.BaseLimitIncrease, memory, numFD)
}
}
if cfg.ProtocolLimits != nil {
lc.protocol = make(map[protocol.ID]BaseLimit)
for proto, l := range cfg.ProtocolLimits {
lc.protocol[proto] = scale(l.BaseLimit, l.BaseLimitIncrease, memory, numFD)
}
}
if cfg.PeerLimits != nil {
lc.peer = make(map[peer.ID]BaseLimit)
for p, l := range cfg.PeerLimits {
lc.peer[p] = scale(l.BaseLimit, l.BaseLimitIncrease, memory, numFD)
}
}
if cfg.ServicePeerLimits != nil {
lc.servicePeer = make(map[string]BaseLimit)
for svc, l := range cfg.ServicePeerLimits {
lc.servicePeer[svc] = scale(l.BaseLimit, l.BaseLimitIncrease, memory, numFD)
}
}
if cfg.ProtocolPeerLimits != nil {
lc.protocolPeer = make(map[protocol.ID]BaseLimit)
for p, l := range cfg.ProtocolPeerLimits {
lc.protocolPeer[p] = scale(l.BaseLimit, l.BaseLimitIncrease, memory, numFD)
}
}
return lc
}
func (cfg *ScalingLimitConfig) AutoScale() ConcreteLimitConfig {
return cfg.Scale(
int64(memory.TotalMemory())/8,
getNumFDs()/2,
)
}
func scale(base BaseLimit, inc BaseLimitIncrease, memory int64, numFD int) BaseLimit {
// mebibytesAvailable represents how many MiBs we're allowed to use. Used to
// scale the limits. If this is below 128MiB we set it to 0 to just use the
// base amounts.
var mebibytesAvailable int
if memory > 128<<20 {
mebibytesAvailable = int((memory) >> 20)
}
l := BaseLimit{
StreamsInbound: base.StreamsInbound + (inc.StreamsInbound*mebibytesAvailable)>>10,
StreamsOutbound: base.StreamsOutbound + (inc.StreamsOutbound*mebibytesAvailable)>>10,
Streams: base.Streams + (inc.Streams*mebibytesAvailable)>>10,
ConnsInbound: base.ConnsInbound + (inc.ConnsInbound*mebibytesAvailable)>>10,
ConnsOutbound: base.ConnsOutbound + (inc.ConnsOutbound*mebibytesAvailable)>>10,
Conns: base.Conns + (inc.Conns*mebibytesAvailable)>>10,
Memory: base.Memory + (inc.Memory*int64(mebibytesAvailable))>>10,
FD: base.FD,
}
if inc.FDFraction > 0 && numFD > 0 {
l.FD = int(inc.FDFraction * float64(numFD))
if l.FD < base.FD {
// Use at least the base amount
l.FD = base.FD
}
}
return l
}
// DefaultLimits are the limits used by the default limiter constructors.
var DefaultLimits = ScalingLimitConfig{
SystemBaseLimit: BaseLimit{
ConnsInbound: 64,
ConnsOutbound: 128,
Conns: 128,
StreamsInbound: 64 * 16,
StreamsOutbound: 128 * 16,
Streams: 128 * 16,
Memory: 128 << 20,
FD: 256,
},
SystemLimitIncrease: BaseLimitIncrease{
ConnsInbound: 64,
ConnsOutbound: 128,
Conns: 128,
StreamsInbound: 64 * 16,
StreamsOutbound: 128 * 16,
Streams: 128 * 16,
Memory: 1 << 30,
FDFraction: 1,
},
TransientBaseLimit: BaseLimit{
ConnsInbound: 32,
ConnsOutbound: 64,
Conns: 64,
StreamsInbound: 128,
StreamsOutbound: 256,
Streams: 256,
Memory: 32 << 20,
FD: 64,
},
TransientLimitIncrease: BaseLimitIncrease{
ConnsInbound: 16,
ConnsOutbound: 32,
Conns: 32,
StreamsInbound: 128,
StreamsOutbound: 256,
Streams: 256,
Memory: 128 << 20,
FDFraction: 0.25,
},
// Setting the allowlisted limits to be the same as the normal limits. The
// allowlist only activates when you reach your normal system/transient
// limits. So it's okay if these limits err on the side of being too big,
// since most of the time you won't even use any of these. Tune these down
// if you want to manage your resources against an allowlisted endpoint.
AllowlistedSystemBaseLimit: BaseLimit{
ConnsInbound: 64,
ConnsOutbound: 128,
Conns: 128,
StreamsInbound: 64 * 16,
StreamsOutbound: 128 * 16,
Streams: 128 * 16,
Memory: 128 << 20,
FD: 256,
},
AllowlistedSystemLimitIncrease: BaseLimitIncrease{
ConnsInbound: 64,
ConnsOutbound: 128,
Conns: 128,
StreamsInbound: 64 * 16,
StreamsOutbound: 128 * 16,
Streams: 128 * 16,
Memory: 1 << 30,
FDFraction: 1,
},
AllowlistedTransientBaseLimit: BaseLimit{
ConnsInbound: 32,
ConnsOutbound: 64,
Conns: 64,
StreamsInbound: 128,
StreamsOutbound: 256,
Streams: 256,
Memory: 32 << 20,
FD: 64,
},
AllowlistedTransientLimitIncrease: BaseLimitIncrease{
ConnsInbound: 16,
ConnsOutbound: 32,
Conns: 32,
StreamsInbound: 128,
StreamsOutbound: 256,
Streams: 256,
Memory: 128 << 20,
FDFraction: 0.25,
},
ServiceBaseLimit: BaseLimit{
StreamsInbound: 1024,
StreamsOutbound: 4096,
Streams: 4096,
Memory: 64 << 20,
},
ServiceLimitIncrease: BaseLimitIncrease{
StreamsInbound: 512,
StreamsOutbound: 2048,
Streams: 2048,
Memory: 128 << 20,
},
ServicePeerBaseLimit: BaseLimit{
StreamsInbound: 128,
StreamsOutbound: 256,
Streams: 256,
Memory: 16 << 20,
},
ServicePeerLimitIncrease: BaseLimitIncrease{
StreamsInbound: 4,
StreamsOutbound: 8,
Streams: 8,
Memory: 4 << 20,
},
ProtocolBaseLimit: BaseLimit{
StreamsInbound: 512,
StreamsOutbound: 2048,
Streams: 2048,
Memory: 64 << 20,
},
ProtocolLimitIncrease: BaseLimitIncrease{
StreamsInbound: 256,
StreamsOutbound: 512,
Streams: 512,
Memory: 164 << 20,
},
ProtocolPeerBaseLimit: BaseLimit{
StreamsInbound: 64,
StreamsOutbound: 128,
Streams: 256,
Memory: 16 << 20,
},
ProtocolPeerLimitIncrease: BaseLimitIncrease{
StreamsInbound: 4,
StreamsOutbound: 8,
Streams: 16,
Memory: 4,
},
PeerBaseLimit: BaseLimit{
// 8 for now so that it matches the number of concurrent dials we may do
// in swarm_dial.go. With future smart dialing work we should bring this
// down
ConnsInbound: 8,
ConnsOutbound: 8,
Conns: 8,
StreamsInbound: 256,
StreamsOutbound: 512,
Streams: 512,
Memory: 64 << 20,
FD: 4,
},
PeerLimitIncrease: BaseLimitIncrease{
StreamsInbound: 128,
StreamsOutbound: 256,
Streams: 256,
Memory: 128 << 20,
FDFraction: 1.0 / 64,
},
ConnBaseLimit: BaseLimit{
ConnsInbound: 1,
ConnsOutbound: 1,
Conns: 1,
FD: 1,
Memory: 32 << 20,
},
StreamBaseLimit: BaseLimit{
StreamsInbound: 1,
StreamsOutbound: 1,
Streams: 1,
Memory: 16 << 20,
},
}
var infiniteBaseLimit = BaseLimit{
Streams: math.MaxInt,
StreamsInbound: math.MaxInt,
StreamsOutbound: math.MaxInt,
Conns: math.MaxInt,
ConnsInbound: math.MaxInt,
ConnsOutbound: math.MaxInt,
FD: math.MaxInt,
Memory: math.MaxInt64,
}
// InfiniteLimits are a limiter configuration that uses unlimited limits, thus effectively not limiting anything.
// Keep in mind that the operating system limits the number of file descriptors that an application can use.
var InfiniteLimits = ConcreteLimitConfig{
system: infiniteBaseLimit,
transient: infiniteBaseLimit,
allowlistedSystem: infiniteBaseLimit,
allowlistedTransient: infiniteBaseLimit,
serviceDefault: infiniteBaseLimit,
servicePeerDefault: infiniteBaseLimit,
protocolDefault: infiniteBaseLimit,
protocolPeerDefault: infiniteBaseLimit,
peerDefault: infiniteBaseLimit,
conn: infiniteBaseLimit,
stream: infiniteBaseLimit,
}