Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: persist limits to Swarm.ResourceMgr.Limits #8901

Merged
merged 3 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 4 additions & 42 deletions config/swarm.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package config

import rcmgr "github.com/libp2p/go-libp2p-resource-manager"

type SwarmConfig struct {
// AddrFilters specifies a set libp2p addresses that we should never
// dial or receive connections from.
Expand Down Expand Up @@ -137,10 +139,8 @@ type ConnMgr struct {
// <https://github.com/libp2p/go-libp2p-resource-manager#readme>
type ResourceMgr struct {
// Enables the Network Resource Manager feature
Enabled Flag `json:",omitempty"`

/* TODO: decide if and how we want to expose limits in our config
Limits *ResourceMgrScopeConfig `json:",omitempty"` */
Enabled Flag `json:",omitempty"`
Limits *rcmgr.BasicLimiterConfig `json:",omitempty"`
lidel marked this conversation as resolved.
Show resolved Hide resolved
}

const (
Expand All @@ -150,41 +150,3 @@ const (
ResourceMgrProtocolScopePrefix = "proto:"
ResourceMgrPeerScopePrefix = "peer:"
)

/* TODO: decide if and how we want to expose limits in our config
type ResourceMgrLimitsConfig struct {
System *ResourceMgrScopeConfig `json:",omitempty"`
Transient *ResourceMgrScopeConfig `json:",omitempty"`

ServiceDefault *ResourceMgrScopeConfig `json:",omitempty"`
ServicePeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
Service map[string]ResourceMgrScopeConfig `json:",omitempty"`
ServicePeer map[string]ResourceMgrScopeConfig `json:",omitempty"`

ProtocolDefault *ResourceMgrScopeConfig `json:",omitempty"`
ProtocolPeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
Protocol map[string]ResourceMgrScopeConfig `json:",omitempty"`
ProtocolPeer map[string]ResourceMgrScopeConfig `json:",omitempty"`

PeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
Peer map[string]ResourceMgrScopeConfig `json:",omitempty"`

Conn *ResourceMgrScopeConfig `json:",omitempty"`
Stream *ResourceMgrScopeConfig `json:",omitempty"`
}
*/

// libp2p Network Resource Manager config for a scope
type ResourceMgrScopeConfig struct {
Dynamic bool `json:",omitempty"`
// set if Dynamic is false
Memory int64 `json:",omitempty"`
// set if Dynamic is true
MemoryFraction float64 `json:",omitempty"`
MinMemory int64 `json:",omitempty"`
MaxMemory int64 `json:",omitempty"`

Streams, StreamsInbound, StreamsOutbound int
Conns, ConnsInbound, ConnsOutbound int
FD int
}
8 changes: 4 additions & 4 deletions core/commands/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
cmds "github.com/ipfs/go-ipfs-cmds"
inet "github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
rcmgr "github.com/libp2p/go-libp2p-resource-manager"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
mamask "github.com/whyrusleeping/multiaddr-filter"
Expand Down Expand Up @@ -380,8 +381,7 @@ It is possible to use this command to inspect and tweak limits at runtime:
$ vi limit.json
$ ipfs swarm limit system limit.json

Changes made via command line are discarded on node shutdown.
For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.
Changes made via command line are persisted in the Swarm.ResourceMgr.Limits field of the $IPFS_PATH/config file.
`},
Arguments: []cmds.Argument{
cmds.StringArg("scope", true, false, "scope of the limit"),
Expand All @@ -401,7 +401,7 @@ For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.

// set scope limit to new values (when limit.json is passed as a second arg)
if req.Files != nil {
var newLimit config.ResourceMgrScopeConfig
var newLimit rcmgr.BasicLimitConfig
it := req.Files.Entries()
if it.Next() {
file := files.FileFromEntry(it)
Expand All @@ -411,7 +411,7 @@ For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.
if err := json.NewDecoder(file).Decode(&newLimit); err != nil {
return errors.New("failed to decode JSON as ResourceMgrScopeConfig")
}
return libp2p.NetSetLimit(node.ResourceManager, scope, newLimit)
return libp2p.NetSetLimit(node.ResourceManager, node.Repo, scope, newLimit)
}
if err := it.Err(); err != nil {
return fmt.Errorf("error opening limit JSON file: %w", err)
Expand Down
106 changes: 69 additions & 37 deletions core/node/libp2p/rcmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package libp2p

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
Expand All @@ -27,7 +26,6 @@ var NoResourceMgrError = fmt.Errorf("missing ResourceMgr: make sure the daemon i

func ResourceManager(cfg config.SwarmConfig) func(fx.Lifecycle, repo.Repo) (network.ResourceManager, Libp2pOpts, error) {
return func(lc fx.Lifecycle, repo repo.Repo) (network.ResourceManager, Libp2pOpts, error) {
var limiter *rcmgr.BasicLimiter
var manager network.ResourceManager
var opts Libp2pOpts

Expand All @@ -47,25 +45,18 @@ func ResourceManager(cfg config.SwarmConfig) func(fx.Lifecycle, repo.Repo) (netw

repoPath, err := config.PathRoot()
if err != nil {
return nil, opts, fmt.Errorf("error opening IPFS_PATH: %w", err)
return nil, opts, fmt.Errorf("opening IPFS_PATH: %w", err)
}

// Create limiter:
// - parse $IPFS_PATH/limits.json if exists
// - use defaultLimits from rcmgr_defaults.go
defaultLimits := adjustedDefaultLimits(cfg)
limitFilePath := filepath.Join(repoPath, NetLimitDefaultFilename)
limitFile, err := os.Open(limitFilePath)
switch {
case err == nil:
defer limitFile.Close()
limiter, err = rcmgr.NewLimiterFromJSON(limitFile, defaultLimits)
if err != nil {
return nil, opts, fmt.Errorf("error parsing libp2p limit file: %w", err)
}
case errors.Is(err, os.ErrNotExist):
limiter = rcmgr.NewStaticLimiter(defaultLimits)
default:

var limits rcmgr.BasicLimiterConfig
if cfg.ResourceMgr.Limits != nil {
limits = *cfg.ResourceMgr.Limits
}

limiter, err := rcmgr.NewLimiter(limits, defaultLimits)
if err != nil {
return nil, opts, err
}

Expand All @@ -80,9 +71,8 @@ func ResourceManager(cfg config.SwarmConfig) func(fx.Lifecycle, repo.Repo) (netw

manager, err = rcmgr.NewResourceManager(limiter, ropts...)
if err != nil {
return nil, opts, fmt.Errorf("error creating libp2p resource manager: %w", err)
return nil, opts, fmt.Errorf("creating libp2p resource manager: %w", err)
}

} else {
log.Debug("libp2p resource manager is disabled")
manager = network.NullResourceManager
Expand Down Expand Up @@ -196,14 +186,13 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
}
}

func NetLimit(mgr network.ResourceManager, scope string) (config.ResourceMgrScopeConfig, error) {
var result config.ResourceMgrScopeConfig
func NetLimit(mgr network.ResourceManager, scope string) (rcmgr.BasicLimitConfig, error) {
var result rcmgr.BasicLimitConfig
getLimit := func(s network.ResourceScope) error {
limiter, ok := s.(rcmgr.ResourceScopeLimiter)
if !ok { // NullResourceManager
return NoResourceMgrError
}

limit := limiter.Limit()
switch l := limit.(type) {
case *rcmgr.StaticLimit:
Expand Down Expand Up @@ -280,7 +269,8 @@ func NetLimit(mgr network.ResourceManager, scope string) (config.ResourceMgrScop
}
}

func NetSetLimit(mgr network.ResourceManager, scope string, limit config.ResourceMgrScopeConfig) error {
// NetSetLimit sets new ResourceManager limits for the given scope. The limits take effect immediately, and are also persisted to the repo config.
func NetSetLimit(mgr network.ResourceManager, repo repo.Repo, scope string, limit rcmgr.BasicLimitConfig) error {
setLimit := func(s network.ResourceScope) error {
limiter, ok := s.(rcmgr.ResourceScopeLimiter)
if !ok { // NullResourceManager
Expand Down Expand Up @@ -324,45 +314,87 @@ func NetSetLimit(mgr network.ResourceManager, scope string, limit config.Resourc
return nil
}

cfg, err := repo.Config()
if err != nil {
return fmt.Errorf("reading config to set limit: %w", err)
}

if cfg.Swarm.ResourceMgr.Limits == nil {
cfg.Swarm.ResourceMgr.Limits = &rcmgr.BasicLimiterConfig{}
}
configLimits := cfg.Swarm.ResourceMgr.Limits

var setConfigFunc func()
switch {
case scope == config.ResourceMgrSystemScope:
err := mgr.ViewSystem(func(s network.ResourceScope) error {
err = mgr.ViewSystem(func(s network.ResourceScope) error {
return setLimit(s)
})
return err
setConfigFunc = func() { configLimits.System = &limit }

case scope == config.ResourceMgrTransientScope:
err := mgr.ViewTransient(func(s network.ResourceScope) error {
err = mgr.ViewTransient(func(s network.ResourceScope) error {
return setLimit(s)
})
return err
setConfigFunc = func() { configLimits.Transient = &limit }

case strings.HasPrefix(scope, config.ResourceMgrServiceScopePrefix):
svc := scope[4:]
err := mgr.ViewService(svc, func(s network.ServiceScope) error {
svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix)
err = mgr.ViewService(svc, func(s network.ServiceScope) error {
return setLimit(s)
})
return err
setConfigFunc = func() {
if configLimits.Service == nil {
configLimits.Service = map[string]rcmgr.BasicLimitConfig{}
}
configLimits.Service[svc] = limit
}

case strings.HasPrefix(scope, config.ResourceMgrProtocolScopePrefix):
proto := scope[6:]
err := mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error {
proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix)
err = mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error {
return setLimit(s)
})
return err
setConfigFunc = func() {
if configLimits.Protocol == nil {
configLimits.Protocol = map[string]rcmgr.BasicLimitConfig{}
}
configLimits.Protocol[proto] = limit
}

case strings.HasPrefix(scope, config.ResourceMgrPeerScopePrefix):
p := scope[5:]
pid, err := peer.Decode(p)
p := strings.TrimPrefix(scope, config.ResourceMgrPeerScopePrefix)
var pid peer.ID
pid, err = peer.Decode(p)
if err != nil {
return fmt.Errorf("invalid peer ID: %q: %w", p, err)
}
err = mgr.ViewPeer(pid, func(s network.PeerScope) error {
return setLimit(s)
})
return err
setConfigFunc = func() {
if configLimits.Peer == nil {
configLimits.Peer = map[string]rcmgr.BasicLimitConfig{}
}
configLimits.Peer[p] = limit
}

default:
return fmt.Errorf("invalid scope %q", scope)
}

if err != nil {
return fmt.Errorf("setting new limits on resource manager: %w", err)
}

if cfg.Swarm.ResourceMgr.Limits == nil {
cfg.Swarm.ResourceMgr.Limits = &rcmgr.BasicLimiterConfig{}
}
setConfigFunc()

if err := repo.SetConfig(cfg); err != nil {
return fmt.Errorf("writing new limits to repo config: %w", err)
}

return nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ require (
github.com/libp2p/go-libp2p-pubsub-router v0.5.0
github.com/libp2p/go-libp2p-quic-transport v0.16.1
github.com/libp2p/go-libp2p-record v0.1.3
github.com/libp2p/go-libp2p-resource-manager v0.1.5
github.com/libp2p/go-libp2p-resource-manager v0.3.0
github.com/libp2p/go-libp2p-routing-helpers v0.2.3
github.com/libp2p/go-libp2p-swarm v0.10.2
github.com/libp2p/go-libp2p-testing v0.8.0
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -910,8 +910,9 @@ github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7
github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk=
github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0=
github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4=
github.com/libp2p/go-libp2p-resource-manager v0.1.5 h1:7J6t9KLFS0MxXDTfqA6rwfVCZl/yLQnXW5LpZjHAANI=
github.com/libp2p/go-libp2p-resource-manager v0.1.5/go.mod h1:wJPNjeE4XQlxeidwqVY5G6DLOKqFK33u2n8blpl0I6Y=
github.com/libp2p/go-libp2p-resource-manager v0.3.0 h1:2+cYxUNi33tcydsVLt6K5Fv2E3OTiVeafltecAj15E0=
github.com/libp2p/go-libp2p-resource-manager v0.3.0/go.mod h1:K+eCkiapf+ey/LADO4TaMpMTP9/Qde/uLlrnRqV4PLQ=
github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys=
github.com/libp2p/go-libp2p-routing-helpers v0.2.3 h1:xY61alxJ6PurSi+MXbywZpelvuU4U4p/gPTxjqCqTzY=
github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw=
Expand Down
Loading