mirror of
https://github.com/libp2p/go-libp2p-resource-manager.git
synced 2025-02-05 17:20:39 +08:00
Merge pull request #48 from libp2p/rework-limits
rewrite limits to allow auto-scaling
This commit is contained in:
commit
d0a1694030
6
go.mod
6
go.mod
@ -7,8 +7,9 @@ require (
|
||||
github.com/libp2p/go-libp2p-core v0.19.0
|
||||
github.com/multiformats/go-multiaddr v0.6.0
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
go.opencensus.io v0.23.0
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
)
|
||||
|
||||
require (
|
||||
@ -38,6 +39,5 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
8
go.sum
8
go.sum
@ -133,10 +133,13 @@ github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+
|
||||
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
@ -238,7 +241,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
216
limit.go
216
limit.go
@ -1,6 +1,9 @@
|
||||
package rcmgr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
@ -20,19 +23,6 @@ type Limit interface {
|
||||
GetConnTotalLimit() int
|
||||
// GetFDLimit returns the file descriptor limit.
|
||||
GetFDLimit() int
|
||||
|
||||
// WithMemoryLimit creates a copy of this limit object, with memory limit adjusted to
|
||||
// the specified memFraction of its current value, bounded by minMemory and maxMemory.
|
||||
WithMemoryLimit(memFraction float64, minMemory, maxMemory int64) Limit
|
||||
// WithStreamLimit creates a copy of this limit object, with stream limits adjusted
|
||||
// as specified.
|
||||
WithStreamLimit(numStreamsIn, numStreamsOut, numStreams int) Limit
|
||||
// WithConnLimit creates a copy of this limit object, with connetion limits adjusted
|
||||
// as specified.
|
||||
WithConnLimit(numConnsIn, numConnsOut, numConns int) Limit
|
||||
// WithFDLimit creates a copy of this limit object, with file descriptor limits adjusted
|
||||
// as specified
|
||||
WithFDLimit(numFD int) Limit
|
||||
}
|
||||
|
||||
// Limiter is the interface for providing limits to the resource manager.
|
||||
@ -48,25 +38,41 @@ type Limiter interface {
|
||||
GetConnLimits() Limit
|
||||
}
|
||||
|
||||
// BasicLimiter is a limiter with fixed limits.
|
||||
type BasicLimiter struct {
|
||||
SystemLimits Limit
|
||||
TransientLimits Limit
|
||||
DefaultServiceLimits Limit
|
||||
DefaultServicePeerLimits Limit
|
||||
ServiceLimits map[string]Limit
|
||||
ServicePeerLimits map[string]Limit
|
||||
DefaultProtocolLimits Limit
|
||||
DefaultProtocolPeerLimits Limit
|
||||
ProtocolLimits map[protocol.ID]Limit
|
||||
ProtocolPeerLimits map[protocol.ID]Limit
|
||||
DefaultPeerLimits Limit
|
||||
PeerLimits map[peer.ID]Limit
|
||||
ConnLimits Limit
|
||||
StreamLimits Limit
|
||||
// NewDefaultLimiterFromJSON creates a new limiter by parsing a json configuration,
|
||||
// using the default limits for fallback.
|
||||
func NewDefaultLimiterFromJSON(in io.Reader) (Limiter, error) {
|
||||
return NewLimiterFromJSON(in, DefaultLimits.AutoScale())
|
||||
}
|
||||
|
||||
var _ Limiter = (*BasicLimiter)(nil)
|
||||
// NewLimiterFromJSON creates a new limiter by parsing a json configuration.
|
||||
func NewLimiterFromJSON(in io.Reader, defaults LimitConfig) (Limiter, error) {
|
||||
cfg, err := readLimiterConfigFromJSON(in, defaults)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fixedLimiter{cfg}, nil
|
||||
}
|
||||
|
||||
func readLimiterConfigFromJSON(in io.Reader, defaults LimitConfig) (LimitConfig, error) {
|
||||
var cfg LimitConfig
|
||||
if err := json.NewDecoder(in).Decode(&cfg); err != nil {
|
||||
return LimitConfig{}, err
|
||||
}
|
||||
cfg.Apply(defaults)
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// fixedLimiter is a limiter with fixed limits.
|
||||
type fixedLimiter struct {
|
||||
LimitConfig
|
||||
}
|
||||
|
||||
var _ Limiter = (*fixedLimiter)(nil)
|
||||
|
||||
func NewFixedLimiter(conf LimitConfig) Limiter {
|
||||
log.Debugw("initializing new limiter with config", "limits", conf)
|
||||
return &fixedLimiter{LimitConfig: conf}
|
||||
}
|
||||
|
||||
// BaseLimit is a mixin type for basic resource limits.
|
||||
type BaseLimit struct {
|
||||
@ -77,13 +83,77 @@ type BaseLimit struct {
|
||||
ConnsInbound int
|
||||
ConnsOutbound int
|
||||
FD int
|
||||
Memory int64
|
||||
}
|
||||
|
||||
// MemoryLimit is a mixin type for memory limits
|
||||
type MemoryLimit struct {
|
||||
MemoryFraction float64
|
||||
MinMemory int64
|
||||
MaxMemory int64
|
||||
// Apply overwrites all zero-valued limits with the values of l2
|
||||
// Must not use a pointer receiver.
|
||||
func (l *BaseLimit) Apply(l2 BaseLimit) {
|
||||
if l.Streams == 0 {
|
||||
l.Streams = l2.Streams
|
||||
}
|
||||
if l.StreamsInbound == 0 {
|
||||
l.StreamsInbound = l2.StreamsInbound
|
||||
}
|
||||
if l.StreamsOutbound == 0 {
|
||||
l.StreamsOutbound = l2.StreamsOutbound
|
||||
}
|
||||
if l.Conns == 0 {
|
||||
l.Conns = l2.Conns
|
||||
}
|
||||
if l.ConnsInbound == 0 {
|
||||
l.ConnsInbound = l2.ConnsInbound
|
||||
}
|
||||
if l.ConnsOutbound == 0 {
|
||||
l.ConnsOutbound = l2.ConnsOutbound
|
||||
}
|
||||
if l.Memory == 0 {
|
||||
l.Memory = l2.Memory
|
||||
}
|
||||
if l.FD == 0 {
|
||||
l.FD = l2.FD
|
||||
}
|
||||
}
|
||||
|
||||
// BaseLimitIncrease is the increase per GB of system memory.
|
||||
type BaseLimitIncrease struct {
|
||||
Streams int
|
||||
StreamsInbound int
|
||||
StreamsOutbound int
|
||||
Conns int
|
||||
ConnsInbound int
|
||||
ConnsOutbound int
|
||||
Memory int64
|
||||
FDFraction float64
|
||||
}
|
||||
|
||||
// Apply overwrites all zero-valued limits with the values of l2
|
||||
// Must not use a pointer receiver.
|
||||
func (l *BaseLimitIncrease) Apply(l2 BaseLimitIncrease) {
|
||||
if l.Streams == 0 {
|
||||
l.Streams = l2.Streams
|
||||
}
|
||||
if l.StreamsInbound == 0 {
|
||||
l.StreamsInbound = l2.StreamsInbound
|
||||
}
|
||||
if l.StreamsOutbound == 0 {
|
||||
l.StreamsOutbound = l2.StreamsOutbound
|
||||
}
|
||||
if l.Conns == 0 {
|
||||
l.Conns = l2.Conns
|
||||
}
|
||||
if l.ConnsInbound == 0 {
|
||||
l.ConnsInbound = l2.ConnsInbound
|
||||
}
|
||||
if l.ConnsOutbound == 0 {
|
||||
l.ConnsOutbound = l2.ConnsOutbound
|
||||
}
|
||||
if l.Memory == 0 {
|
||||
l.Memory = l2.Memory
|
||||
}
|
||||
if l.FDFraction == 0 {
|
||||
l.FDFraction = l2.FDFraction
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BaseLimit) GetStreamLimit(dir network.Direction) int {
|
||||
@ -114,74 +184,62 @@ func (l *BaseLimit) GetFDLimit() int {
|
||||
return l.FD
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetSystemLimits() Limit {
|
||||
return l.SystemLimits
|
||||
func (l *BaseLimit) GetMemoryLimit() int64 {
|
||||
return l.Memory
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetTransientLimits() Limit {
|
||||
return l.TransientLimits
|
||||
func (l *fixedLimiter) GetSystemLimits() Limit {
|
||||
return &l.System
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetServiceLimits(svc string) Limit {
|
||||
sl, ok := l.ServiceLimits[svc]
|
||||
func (l *fixedLimiter) GetTransientLimits() Limit {
|
||||
return &l.Transient
|
||||
}
|
||||
|
||||
func (l *fixedLimiter) GetServiceLimits(svc string) Limit {
|
||||
sl, ok := l.Service[svc]
|
||||
if !ok {
|
||||
return l.DefaultServiceLimits
|
||||
return &l.ServiceDefault
|
||||
}
|
||||
return sl
|
||||
return &sl
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetServicePeerLimits(svc string) Limit {
|
||||
pl, ok := l.ServicePeerLimits[svc]
|
||||
func (l *fixedLimiter) GetServicePeerLimits(svc string) Limit {
|
||||
pl, ok := l.ServicePeer[svc]
|
||||
if !ok {
|
||||
return l.DefaultServicePeerLimits
|
||||
return &l.ServicePeerDefault
|
||||
}
|
||||
return pl
|
||||
return &pl
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetProtocolLimits(proto protocol.ID) Limit {
|
||||
pl, ok := l.ProtocolLimits[proto]
|
||||
func (l *fixedLimiter) GetProtocolLimits(proto protocol.ID) Limit {
|
||||
pl, ok := l.Protocol[proto]
|
||||
if !ok {
|
||||
return l.DefaultProtocolLimits
|
||||
return &l.ProtocolDefault
|
||||
}
|
||||
return pl
|
||||
return &pl
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetProtocolPeerLimits(proto protocol.ID) Limit {
|
||||
pl, ok := l.ProtocolPeerLimits[proto]
|
||||
func (l *fixedLimiter) GetProtocolPeerLimits(proto protocol.ID) Limit {
|
||||
pl, ok := l.ProtocolPeer[proto]
|
||||
if !ok {
|
||||
return l.DefaultProtocolPeerLimits
|
||||
return &l.ProtocolPeerDefault
|
||||
}
|
||||
return pl
|
||||
return &pl
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetPeerLimits(p peer.ID) Limit {
|
||||
pl, ok := l.PeerLimits[p]
|
||||
func (l *fixedLimiter) GetPeerLimits(p peer.ID) Limit {
|
||||
pl, ok := l.Peer[p]
|
||||
if !ok {
|
||||
return l.DefaultPeerLimits
|
||||
return &l.PeerDefault
|
||||
}
|
||||
return pl
|
||||
return &pl
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetStreamLimits(p peer.ID) Limit {
|
||||
return l.StreamLimits
|
||||
func (l *fixedLimiter) GetStreamLimits(_ peer.ID) Limit {
|
||||
return &l.Stream
|
||||
}
|
||||
|
||||
func (l *BasicLimiter) GetConnLimits() Limit {
|
||||
return l.ConnLimits
|
||||
}
|
||||
|
||||
func (l *MemoryLimit) GetMemory(memoryCap int64) int64 {
|
||||
return memoryLimit(memoryCap, l.MemoryFraction, l.MinMemory, l.MaxMemory)
|
||||
}
|
||||
|
||||
func memoryLimit(memoryCap int64, memFraction float64, minMemory, maxMemory int64) int64 {
|
||||
memoryCap = int64(float64(memoryCap) * memFraction)
|
||||
switch {
|
||||
case memoryCap < minMemory:
|
||||
return minMemory
|
||||
case memoryCap > maxMemory:
|
||||
return maxMemory
|
||||
default:
|
||||
return memoryCap
|
||||
}
|
||||
func (l *fixedLimiter) GetConnLimits() Limit {
|
||||
return &l.Conn
|
||||
}
|
||||
|
309
limit_config.go
309
limit_config.go
@ -1,309 +0,0 @@
|
||||
package rcmgr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
|
||||
"github.com/pbnjay/memory"
|
||||
)
|
||||
|
||||
type BasicLimitConfig struct {
|
||||
// if true, then a dynamic limit is used
|
||||
Dynamic bool `json:",omitempty"`
|
||||
// either Memory is set for fixed memory limit
|
||||
Memory int64 `json:",omitempty"`
|
||||
// or the following 3 fields for computed memory limits
|
||||
MinMemory int64 `json:",omitempty"`
|
||||
MaxMemory int64 `json:",omitempty"`
|
||||
MemoryFraction float64 `json:",omitempty"`
|
||||
|
||||
StreamsInbound int
|
||||
StreamsOutbound int
|
||||
Streams int
|
||||
|
||||
ConnsInbound int
|
||||
ConnsOutbound int
|
||||
Conns int
|
||||
|
||||
FD int
|
||||
}
|
||||
|
||||
func (cfg *BasicLimitConfig) toLimit(base BaseLimit, mem MemoryLimit) (Limit, error) {
|
||||
if cfg == nil {
|
||||
m := mem.GetMemory(int64(memory.TotalMemory()))
|
||||
return &StaticLimit{
|
||||
Memory: m,
|
||||
BaseLimit: base,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if cfg.Streams > 0 {
|
||||
base.Streams = cfg.Streams
|
||||
}
|
||||
if cfg.StreamsInbound > 0 {
|
||||
base.StreamsInbound = cfg.StreamsInbound
|
||||
}
|
||||
if cfg.StreamsOutbound > 0 {
|
||||
base.StreamsOutbound = cfg.StreamsOutbound
|
||||
}
|
||||
if cfg.Conns > 0 {
|
||||
base.Conns = cfg.Conns
|
||||
}
|
||||
if cfg.ConnsInbound > 0 {
|
||||
base.ConnsInbound = cfg.ConnsInbound
|
||||
}
|
||||
if cfg.ConnsOutbound > 0 {
|
||||
base.ConnsOutbound = cfg.ConnsOutbound
|
||||
}
|
||||
if cfg.FD > 0 {
|
||||
base.FD = cfg.FD
|
||||
}
|
||||
|
||||
switch {
|
||||
case cfg.Memory > 0:
|
||||
return &StaticLimit{
|
||||
Memory: cfg.Memory,
|
||||
BaseLimit: base,
|
||||
}, nil
|
||||
|
||||
case cfg.Dynamic:
|
||||
if cfg.MemoryFraction < 0 {
|
||||
return nil, fmt.Errorf("negative memory fraction: %f", cfg.MemoryFraction)
|
||||
}
|
||||
if cfg.MemoryFraction > 0 {
|
||||
mem.MemoryFraction = cfg.MemoryFraction
|
||||
}
|
||||
if cfg.MinMemory > 0 {
|
||||
mem.MinMemory = cfg.MinMemory
|
||||
}
|
||||
if cfg.MaxMemory > 0 {
|
||||
mem.MaxMemory = cfg.MaxMemory
|
||||
}
|
||||
|
||||
return &DynamicLimit{
|
||||
MemoryLimit: mem,
|
||||
BaseLimit: base,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
if cfg.MemoryFraction < 0 {
|
||||
return nil, fmt.Errorf("negative memory fraction: %f", cfg.MemoryFraction)
|
||||
}
|
||||
if cfg.MemoryFraction > 0 {
|
||||
mem.MemoryFraction = cfg.MemoryFraction
|
||||
}
|
||||
if cfg.MinMemory > 0 {
|
||||
mem.MinMemory = cfg.MinMemory
|
||||
}
|
||||
if cfg.MaxMemory > 0 {
|
||||
mem.MaxMemory = cfg.MaxMemory
|
||||
}
|
||||
|
||||
m := mem.GetMemory(int64(memory.TotalMemory()))
|
||||
return &StaticLimit{
|
||||
Memory: m,
|
||||
BaseLimit: base,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *BasicLimitConfig) toLimitFixed(base BaseLimit, mem int64) (Limit, error) {
|
||||
if cfg == nil {
|
||||
return &StaticLimit{
|
||||
Memory: mem,
|
||||
BaseLimit: base,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if cfg.Streams > 0 {
|
||||
base.Streams = cfg.Streams
|
||||
}
|
||||
if cfg.StreamsInbound > 0 {
|
||||
base.StreamsInbound = cfg.StreamsInbound
|
||||
}
|
||||
if cfg.StreamsOutbound > 0 {
|
||||
base.StreamsOutbound = cfg.StreamsOutbound
|
||||
}
|
||||
if cfg.Conns > 0 {
|
||||
base.Conns = cfg.Conns
|
||||
}
|
||||
if cfg.ConnsInbound > 0 {
|
||||
base.ConnsInbound = cfg.ConnsInbound
|
||||
}
|
||||
if cfg.ConnsOutbound > 0 {
|
||||
base.ConnsOutbound = cfg.ConnsOutbound
|
||||
}
|
||||
if cfg.FD > 0 {
|
||||
base.FD = cfg.FD
|
||||
}
|
||||
|
||||
switch {
|
||||
case cfg.Memory > 0:
|
||||
return &StaticLimit{
|
||||
Memory: cfg.Memory,
|
||||
BaseLimit: base,
|
||||
}, nil
|
||||
|
||||
case cfg.Dynamic:
|
||||
return nil, fmt.Errorf("cannot specify dynamic limit for fixed memory limit")
|
||||
|
||||
default:
|
||||
if cfg.MemoryFraction > 0 || cfg.MinMemory > 0 || cfg.MaxMemory > 0 {
|
||||
return nil, fmt.Errorf("cannot specify dynamic range for fixed memory limit")
|
||||
}
|
||||
return &StaticLimit{
|
||||
Memory: mem,
|
||||
BaseLimit: base,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type BasicLimiterConfig struct {
|
||||
System *BasicLimitConfig `json:",omitempty"`
|
||||
Transient *BasicLimitConfig `json:",omitempty"`
|
||||
|
||||
ServiceDefault *BasicLimitConfig `json:",omitempty"`
|
||||
ServicePeerDefault *BasicLimitConfig `json:",omitempty"`
|
||||
Service map[string]BasicLimitConfig `json:",omitempty"`
|
||||
ServicePeer map[string]BasicLimitConfig `json:",omitempty"`
|
||||
|
||||
ProtocolDefault *BasicLimitConfig `json:",omitempty"`
|
||||
ProtocolPeerDefault *BasicLimitConfig `json:",omitempty"`
|
||||
Protocol map[string]BasicLimitConfig `json:",omitempty"`
|
||||
ProtocolPeer map[string]BasicLimitConfig `json:",omitempty"`
|
||||
|
||||
PeerDefault *BasicLimitConfig `json:",omitempty"`
|
||||
Peer map[string]BasicLimitConfig `json:",omitempty"`
|
||||
|
||||
Conn *BasicLimitConfig `json:",omitempty"`
|
||||
Stream *BasicLimitConfig `json:",omitempty"`
|
||||
}
|
||||
|
||||
// NewDefaultLimiterFromJSON creates a new limiter by parsing a json configuration,
|
||||
// using the default limits for fallback.
|
||||
func NewDefaultLimiterFromJSON(in io.Reader) (*BasicLimiter, error) {
|
||||
return NewLimiterFromJSON(in, DefaultLimits)
|
||||
}
|
||||
|
||||
// NewLimiterFromJSON creates a new limiter by parsing a json configuration.
|
||||
func NewLimiterFromJSON(in io.Reader, defaults DefaultLimitConfig) (*BasicLimiter, error) {
|
||||
jin := json.NewDecoder(in)
|
||||
|
||||
var cfg BasicLimiterConfig
|
||||
|
||||
if err := jin.Decode(&cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewLimiter(cfg, defaults)
|
||||
}
|
||||
|
||||
func NewLimiter(cfg BasicLimiterConfig, defaults DefaultLimitConfig) (*BasicLimiter, error) {
|
||||
limiter := new(BasicLimiter)
|
||||
var err error
|
||||
|
||||
limiter.SystemLimits, err = cfg.System.toLimit(defaults.SystemBaseLimit, defaults.SystemMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid system limit: %w", err)
|
||||
}
|
||||
|
||||
limiter.TransientLimits, err = cfg.Transient.toLimit(defaults.TransientBaseLimit, defaults.TransientMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid transient limit: %w", err)
|
||||
}
|
||||
|
||||
limiter.DefaultServiceLimits, err = cfg.ServiceDefault.toLimit(defaults.ServiceBaseLimit, defaults.ServiceMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid default service limit: %w", err)
|
||||
}
|
||||
|
||||
limiter.DefaultServicePeerLimits, err = cfg.ServicePeerDefault.toLimit(defaults.ServicePeerBaseLimit, defaults.ServicePeerMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid default service peer limit: %w", err)
|
||||
}
|
||||
|
||||
if len(cfg.Service) > 0 {
|
||||
limiter.ServiceLimits = make(map[string]Limit, len(cfg.Service))
|
||||
for svc, cfgLimit := range cfg.Service {
|
||||
limiter.ServiceLimits[svc], err = cfgLimit.toLimit(defaults.ServiceBaseLimit, defaults.ServiceMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid service limit for %s: %w", svc, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(cfg.ServicePeer) > 0 {
|
||||
limiter.ServicePeerLimits = make(map[string]Limit, len(cfg.ServicePeer))
|
||||
for svc, cfgLimit := range cfg.ServicePeer {
|
||||
limiter.ServicePeerLimits[svc], err = cfgLimit.toLimit(defaults.ServicePeerBaseLimit, defaults.ServicePeerMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid service peer limit for %s: %w", svc, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
limiter.DefaultProtocolLimits, err = cfg.ProtocolDefault.toLimit(defaults.ProtocolBaseLimit, defaults.ProtocolMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid default protocol limit: %w", err)
|
||||
}
|
||||
|
||||
limiter.DefaultProtocolPeerLimits, err = cfg.ProtocolPeerDefault.toLimit(defaults.ProtocolPeerBaseLimit, defaults.ProtocolPeerMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid default protocol peer limit: %w", err)
|
||||
}
|
||||
|
||||
if len(cfg.Protocol) > 0 {
|
||||
limiter.ProtocolLimits = make(map[protocol.ID]Limit, len(cfg.Protocol))
|
||||
for p, cfgLimit := range cfg.Protocol {
|
||||
limiter.ProtocolLimits[protocol.ID(p)], err = cfgLimit.toLimit(defaults.ProtocolBaseLimit, defaults.ProtocolMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid service limit for %s: %w", p, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(cfg.ProtocolPeer) > 0 {
|
||||
limiter.ProtocolPeerLimits = make(map[protocol.ID]Limit, len(cfg.ProtocolPeer))
|
||||
for p, cfgLimit := range cfg.ProtocolPeer {
|
||||
limiter.ProtocolPeerLimits[protocol.ID(p)], err = cfgLimit.toLimit(defaults.ProtocolPeerBaseLimit, defaults.ProtocolPeerMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid service peer limit for %s: %w", p, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
limiter.DefaultPeerLimits, err = cfg.PeerDefault.toLimit(defaults.PeerBaseLimit, defaults.PeerMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid peer limit: %w", err)
|
||||
}
|
||||
|
||||
if len(cfg.Peer) > 0 {
|
||||
limiter.PeerLimits = make(map[peer.ID]Limit, len(cfg.Peer))
|
||||
for p, cfgLimit := range cfg.Peer {
|
||||
pid, err := peer.Decode(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid peer ID %s: %w", p, err)
|
||||
}
|
||||
limiter.PeerLimits[pid], err = cfgLimit.toLimit(defaults.PeerBaseLimit, defaults.PeerMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid peer limit for %s: %w", p, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
limiter.ConnLimits, err = cfg.Conn.toLimitFixed(defaults.ConnBaseLimit, defaults.ConnMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid conn limit: %w", err)
|
||||
}
|
||||
|
||||
limiter.StreamLimits, err = cfg.Stream.toLimitFixed(defaults.StreamBaseLimit, defaults.StreamMemory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid stream limit: %w", err)
|
||||
}
|
||||
|
||||
return limiter, nil
|
||||
}
|
@ -4,121 +4,50 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func withMemoryLimit(l BaseLimit, m int64) BaseLimit {
|
||||
l2 := l
|
||||
l2.Memory = m
|
||||
return l2
|
||||
}
|
||||
|
||||
func TestLimitConfigParser(t *testing.T) {
|
||||
in, err := os.Open("limit_config_test.json")
|
||||
require.NoError(t, err)
|
||||
defer in.Close()
|
||||
|
||||
limiter, err := NewDefaultLimiterFromJSON(in)
|
||||
DefaultLimits.AddServiceLimit("C", DefaultLimits.ServiceBaseLimit, BaseLimitIncrease{})
|
||||
DefaultLimits.AddProtocolPeerLimit("C", DefaultLimits.ServiceBaseLimit, BaseLimitIncrease{})
|
||||
defaults := DefaultLimits.AutoScale()
|
||||
cfg, err := readLimiterConfigFromJSON(in, defaults)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t,
|
||||
&DynamicLimit{
|
||||
MemoryLimit: MemoryLimit{
|
||||
MinMemory: 16384,
|
||||
MaxMemory: 65536,
|
||||
MemoryFraction: 0.125,
|
||||
},
|
||||
BaseLimit: BaseLimit{
|
||||
Streams: 64,
|
||||
StreamsInbound: 32,
|
||||
StreamsOutbound: 48,
|
||||
Conns: 16,
|
||||
ConnsInbound: 8,
|
||||
ConnsOutbound: 16,
|
||||
FD: 16,
|
||||
},
|
||||
},
|
||||
limiter.SystemLimits)
|
||||
require.Equal(t, int64(65536), cfg.System.Memory)
|
||||
require.Equal(t, defaults.System.Streams, cfg.System.Streams)
|
||||
require.Equal(t, defaults.System.StreamsInbound, cfg.System.StreamsInbound)
|
||||
require.Equal(t, defaults.System.StreamsOutbound, cfg.System.StreamsOutbound)
|
||||
require.Equal(t, 16, cfg.System.Conns)
|
||||
require.Equal(t, 8, cfg.System.ConnsInbound)
|
||||
require.Equal(t, 16, cfg.System.ConnsOutbound)
|
||||
require.Equal(t, 16, cfg.System.FD)
|
||||
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: DefaultLimits.TransientBaseLimit,
|
||||
},
|
||||
limiter.TransientLimits)
|
||||
require.Equal(t, defaults.Transient, cfg.Transient)
|
||||
require.Equal(t, int64(8765), cfg.ServiceDefault.Memory)
|
||||
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: DefaultLimits.ServiceBaseLimit,
|
||||
},
|
||||
limiter.DefaultServiceLimits)
|
||||
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 2048,
|
||||
BaseLimit: DefaultLimits.ServicePeerBaseLimit,
|
||||
},
|
||||
limiter.DefaultServicePeerLimits)
|
||||
|
||||
require.Equal(t, 1, len(limiter.ServiceLimits))
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: DefaultLimits.ServiceBaseLimit,
|
||||
},
|
||||
limiter.ServiceLimits["A"])
|
||||
|
||||
require.Equal(t, 1, len(limiter.ServicePeerLimits))
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: DefaultLimits.ServicePeerBaseLimit,
|
||||
},
|
||||
limiter.ServicePeerLimits["A"])
|
||||
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 2048,
|
||||
BaseLimit: DefaultLimits.ProtocolBaseLimit,
|
||||
},
|
||||
limiter.DefaultProtocolLimits)
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 1024,
|
||||
BaseLimit: DefaultLimits.ProtocolPeerBaseLimit,
|
||||
},
|
||||
limiter.DefaultProtocolPeerLimits)
|
||||
|
||||
require.Equal(t, 1, len(limiter.ProtocolLimits))
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: DefaultLimits.ProtocolBaseLimit,
|
||||
},
|
||||
limiter.ProtocolLimits["/A"])
|
||||
|
||||
require.Equal(t, 1, len(limiter.ProtocolPeerLimits))
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: DefaultLimits.ProtocolPeerBaseLimit,
|
||||
},
|
||||
limiter.ProtocolPeerLimits["/A"])
|
||||
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: DefaultLimits.PeerBaseLimit,
|
||||
},
|
||||
limiter.DefaultPeerLimits)
|
||||
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 1 << 20,
|
||||
BaseLimit: DefaultLimits.ConnBaseLimit,
|
||||
},
|
||||
limiter.ConnLimits)
|
||||
|
||||
require.Equal(t,
|
||||
&StaticLimit{
|
||||
Memory: 16 << 20,
|
||||
BaseLimit: DefaultLimits.StreamBaseLimit,
|
||||
},
|
||||
limiter.StreamLimits)
|
||||
require.Contains(t, cfg.Service, "A")
|
||||
require.Equal(t, withMemoryLimit(cfg.ServiceDefault, 8192), cfg.Service["A"])
|
||||
require.Contains(t, cfg.Service, "B")
|
||||
require.Equal(t, cfg.ServiceDefault, cfg.Service["B"])
|
||||
require.Contains(t, cfg.Service, "C")
|
||||
require.Equal(t, defaults.Service["C"], cfg.Service["C"])
|
||||
|
||||
require.Equal(t, int64(4096), cfg.PeerDefault.Memory)
|
||||
peerID, err := peer.Decode("12D3KooWPFH2Bx2tPfw6RLxN8k2wh47GRXgkt9yrAHU37zFwHWzS")
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, cfg.Peer, peerID)
|
||||
require.Equal(t, int64(4097), cfg.Peer[peerID].Memory)
|
||||
}
|
||||
|
@ -1,32 +1,22 @@
|
||||
{
|
||||
"System": {
|
||||
"Dynamic": true,
|
||||
"MinMemory": 16384,
|
||||
"MaxMemory": 65536,
|
||||
"MemoryFraction": 0.125,
|
||||
"Streams": 64,
|
||||
"StreamsInbound": 32,
|
||||
"StreamsOutbound": 48,
|
||||
"Memory": 65536,
|
||||
"Conns": 16,
|
||||
"ConnsInbound": 8,
|
||||
"ConnsOutbound": 16,
|
||||
"FD": 16
|
||||
},
|
||||
"Transient": {
|
||||
"MinMemory": 1024,
|
||||
"MaxMemory": 4096,
|
||||
"MemoryFraction": 0.03125
|
||||
},
|
||||
"ServiceDefault": {
|
||||
"Memory": 8192
|
||||
},
|
||||
"ServicePeerDefault": {
|
||||
"Memory": 2048
|
||||
"Memory": 8765
|
||||
},
|
||||
"Service": {
|
||||
"A": {
|
||||
"Memory": 8192
|
||||
}
|
||||
},
|
||||
"B": {}
|
||||
},
|
||||
"ServicePeerDefault": {
|
||||
"Memory": 2048
|
||||
},
|
||||
"ServicePeer": {
|
||||
"A": {
|
||||
@ -44,17 +34,12 @@
|
||||
"Memory": 8192
|
||||
}
|
||||
},
|
||||
"ProtocolPeer": {
|
||||
"/A": {
|
||||
"Memory": 4096
|
||||
}
|
||||
},
|
||||
"PeerDefault": {
|
||||
"Memory": 4096
|
||||
},
|
||||
"Peer": {
|
||||
"12D3KooWPFH2Bx2tPfw6RLxN8k2wh47GRXgkt9yrAHU37zFwHWzS": {
|
||||
"Memory": 4096
|
||||
}
|
||||
"12D3KooWPFH2Bx2tPfw6RLxN8k2wh47GRXgkt9yrAHU37zFwHWzS": {
|
||||
"Memory": 4097
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,148 +1,424 @@
|
||||
package rcmgr
|
||||
|
||||
import "math"
|
||||
import (
|
||||
"math"
|
||||
|
||||
// DefaultLimitConfig is a struct for configuring default limits.
|
||||
type DefaultLimitConfig struct {
|
||||
SystemBaseLimit BaseLimit
|
||||
SystemMemory MemoryLimit
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
|
||||
TransientBaseLimit BaseLimit
|
||||
TransientMemory MemoryLimit
|
||||
"github.com/pbnjay/memory"
|
||||
)
|
||||
|
||||
ServiceBaseLimit BaseLimit
|
||||
ServiceMemory MemoryLimit
|
||||
|
||||
ServicePeerBaseLimit BaseLimit
|
||||
ServicePeerMemory MemoryLimit
|
||||
|
||||
ProtocolBaseLimit BaseLimit
|
||||
ProtocolMemory MemoryLimit
|
||||
|
||||
ProtocolPeerBaseLimit BaseLimit
|
||||
ProtocolPeerMemory MemoryLimit
|
||||
|
||||
PeerBaseLimit BaseLimit
|
||||
PeerMemory MemoryLimit
|
||||
|
||||
ConnBaseLimit BaseLimit
|
||||
ConnMemory int64
|
||||
|
||||
StreamBaseLimit BaseLimit
|
||||
StreamMemory int64
|
||||
type baseLimitConfig struct {
|
||||
BaseLimit BaseLimit
|
||||
BaseLimitIncrease BaseLimitIncrease
|
||||
}
|
||||
|
||||
func (cfg *DefaultLimitConfig) WithSystemMemory(memFraction float64, minMemory, maxMemory int64) DefaultLimitConfig {
|
||||
refactor := memFraction / cfg.SystemMemory.MemoryFraction
|
||||
r := *cfg
|
||||
r.SystemMemory.MemoryFraction = memFraction
|
||||
r.SystemMemory.MinMemory = minMemory
|
||||
r.SystemMemory.MaxMemory = maxMemory
|
||||
r.TransientMemory.MemoryFraction *= refactor
|
||||
r.ServiceMemory.MemoryFraction *= refactor
|
||||
r.ServicePeerMemory.MemoryFraction *= refactor
|
||||
r.ProtocolMemory.MemoryFraction *= refactor
|
||||
r.ProtocolPeerMemory.MemoryFraction *= refactor
|
||||
r.PeerMemory.MemoryFraction *= refactor
|
||||
return r
|
||||
// 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
|
||||
|
||||
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 LimitConfig struct {
|
||||
System BaseLimit `json:",omitempty"`
|
||||
Transient BaseLimit `json:",omitempty"`
|
||||
|
||||
ServiceDefault BaseLimit `json:",omitempty"`
|
||||
Service map[string]BaseLimit `json:",omitempty"`
|
||||
|
||||
ServicePeerDefault BaseLimit `json:",omitempty"`
|
||||
ServicePeer map[string]BaseLimit `json:",omitempty"`
|
||||
|
||||
ProtocolDefault BaseLimit `json:",omitempty"`
|
||||
Protocol map[protocol.ID]BaseLimit `json:",omitempty"`
|
||||
|
||||
ProtocolPeerDefault BaseLimit `json:",omitempty"`
|
||||
ProtocolPeer map[protocol.ID]BaseLimit `json:",omitempty"`
|
||||
|
||||
PeerDefault BaseLimit `json:",omitempty"`
|
||||
Peer map[peer.ID]BaseLimit `json:",omitempty"`
|
||||
|
||||
Conn BaseLimit `json:",omitempty"`
|
||||
Stream BaseLimit `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (cfg *LimitConfig) Apply(c LimitConfig) {
|
||||
cfg.System.Apply(c.System)
|
||||
cfg.Transient.Apply(c.Transient)
|
||||
cfg.ServiceDefault.Apply(c.ServiceDefault)
|
||||
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)
|
||||
|
||||
// TODO: the following could be solved a lot nicer, if only we could use generics
|
||||
for s, l := range cfg.Service {
|
||||
r := cfg.ServiceDefault
|
||||
if l2, ok := c.Service[s]; ok {
|
||||
r = l2
|
||||
}
|
||||
l.Apply(r)
|
||||
cfg.Service[s] = l
|
||||
}
|
||||
if c.Service != nil && cfg.Service == nil {
|
||||
cfg.Service = make(map[string]BaseLimit)
|
||||
}
|
||||
for s, l := range c.Service {
|
||||
if _, ok := cfg.Service[s]; !ok {
|
||||
cfg.Service[s] = l
|
||||
}
|
||||
}
|
||||
|
||||
for s, l := range cfg.ServicePeer {
|
||||
r := cfg.ServicePeerDefault
|
||||
if l2, ok := c.ServicePeer[s]; ok {
|
||||
r = l2
|
||||
}
|
||||
l.Apply(r)
|
||||
cfg.ServicePeer[s] = l
|
||||
}
|
||||
if c.ServicePeer != nil && cfg.ServicePeer == nil {
|
||||
cfg.ServicePeer = make(map[string]BaseLimit)
|
||||
}
|
||||
for s, l := range c.ServicePeer {
|
||||
if _, ok := cfg.ServicePeer[s]; !ok {
|
||||
cfg.ServicePeer[s] = l
|
||||
}
|
||||
}
|
||||
|
||||
for s, l := range cfg.Protocol {
|
||||
r := cfg.ProtocolDefault
|
||||
if l2, ok := c.Protocol[s]; ok {
|
||||
r = l2
|
||||
}
|
||||
l.Apply(r)
|
||||
cfg.Protocol[s] = l
|
||||
}
|
||||
if c.Protocol != nil && cfg.Protocol == nil {
|
||||
cfg.Protocol = make(map[protocol.ID]BaseLimit)
|
||||
}
|
||||
for s, l := range c.Protocol {
|
||||
if _, ok := cfg.Protocol[s]; !ok {
|
||||
cfg.Protocol[s] = l
|
||||
}
|
||||
}
|
||||
|
||||
for s, l := range cfg.ProtocolPeer {
|
||||
r := cfg.ProtocolPeerDefault
|
||||
if l2, ok := c.ProtocolPeer[s]; ok {
|
||||
r = l2
|
||||
}
|
||||
l.Apply(r)
|
||||
cfg.ProtocolPeer[s] = l
|
||||
}
|
||||
if c.ProtocolPeer != nil && cfg.ProtocolPeer == nil {
|
||||
cfg.ProtocolPeer = make(map[protocol.ID]BaseLimit)
|
||||
}
|
||||
for s, l := range c.ProtocolPeer {
|
||||
if _, ok := cfg.ProtocolPeer[s]; !ok {
|
||||
cfg.ProtocolPeer[s] = l
|
||||
}
|
||||
}
|
||||
|
||||
for s, l := range cfg.Peer {
|
||||
r := cfg.PeerDefault
|
||||
if l2, ok := c.Peer[s]; ok {
|
||||
r = l2
|
||||
}
|
||||
l.Apply(r)
|
||||
cfg.Peer[s] = l
|
||||
}
|
||||
if c.Peer != nil && cfg.Peer == nil {
|
||||
cfg.Peer = make(map[peer.ID]BaseLimit)
|
||||
}
|
||||
for s, l := range c.Peer {
|
||||
if _, ok := cfg.Peer[s]; !ok {
|
||||
cfg.Peer[s] = l
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale scales up a limit configuration.
|
||||
// memory is the amount of memory that the stack is allowed to consume,
|
||||
// for a full 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) LimitConfig {
|
||||
var scaleFactor int
|
||||
if memory > 128<<20 {
|
||||
scaleFactor = int((memory - 128<<20) >> 20)
|
||||
}
|
||||
lc := LimitConfig{
|
||||
System: scale(cfg.SystemBaseLimit, cfg.SystemLimitIncrease, scaleFactor, numFD),
|
||||
Transient: scale(cfg.TransientBaseLimit, cfg.TransientLimitIncrease, scaleFactor, numFD),
|
||||
ServiceDefault: scale(cfg.ServiceBaseLimit, cfg.ServiceLimitIncrease, scaleFactor, numFD),
|
||||
ServicePeerDefault: scale(cfg.ServicePeerBaseLimit, cfg.ServicePeerLimitIncrease, scaleFactor, numFD),
|
||||
ProtocolDefault: scale(cfg.ProtocolBaseLimit, cfg.ProtocolLimitIncrease, scaleFactor, numFD),
|
||||
ProtocolPeerDefault: scale(cfg.ProtocolPeerBaseLimit, cfg.ProtocolPeerLimitIncrease, scaleFactor, numFD),
|
||||
PeerDefault: scale(cfg.PeerBaseLimit, cfg.PeerLimitIncrease, scaleFactor, numFD),
|
||||
Conn: scale(cfg.ConnBaseLimit, cfg.ConnLimitIncrease, scaleFactor, numFD),
|
||||
Stream: scale(cfg.StreamBaseLimit, cfg.ConnLimitIncrease, scaleFactor, 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, scaleFactor, 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, scaleFactor, 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, scaleFactor, 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, scaleFactor, 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, scaleFactor, numFD)
|
||||
}
|
||||
}
|
||||
return lc
|
||||
}
|
||||
|
||||
func (cfg *ScalingLimitConfig) AutoScale() LimitConfig {
|
||||
return cfg.Scale(
|
||||
int64(memory.TotalMemory())/8,
|
||||
getNumFDs()/2,
|
||||
)
|
||||
}
|
||||
|
||||
// factor is the number of MBs above the minimum (128 MB)
|
||||
func scale(base BaseLimit, inc BaseLimitIncrease, factor int, numFD int) BaseLimit {
|
||||
l := BaseLimit{
|
||||
StreamsInbound: base.StreamsInbound + (inc.StreamsInbound*factor)>>10,
|
||||
StreamsOutbound: base.StreamsOutbound + (inc.StreamsOutbound*factor)>>10,
|
||||
Streams: base.Streams + (inc.Streams*factor)>>10,
|
||||
ConnsInbound: base.ConnsInbound + (inc.ConnsInbound*factor)>>10,
|
||||
ConnsOutbound: base.ConnsOutbound + (inc.ConnsOutbound*factor)>>10,
|
||||
Conns: base.Conns + (inc.Conns*factor)>>10,
|
||||
Memory: base.Memory + (inc.Memory*int64(factor))>>10,
|
||||
FD: base.FD,
|
||||
}
|
||||
if inc.FDFraction > 0 && numFD > 0 {
|
||||
l.FD = int(inc.FDFraction * float64(numFD))
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// DefaultLimits are the limits used by the default limiter constructors.
|
||||
var DefaultLimits = DefaultLimitConfig{
|
||||
var DefaultLimits = ScalingLimitConfig{
|
||||
SystemBaseLimit: BaseLimit{
|
||||
StreamsInbound: 4096,
|
||||
StreamsOutbound: 16384,
|
||||
Streams: 16384,
|
||||
ConnsInbound: 256,
|
||||
ConnsOutbound: 1024,
|
||||
Conns: 1024,
|
||||
FD: 512,
|
||||
ConnsInbound: 64,
|
||||
ConnsOutbound: 128,
|
||||
Conns: 128,
|
||||
StreamsInbound: 64 * 16,
|
||||
StreamsOutbound: 128 * 16,
|
||||
Streams: 128 * 16,
|
||||
Memory: 128 << 20,
|
||||
FD: 256,
|
||||
},
|
||||
|
||||
SystemMemory: MemoryLimit{
|
||||
MemoryFraction: 0.125,
|
||||
MinMemory: 128 << 20,
|
||||
MaxMemory: 1 << 30,
|
||||
SystemLimitIncrease: BaseLimitIncrease{
|
||||
ConnsInbound: 64,
|
||||
ConnsOutbound: 128,
|
||||
Conns: 128,
|
||||
StreamsInbound: 64 * 16,
|
||||
StreamsOutbound: 128 * 16,
|
||||
Streams: 128 * 16,
|
||||
Memory: 1 << 30,
|
||||
FDFraction: 1,
|
||||
},
|
||||
|
||||
TransientBaseLimit: BaseLimit{
|
||||
StreamsInbound: 128,
|
||||
StreamsOutbound: 512,
|
||||
Streams: 512,
|
||||
ConnsInbound: 32,
|
||||
ConnsOutbound: 128,
|
||||
Conns: 128,
|
||||
FD: 128,
|
||||
ConnsOutbound: 64,
|
||||
Conns: 64,
|
||||
StreamsInbound: 128,
|
||||
StreamsOutbound: 256,
|
||||
Streams: 256,
|
||||
Memory: 32 << 20,
|
||||
FD: 64,
|
||||
},
|
||||
|
||||
TransientMemory: MemoryLimit{
|
||||
MemoryFraction: 1,
|
||||
MinMemory: 64 << 20,
|
||||
MaxMemory: 64 << 20,
|
||||
TransientLimitIncrease: BaseLimitIncrease{
|
||||
ConnsInbound: 16,
|
||||
ConnsOutbound: 32,
|
||||
Conns: 32,
|
||||
StreamsInbound: 128,
|
||||
StreamsOutbound: 256,
|
||||
Streams: 256,
|
||||
Memory: 128 << 20,
|
||||
FDFraction: 0.25,
|
||||
},
|
||||
|
||||
ServiceBaseLimit: BaseLimit{
|
||||
StreamsInbound: 2048,
|
||||
StreamsOutbound: 8192,
|
||||
Streams: 8192,
|
||||
},
|
||||
|
||||
ServiceMemory: MemoryLimit{
|
||||
MemoryFraction: 0.125 / 4,
|
||||
MinMemory: 64 << 20,
|
||||
MaxMemory: 256 << 20,
|
||||
},
|
||||
|
||||
ServicePeerBaseLimit: BaseLimit{
|
||||
StreamsInbound: 256,
|
||||
StreamsOutbound: 512,
|
||||
Streams: 512,
|
||||
},
|
||||
|
||||
ServicePeerMemory: MemoryLimit{
|
||||
MemoryFraction: 0.125 / 16,
|
||||
MinMemory: 16 << 20,
|
||||
MaxMemory: 64 << 20,
|
||||
},
|
||||
|
||||
ProtocolBaseLimit: BaseLimit{
|
||||
StreamsInbound: 1024,
|
||||
StreamsOutbound: 4096,
|
||||
Streams: 4096,
|
||||
Memory: 64 << 20,
|
||||
},
|
||||
|
||||
ProtocolMemory: MemoryLimit{
|
||||
MemoryFraction: 0.125 / 8,
|
||||
MinMemory: 64 << 20,
|
||||
MaxMemory: 128 << 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: 128,
|
||||
StreamsOutbound: 256,
|
||||
Streams: 512,
|
||||
StreamsInbound: 64,
|
||||
StreamsOutbound: 128,
|
||||
Streams: 256,
|
||||
Memory: 16 << 20,
|
||||
},
|
||||
|
||||
ProtocolPeerMemory: MemoryLimit{
|
||||
MemoryFraction: 0.125 / 16,
|
||||
MinMemory: 16 << 20,
|
||||
MaxMemory: 64 << 20,
|
||||
ProtocolPeerLimitIncrease: BaseLimitIncrease{
|
||||
StreamsInbound: 4,
|
||||
StreamsOutbound: 8,
|
||||
Streams: 16,
|
||||
Memory: 4,
|
||||
},
|
||||
|
||||
PeerBaseLimit: BaseLimit{
|
||||
StreamsInbound: 512,
|
||||
StreamsOutbound: 1024,
|
||||
Streams: 1024,
|
||||
ConnsInbound: 8,
|
||||
ConnsOutbound: 16,
|
||||
Conns: 16,
|
||||
FD: 8,
|
||||
ConnsInbound: 4,
|
||||
ConnsOutbound: 8,
|
||||
Conns: 8,
|
||||
StreamsInbound: 256,
|
||||
StreamsOutbound: 512,
|
||||
Streams: 512,
|
||||
Memory: 64 << 20,
|
||||
FD: 4,
|
||||
},
|
||||
|
||||
PeerMemory: MemoryLimit{
|
||||
MemoryFraction: 0.125 / 16,
|
||||
MinMemory: 64 << 20,
|
||||
MaxMemory: 128 << 20,
|
||||
PeerLimitIncrease: BaseLimitIncrease{
|
||||
StreamsInbound: 128,
|
||||
StreamsOutbound: 256,
|
||||
Streams: 256,
|
||||
Memory: 128 << 20,
|
||||
FDFraction: 1.0 / 64,
|
||||
},
|
||||
|
||||
ConnBaseLimit: BaseLimit{
|
||||
@ -150,17 +426,15 @@ var DefaultLimits = DefaultLimitConfig{
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
Memory: 1 << 20,
|
||||
},
|
||||
|
||||
ConnMemory: 1 << 20,
|
||||
|
||||
StreamBaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
Memory: 16 << 20,
|
||||
},
|
||||
|
||||
StreamMemory: 16 << 20,
|
||||
}
|
||||
|
||||
var infiniteBaseLimit = BaseLimit{
|
||||
@ -171,33 +445,19 @@ var infiniteBaseLimit = BaseLimit{
|
||||
ConnsInbound: math.MaxInt,
|
||||
ConnsOutbound: math.MaxInt,
|
||||
FD: math.MaxInt,
|
||||
}
|
||||
|
||||
var infiniteMemoryLimit = MemoryLimit{
|
||||
MemoryFraction: 1,
|
||||
MinMemory: math.MaxInt64,
|
||||
MaxMemory: math.MaxInt64,
|
||||
Memory: math.MaxInt64,
|
||||
}
|
||||
|
||||
// InfiniteLimits are a limiter configuration that uses infinite 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 = DefaultLimitConfig{
|
||||
SystemBaseLimit: infiniteBaseLimit,
|
||||
SystemMemory: infiniteMemoryLimit,
|
||||
TransientBaseLimit: infiniteBaseLimit,
|
||||
TransientMemory: infiniteMemoryLimit,
|
||||
ServiceBaseLimit: infiniteBaseLimit,
|
||||
ServiceMemory: infiniteMemoryLimit,
|
||||
ServicePeerBaseLimit: infiniteBaseLimit,
|
||||
ServicePeerMemory: infiniteMemoryLimit,
|
||||
ProtocolBaseLimit: infiniteBaseLimit,
|
||||
ProtocolMemory: infiniteMemoryLimit,
|
||||
ProtocolPeerBaseLimit: infiniteBaseLimit,
|
||||
ProtocolPeerMemory: infiniteMemoryLimit,
|
||||
PeerBaseLimit: infiniteBaseLimit,
|
||||
PeerMemory: infiniteMemoryLimit,
|
||||
ConnBaseLimit: infiniteBaseLimit,
|
||||
ConnMemory: math.MaxInt64,
|
||||
StreamBaseLimit: infiniteBaseLimit,
|
||||
StreamMemory: math.MaxInt64,
|
||||
var InfiniteLimits = LimitConfig{
|
||||
System: infiniteBaseLimit,
|
||||
Transient: infiniteBaseLimit,
|
||||
ServiceDefault: infiniteBaseLimit,
|
||||
ServicePeerDefault: infiniteBaseLimit,
|
||||
ProtocolDefault: infiniteBaseLimit,
|
||||
ProtocolPeerDefault: infiniteBaseLimit,
|
||||
PeerDefault: infiniteBaseLimit,
|
||||
Conn: infiniteBaseLimit,
|
||||
Stream: infiniteBaseLimit,
|
||||
}
|
||||
|
128
limit_dynamic.go
128
limit_dynamic.go
@ -1,128 +0,0 @@
|
||||
package rcmgr
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/pbnjay/memory"
|
||||
)
|
||||
|
||||
// DynamicLimit is a limit with dynamic memory values, based on available (free) memory
|
||||
type DynamicLimit struct {
|
||||
BaseLimit
|
||||
MemoryLimit
|
||||
}
|
||||
|
||||
var _ Limit = (*DynamicLimit)(nil)
|
||||
|
||||
func (l *DynamicLimit) GetMemoryLimit() int64 {
|
||||
freemem := memory.FreeMemory()
|
||||
|
||||
// account for memory retained by the runtime that is actually free
|
||||
// HeapInuse - HeapAlloc is the memory available in allocator spans
|
||||
// HeapIdle - HeapReleased is memory held by the runtime that could be returned to the OS
|
||||
var memstat runtime.MemStats
|
||||
runtime.ReadMemStats(&memstat)
|
||||
|
||||
freemem += (memstat.HeapInuse - memstat.HeapAlloc) + (memstat.HeapIdle - memstat.HeapReleased)
|
||||
return l.MemoryLimit.GetMemory(int64(freemem))
|
||||
}
|
||||
|
||||
func (l *DynamicLimit) WithMemoryLimit(memFraction float64, minMemory, maxMemory int64) Limit {
|
||||
r := new(DynamicLimit)
|
||||
*r = *l
|
||||
|
||||
r.MemoryLimit.MemoryFraction *= memFraction
|
||||
r.MemoryLimit.MinMemory = minMemory
|
||||
r.MemoryLimit.MaxMemory = maxMemory
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *DynamicLimit) WithStreamLimit(numStreamsIn, numStreamsOut, numStreams int) Limit {
|
||||
r := new(DynamicLimit)
|
||||
*r = *l
|
||||
|
||||
r.BaseLimit.StreamsInbound = numStreamsIn
|
||||
r.BaseLimit.StreamsOutbound = numStreamsOut
|
||||
r.BaseLimit.Streams = numStreams
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *DynamicLimit) WithConnLimit(numConnsIn, numConnsOut, numConns int) Limit {
|
||||
r := new(DynamicLimit)
|
||||
*r = *l
|
||||
|
||||
r.BaseLimit.ConnsInbound = numConnsIn
|
||||
r.BaseLimit.ConnsOutbound = numConnsOut
|
||||
r.BaseLimit.Conns = numConns
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *DynamicLimit) WithFDLimit(numFD int) Limit {
|
||||
r := new(DynamicLimit)
|
||||
*r = *l
|
||||
|
||||
r.BaseLimit.FD = numFD
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// NewDefaultDynamicLimiter creates a limiter with default limits and a memory cap
|
||||
// dynamically computed based on available memory.
|
||||
func NewDefaultDynamicLimiter(memFraction float64, minMemory, maxMemory int64) *BasicLimiter {
|
||||
return NewDynamicLimiter(DefaultLimits.WithSystemMemory(memFraction, minMemory, maxMemory))
|
||||
}
|
||||
|
||||
// NewDynamicLimiter crates a dynamic limiter with the specified defaults
|
||||
func NewDynamicLimiter(cfg DefaultLimitConfig) *BasicLimiter {
|
||||
system := &DynamicLimit{
|
||||
MemoryLimit: cfg.SystemMemory,
|
||||
BaseLimit: cfg.SystemBaseLimit,
|
||||
}
|
||||
transient := &DynamicLimit{
|
||||
MemoryLimit: cfg.TransientMemory,
|
||||
BaseLimit: cfg.TransientBaseLimit,
|
||||
}
|
||||
svc := &DynamicLimit{
|
||||
MemoryLimit: cfg.ServiceMemory,
|
||||
BaseLimit: cfg.ServiceBaseLimit,
|
||||
}
|
||||
svcPeer := &DynamicLimit{
|
||||
MemoryLimit: cfg.ServicePeerMemory,
|
||||
BaseLimit: cfg.ServicePeerBaseLimit,
|
||||
}
|
||||
proto := &DynamicLimit{
|
||||
MemoryLimit: cfg.ProtocolMemory,
|
||||
BaseLimit: cfg.ProtocolBaseLimit,
|
||||
}
|
||||
protoPeer := &DynamicLimit{
|
||||
MemoryLimit: cfg.ProtocolPeerMemory,
|
||||
BaseLimit: cfg.ProtocolPeerBaseLimit,
|
||||
}
|
||||
peer := &DynamicLimit{
|
||||
MemoryLimit: cfg.PeerMemory,
|
||||
BaseLimit: cfg.PeerBaseLimit,
|
||||
}
|
||||
conn := &StaticLimit{
|
||||
Memory: cfg.ConnMemory,
|
||||
BaseLimit: cfg.ConnBaseLimit,
|
||||
}
|
||||
stream := &StaticLimit{
|
||||
Memory: cfg.StreamMemory,
|
||||
BaseLimit: cfg.StreamBaseLimit,
|
||||
}
|
||||
|
||||
return &BasicLimiter{
|
||||
SystemLimits: system,
|
||||
TransientLimits: transient,
|
||||
DefaultServiceLimits: svc,
|
||||
DefaultServicePeerLimits: svcPeer,
|
||||
DefaultProtocolLimits: proto,
|
||||
DefaultProtocolPeerLimits: protoPeer,
|
||||
DefaultPeerLimits: peer,
|
||||
ConnLimits: conn,
|
||||
StreamLimits: stream,
|
||||
}
|
||||
}
|
138
limit_static.go
138
limit_static.go
@ -1,138 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
func (l *StaticLimit) WithMemoryLimit(memFraction float64, minMemory, maxMemory int64) Limit {
|
||||
r := new(StaticLimit)
|
||||
*r = *l
|
||||
|
||||
r.Memory = int64(memFraction * float64(r.Memory))
|
||||
if r.Memory < minMemory {
|
||||
r.Memory = minMemory
|
||||
} else if r.Memory > maxMemory {
|
||||
r.Memory = maxMemory
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *StaticLimit) WithStreamLimit(numStreamsIn, numStreamsOut, numStreams int) Limit {
|
||||
r := new(StaticLimit)
|
||||
*r = *l
|
||||
|
||||
r.BaseLimit.StreamsInbound = numStreamsIn
|
||||
r.BaseLimit.StreamsOutbound = numStreamsOut
|
||||
r.BaseLimit.Streams = numStreams
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *StaticLimit) WithConnLimit(numConnsIn, numConnsOut, numConns int) Limit {
|
||||
r := new(StaticLimit)
|
||||
*r = *l
|
||||
|
||||
r.BaseLimit.ConnsInbound = numConnsIn
|
||||
r.BaseLimit.ConnsOutbound = numConnsOut
|
||||
r.BaseLimit.Conns = numConns
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *StaticLimit) WithFDLimit(numFD int) Limit {
|
||||
r := new(StaticLimit)
|
||||
*r = *l
|
||||
|
||||
r.BaseLimit.FD = numFD
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// NewDefaultStaticLimiter creates a static 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 NewDefaultStaticLimiter(memFraction float64, minMemory, maxMemory int64) *BasicLimiter {
|
||||
return NewStaticLimiter(DefaultLimits.WithSystemMemory(memFraction, minMemory, maxMemory))
|
||||
}
|
||||
|
||||
// NewDefaultFixedLimiter creates a static limiter with default base limits and a specified system
|
||||
// memory cap.
|
||||
func NewDefaultFixedLimiter(memoryCap int64) *BasicLimiter {
|
||||
return NewStaticLimiter(DefaultLimits.WithSystemMemory(1, memoryCap, memoryCap))
|
||||
}
|
||||
|
||||
// NewDefaultLimiter creates a static limiter with the default limits
|
||||
func NewDefaultLimiter() *BasicLimiter {
|
||||
return NewStaticLimiter(DefaultLimits)
|
||||
}
|
||||
|
||||
// NewStaticLimiter creates a static limiter using the specified system memory cap and default
|
||||
// limit config.
|
||||
func NewStaticLimiter(cfg DefaultLimitConfig) *BasicLimiter {
|
||||
memoryCap := memoryLimit(
|
||||
int64(memory.TotalMemory()),
|
||||
cfg.SystemMemory.MemoryFraction,
|
||||
cfg.SystemMemory.MinMemory,
|
||||
cfg.SystemMemory.MaxMemory)
|
||||
system := &StaticLimit{
|
||||
Memory: memoryCap,
|
||||
BaseLimit: cfg.SystemBaseLimit,
|
||||
}
|
||||
transient := &StaticLimit{
|
||||
Memory: cfg.TransientMemory.GetMemory(memoryCap),
|
||||
BaseLimit: cfg.TransientBaseLimit,
|
||||
}
|
||||
svc := &StaticLimit{
|
||||
Memory: cfg.ServiceMemory.GetMemory(memoryCap),
|
||||
BaseLimit: cfg.ServiceBaseLimit,
|
||||
}
|
||||
svcPeer := &StaticLimit{
|
||||
Memory: cfg.ServicePeerMemory.GetMemory(memoryCap),
|
||||
BaseLimit: cfg.ServicePeerBaseLimit,
|
||||
}
|
||||
proto := &StaticLimit{
|
||||
Memory: cfg.ProtocolMemory.GetMemory(memoryCap),
|
||||
BaseLimit: cfg.ProtocolBaseLimit,
|
||||
}
|
||||
protoPeer := &StaticLimit{
|
||||
Memory: cfg.ProtocolPeerMemory.GetMemory(memoryCap),
|
||||
BaseLimit: cfg.ProtocolPeerBaseLimit,
|
||||
}
|
||||
peer := &StaticLimit{
|
||||
Memory: cfg.PeerMemory.GetMemory(memoryCap),
|
||||
BaseLimit: cfg.PeerBaseLimit,
|
||||
}
|
||||
conn := &StaticLimit{
|
||||
Memory: cfg.ConnMemory,
|
||||
BaseLimit: cfg.ConnBaseLimit,
|
||||
}
|
||||
stream := &StaticLimit{
|
||||
Memory: cfg.StreamMemory,
|
||||
BaseLimit: cfg.StreamBaseLimit,
|
||||
}
|
||||
|
||||
return &BasicLimiter{
|
||||
SystemLimits: system,
|
||||
TransientLimits: transient,
|
||||
DefaultServiceLimits: svc,
|
||||
DefaultServicePeerLimits: svcPeer,
|
||||
DefaultProtocolLimits: proto,
|
||||
DefaultProtocolPeerLimits: protoPeer,
|
||||
DefaultPeerLimits: peer,
|
||||
ConnLimits: conn,
|
||||
StreamLimits: stream,
|
||||
}
|
||||
}
|
88
limit_test.go
Normal file
88
limit_test.go
Normal file
@ -0,0 +1,88 @@
|
||||
package rcmgr
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFileDescriptorCounting(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("can't read file descriptors on Windows")
|
||||
}
|
||||
n := getNumFDs()
|
||||
require.NotZero(t, n)
|
||||
require.Less(t, n, int(1e6))
|
||||
}
|
||||
|
||||
func TestScaling(t *testing.T) {
|
||||
base := BaseLimit{
|
||||
Streams: 100,
|
||||
StreamsInbound: 200,
|
||||
StreamsOutbound: 400,
|
||||
Conns: 10,
|
||||
ConnsInbound: 20,
|
||||
ConnsOutbound: 40,
|
||||
FD: 1,
|
||||
Memory: 1 << 20,
|
||||
}
|
||||
|
||||
t.Run("no scaling if no increase is defined", func(t *testing.T) {
|
||||
cfg := ScalingLimitConfig{ServiceBaseLimit: base}
|
||||
scaled := cfg.Scale(8<<30, 100)
|
||||
require.Equal(t, base, scaled.ServiceDefault)
|
||||
})
|
||||
|
||||
t.Run("scaling", func(t *testing.T) {
|
||||
cfg := ScalingLimitConfig{
|
||||
TransientBaseLimit: base,
|
||||
TransientLimitIncrease: BaseLimitIncrease{
|
||||
Streams: 1,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 3,
|
||||
Conns: 4,
|
||||
ConnsInbound: 5,
|
||||
ConnsOutbound: 6,
|
||||
Memory: 7,
|
||||
FDFraction: 0.5,
|
||||
},
|
||||
}
|
||||
scaled := cfg.Scale(128<<20+4<<30, 1000)
|
||||
require.Equal(t, 500, scaled.Transient.FD)
|
||||
require.Equal(t, base.Streams+4, scaled.Transient.Streams)
|
||||
require.Equal(t, base.StreamsInbound+4*2, scaled.Transient.StreamsInbound)
|
||||
require.Equal(t, base.StreamsOutbound+4*3, scaled.Transient.StreamsOutbound)
|
||||
require.Equal(t, base.Conns+4*4, scaled.Transient.Conns)
|
||||
require.Equal(t, base.ConnsInbound+4*5, scaled.Transient.ConnsInbound)
|
||||
require.Equal(t, base.ConnsOutbound+4*6, scaled.Transient.ConnsOutbound)
|
||||
require.Equal(t, base.Memory+4*7, scaled.Transient.Memory)
|
||||
})
|
||||
|
||||
t.Run("scaling limits in maps", func(t *testing.T) {
|
||||
cfg := ScalingLimitConfig{
|
||||
ServiceLimits: map[string]baseLimitConfig{
|
||||
"A": {
|
||||
BaseLimit: BaseLimit{Streams: 10, Memory: 100, FD: 9},
|
||||
},
|
||||
"B": {
|
||||
BaseLimit: BaseLimit{Streams: 20, Memory: 200, FD: 10},
|
||||
BaseLimitIncrease: BaseLimitIncrease{Streams: 2, Memory: 3, FDFraction: 0.4},
|
||||
},
|
||||
},
|
||||
}
|
||||
scaled := cfg.Scale(128<<20+4<<30, 1000)
|
||||
|
||||
require.Len(t, scaled.Service, 2)
|
||||
require.Contains(t, scaled.Service, "A")
|
||||
require.Equal(t, 10, scaled.Service["A"].Streams)
|
||||
require.Equal(t, int64(100), scaled.Service["A"].Memory)
|
||||
require.Equal(t, 9, scaled.Service["A"].FD)
|
||||
|
||||
require.Contains(t, scaled.Service, "B")
|
||||
require.Equal(t, 20+4*2, scaled.Service["B"].Streams)
|
||||
require.Equal(t, int64(200+4*3), scaled.Service["B"].Memory)
|
||||
require.Equal(t, 400, scaled.Service["B"].FD)
|
||||
|
||||
})
|
||||
}
|
294
rcmgr_test.go
294
rcmgr_test.go
@ -20,167 +20,138 @@ func TestResourceManager(t *testing.T) {
|
||||
svcA := "A.svc"
|
||||
svcB := "B.svc"
|
||||
nmgr, err := NewResourceManager(
|
||||
&BasicLimiter{
|
||||
SystemLimits: &StaticLimit{
|
||||
Memory: 16384,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 3,
|
||||
StreamsOutbound: 3,
|
||||
Streams: 6,
|
||||
ConnsInbound: 3,
|
||||
ConnsOutbound: 3,
|
||||
Conns: 6,
|
||||
FD: 2,
|
||||
},
|
||||
NewFixedLimiter(LimitConfig{
|
||||
System: BaseLimit{
|
||||
Memory: 16384,
|
||||
StreamsInbound: 3,
|
||||
StreamsOutbound: 3,
|
||||
Streams: 6,
|
||||
ConnsInbound: 3,
|
||||
ConnsOutbound: 3,
|
||||
Conns: 6,
|
||||
FD: 2,
|
||||
},
|
||||
TransientLimits: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 2,
|
||||
Transient: BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 2,
|
||||
FD: 1,
|
||||
},
|
||||
ServiceDefault: BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 2,
|
||||
FD: 1,
|
||||
},
|
||||
ServicePeerDefault: BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 5,
|
||||
StreamsOutbound: 5,
|
||||
Streams: 10,
|
||||
},
|
||||
Service: map[string]BaseLimit{
|
||||
svcA: {
|
||||
Memory: 8192,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 4,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 4,
|
||||
FD: 1,
|
||||
},
|
||||
svcB: {
|
||||
Memory: 8192,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 4,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 4,
|
||||
FD: 1,
|
||||
},
|
||||
},
|
||||
DefaultServiceLimits: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
ServicePeer: map[string]BaseLimit{
|
||||
svcB: {
|
||||
Memory: 8192,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 2,
|
||||
},
|
||||
},
|
||||
ProtocolDefault: BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
},
|
||||
Protocol: map[protocol.ID]BaseLimit{
|
||||
protoA: {
|
||||
Memory: 8192,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
},
|
||||
},
|
||||
ProtocolPeer: map[protocol.ID]BaseLimit{
|
||||
protoB: {
|
||||
Memory: 8192,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
},
|
||||
},
|
||||
PeerDefault: BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 2,
|
||||
FD: 1,
|
||||
},
|
||||
ProtocolPeerDefault: BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 5,
|
||||
StreamsOutbound: 5,
|
||||
Streams: 10,
|
||||
},
|
||||
Peer: map[peer.ID]BaseLimit{
|
||||
peerA: {
|
||||
Memory: 8192,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 4,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 4,
|
||||
FD: 1,
|
||||
},
|
||||
},
|
||||
DefaultServicePeerLimits: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 5,
|
||||
StreamsOutbound: 5,
|
||||
Streams: 10,
|
||||
},
|
||||
Conn: BaseLimit{
|
||||
Memory: 4096,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
ServiceLimits: map[string]Limit{
|
||||
svcA: &StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 4,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 4,
|
||||
FD: 1,
|
||||
},
|
||||
},
|
||||
svcB: &StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 4,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 4,
|
||||
FD: 1,
|
||||
},
|
||||
},
|
||||
Stream: BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
},
|
||||
ServicePeerLimits: map[string]Limit{
|
||||
svcB: &StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultProtocolLimits: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
},
|
||||
},
|
||||
ProtocolLimits: map[protocol.ID]Limit{
|
||||
protoA: &StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
ProtocolPeerLimits: map[protocol.ID]Limit{
|
||||
protoB: &StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultPeerLimits: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 2,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 2,
|
||||
FD: 1,
|
||||
},
|
||||
},
|
||||
DefaultProtocolPeerLimits: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 5,
|
||||
StreamsOutbound: 5,
|
||||
Streams: 10,
|
||||
},
|
||||
},
|
||||
PeerLimits: map[peer.ID]Limit{
|
||||
peerA: &StaticLimit{
|
||||
Memory: 8192,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 4,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 4,
|
||||
FD: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
ConnLimits: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
},
|
||||
StreamLimits: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -1006,10 +977,14 @@ func TestResourceManager(t *testing.T) {
|
||||
func TestResourceManagerWithAllowlist(t *testing.T) {
|
||||
peerA := test.RandPeerIDFatal(t)
|
||||
|
||||
limits := NewDefaultLimiter()
|
||||
limits.SystemLimits = limits.SystemLimits.WithConnLimit(0, 0, 0)
|
||||
limits.TransientLimits = limits.SystemLimits.WithConnLimit(0, 0, 0)
|
||||
rcmgr, err := NewResourceManager(limits, WithAllowlistedMultiaddrs([]multiaddr.Multiaddr{
|
||||
limits := DefaultLimits.Scale(1<<30, 100)
|
||||
limits.System.Conns = 0
|
||||
limits.System.ConnsInbound = 0
|
||||
limits.System.ConnsOutbound = 0
|
||||
limits.Transient.Conns = 0
|
||||
limits.Transient.ConnsInbound = 0
|
||||
limits.Transient.ConnsOutbound = 0
|
||||
rcmgr, err := NewResourceManager(NewFixedLimiter(limits), WithAllowlistedMultiaddrs([]multiaddr.Multiaddr{
|
||||
multiaddr.StringCast("/ip4/1.2.3.4"),
|
||||
multiaddr.StringCast("/ip4/4.3.2.1/p2p/" + peerA.String()),
|
||||
}))
|
||||
@ -1021,9 +996,18 @@ func TestResourceManagerWithAllowlist(t *testing.T) {
|
||||
// Setup allowlist. TODO, replace this with a config once config changes are in
|
||||
r := rcmgr.(*resourceManager)
|
||||
|
||||
r.allowlistedSystem = newSystemScope(limits.GetSystemLimits().WithConnLimit(2, 1, 2), r, "allowlistedSystem")
|
||||
sysLimit := limits.System
|
||||
sysLimit.Conns = 2
|
||||
sysLimit.ConnsInbound = 2
|
||||
sysLimit.ConnsOutbound = 1
|
||||
r.allowlistedSystem = newSystemScope(&sysLimit, r, "allowlistedSystem")
|
||||
r.allowlistedSystem.IncRef()
|
||||
r.allowlistedTransient = newTransientScope(limits.GetTransientLimits().WithConnLimit(1, 1, 1), r, "allowlistedTransient", r.allowlistedSystem.resourceScope)
|
||||
|
||||
transLimit := limits.Transient
|
||||
transLimit.Conns = 1
|
||||
transLimit.ConnsInbound = 1
|
||||
transLimit.ConnsOutbound = 1
|
||||
r.allowlistedTransient = newTransientScope(&transLimit, r, "allowlistedTransient", r.allowlistedSystem.resourceScope)
|
||||
r.allowlistedTransient.IncRef()
|
||||
}
|
||||
|
||||
|
244
scope_test.go
244
scope_test.go
@ -30,17 +30,15 @@ func checkResources(t *testing.T, rc *resources, st network.ScopeStat) {
|
||||
}
|
||||
|
||||
func TestResources(t *testing.T) {
|
||||
rc := resources{limit: &StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
rc := resources{limit: &BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
}}
|
||||
|
||||
checkResources(t, &rc, network.ScopeStat{})
|
||||
@ -245,17 +243,15 @@ func TestResources(t *testing.T) {
|
||||
|
||||
func TestResourceScopeSimple(t *testing.T) {
|
||||
s := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
nil, "test", nil, nil,
|
||||
)
|
||||
@ -379,17 +375,15 @@ func testResourceScopeBasic(t *testing.T, s *resourceScope) {
|
||||
|
||||
func TestResourceScopeTxnBasic(t *testing.T) {
|
||||
s := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
nil, "test", nil, nil,
|
||||
)
|
||||
@ -416,17 +410,15 @@ func TestResourceScopeTxnBasic(t *testing.T) {
|
||||
|
||||
func TestResourceScopeTxnZombie(t *testing.T) {
|
||||
s := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
nil, "test", nil, nil,
|
||||
)
|
||||
@ -460,17 +452,15 @@ func TestResourceScopeTxnZombie(t *testing.T) {
|
||||
|
||||
func TestResourceScopeTxnTree(t *testing.T) {
|
||||
s := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 1,
|
||||
StreamsOutbound: 1,
|
||||
Streams: 1,
|
||||
ConnsInbound: 1,
|
||||
ConnsOutbound: 1,
|
||||
Conns: 1,
|
||||
FD: 1,
|
||||
},
|
||||
nil, "test", nil, nil,
|
||||
)
|
||||
@ -571,92 +561,80 @@ func TestResourceScopeDAG(t *testing.T) {
|
||||
// \
|
||||
// ------> s6
|
||||
s1 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 4,
|
||||
StreamsOutbound: 4,
|
||||
Streams: 4,
|
||||
ConnsInbound: 4,
|
||||
ConnsOutbound: 4,
|
||||
Conns: 4,
|
||||
FD: 4,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 4096,
|
||||
StreamsInbound: 4,
|
||||
StreamsOutbound: 4,
|
||||
Streams: 4,
|
||||
ConnsInbound: 4,
|
||||
ConnsOutbound: 4,
|
||||
Conns: 4,
|
||||
FD: 4,
|
||||
},
|
||||
nil, "test", nil, nil,
|
||||
)
|
||||
s2 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 2048,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 2048,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
[]*resourceScope{s1}, "test", nil, nil,
|
||||
)
|
||||
s3 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 2048,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 2048,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
[]*resourceScope{s1}, "test", nil, nil,
|
||||
)
|
||||
s4 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 2048,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 2048,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
[]*resourceScope{s2, s3, s1}, "test", nil, nil,
|
||||
)
|
||||
s5 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 2048,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 2048,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
[]*resourceScope{s2, s1}, "test", nil, nil,
|
||||
)
|
||||
s6 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 2048,
|
||||
BaseLimit: BaseLimit{
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
&BaseLimit{
|
||||
Memory: 2048,
|
||||
StreamsInbound: 2,
|
||||
StreamsOutbound: 2,
|
||||
Streams: 2,
|
||||
ConnsInbound: 2,
|
||||
ConnsOutbound: 2,
|
||||
Conns: 2,
|
||||
FD: 2,
|
||||
},
|
||||
[]*resourceScope{s3, s1}, "test", nil, nil,
|
||||
)
|
||||
@ -1095,39 +1073,27 @@ func TestResourceScopeDAGTxn(t *testing.T) {
|
||||
// \
|
||||
// ------> s6
|
||||
s1 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 8192,
|
||||
},
|
||||
&BaseLimit{Memory: 8192},
|
||||
nil, "test", nil, nil,
|
||||
)
|
||||
s2 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096 + 2048,
|
||||
},
|
||||
&BaseLimit{Memory: 4096 + 2048},
|
||||
[]*resourceScope{s1}, "test", nil, nil,
|
||||
)
|
||||
s3 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096 + 2048,
|
||||
},
|
||||
&BaseLimit{Memory: 4096 + 2048},
|
||||
[]*resourceScope{s1}, "test", nil, nil,
|
||||
)
|
||||
s4 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096 + 1024,
|
||||
},
|
||||
&BaseLimit{Memory: 4096 + 1024},
|
||||
[]*resourceScope{s2, s3, s1}, "test", nil, nil,
|
||||
)
|
||||
s5 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096 + 1024,
|
||||
},
|
||||
&BaseLimit{Memory: 4096 + 1024},
|
||||
[]*resourceScope{s2, s1}, "test", nil, nil,
|
||||
)
|
||||
s6 := newResourceScope(
|
||||
&StaticLimit{
|
||||
Memory: 4096 + 1024,
|
||||
},
|
||||
&BaseLimit{Memory: 4096 + 1024},
|
||||
[]*resourceScope{s3, s1}, "test", nil, nil,
|
||||
)
|
||||
|
||||
|
11
sys_not_unix.go
Normal file
11
sys_not_unix.go
Normal file
@ -0,0 +1,11 @@
|
||||
//go:build !linux && !darwin
|
||||
|
||||
package rcmgr
|
||||
|
||||
import "runtime"
|
||||
|
||||
// TODO: figure out how to get the number of file descriptors on Windows and other systems
|
||||
func getNumFDs() int {
|
||||
log.Warnf("cannot determine number of file descriptors on %s", runtime.GOOS)
|
||||
return 0
|
||||
}
|
17
sys_unix.go
Normal file
17
sys_unix.go
Normal file
@ -0,0 +1,17 @@
|
||||
//go:build linux || darwin
|
||||
// +build linux darwin
|
||||
|
||||
package rcmgr
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func getNumFDs() int {
|
||||
var l unix.Rlimit
|
||||
if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &l); err != nil {
|
||||
log.Errorw("failed to get fd limit", "error", err)
|
||||
return 0
|
||||
}
|
||||
return int(l.Cur)
|
||||
}
|
Loading…
Reference in New Issue
Block a user