Skip to content

Commit 74aff24

Browse files
guseggertlidel
andauthored
feat: persist limits to Swarm.ResourceMgr.Limits (#8901)
* feat: persist limit changes to config This changes the "ipfs swarm limit" command so that when limit changes are applied via the command line, they are persisted to the repo config, so that they remain in effect when the daemon restarts. Any existing limit.json can be dropped into the IPFS config easily using something like: cat ~/.ipfs/config | jq ".Swarm.ResourceMgr.Limits = $(cat limit.json)" | sponge ~/.ipfs/config This also upgrades to Resource Manager v0.3.0, which exports the config schema so that we don't have to maintain our own copy of it. Co-authored-by: Marcin Rataj <[email protected]>
1 parent d4879a4 commit 74aff24

File tree

6 files changed

+198
-114
lines changed

6 files changed

+198
-114
lines changed

config/swarm.go

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package config
22

3+
import rcmgr "github.com/libp2p/go-libp2p-resource-manager"
4+
35
type SwarmConfig struct {
46
// AddrFilters specifies a set libp2p addresses that we should never
57
// dial or receive connections from.
@@ -137,10 +139,8 @@ type ConnMgr struct {
137139
// <https://github.com/libp2p/go-libp2p-resource-manager#readme>
138140
type ResourceMgr struct {
139141
// Enables the Network Resource Manager feature
140-
Enabled Flag `json:",omitempty"`
141-
142-
/* TODO: decide if and how we want to expose limits in our config
143-
Limits *ResourceMgrScopeConfig `json:",omitempty"` */
142+
Enabled Flag `json:",omitempty"`
143+
Limits *rcmgr.BasicLimiterConfig `json:",omitempty"`
144144
}
145145

146146
const (
@@ -150,41 +150,3 @@ const (
150150
ResourceMgrProtocolScopePrefix = "proto:"
151151
ResourceMgrPeerScopePrefix = "peer:"
152152
)
153-
154-
/* TODO: decide if and how we want to expose limits in our config
155-
type ResourceMgrLimitsConfig struct {
156-
System *ResourceMgrScopeConfig `json:",omitempty"`
157-
Transient *ResourceMgrScopeConfig `json:",omitempty"`
158-
159-
ServiceDefault *ResourceMgrScopeConfig `json:",omitempty"`
160-
ServicePeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
161-
Service map[string]ResourceMgrScopeConfig `json:",omitempty"`
162-
ServicePeer map[string]ResourceMgrScopeConfig `json:",omitempty"`
163-
164-
ProtocolDefault *ResourceMgrScopeConfig `json:",omitempty"`
165-
ProtocolPeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
166-
Protocol map[string]ResourceMgrScopeConfig `json:",omitempty"`
167-
ProtocolPeer map[string]ResourceMgrScopeConfig `json:",omitempty"`
168-
169-
PeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
170-
Peer map[string]ResourceMgrScopeConfig `json:",omitempty"`
171-
172-
Conn *ResourceMgrScopeConfig `json:",omitempty"`
173-
Stream *ResourceMgrScopeConfig `json:",omitempty"`
174-
}
175-
*/
176-
177-
// libp2p Network Resource Manager config for a scope
178-
type ResourceMgrScopeConfig struct {
179-
Dynamic bool `json:",omitempty"`
180-
// set if Dynamic is false
181-
Memory int64 `json:",omitempty"`
182-
// set if Dynamic is true
183-
MemoryFraction float64 `json:",omitempty"`
184-
MinMemory int64 `json:",omitempty"`
185-
MaxMemory int64 `json:",omitempty"`
186-
187-
Streams, StreamsInbound, StreamsOutbound int
188-
Conns, ConnsInbound, ConnsOutbound int
189-
FD int
190-
}

core/commands/swarm.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
cmds "github.com/ipfs/go-ipfs-cmds"
2424
inet "github.com/libp2p/go-libp2p-core/network"
2525
"github.com/libp2p/go-libp2p-core/peer"
26+
rcmgr "github.com/libp2p/go-libp2p-resource-manager"
2627
ma "github.com/multiformats/go-multiaddr"
2728
madns "github.com/multiformats/go-multiaddr-dns"
2829
mamask "github.com/whyrusleeping/multiaddr-filter"
@@ -380,8 +381,7 @@ It is possible to use this command to inspect and tweak limits at runtime:
380381
$ vi limit.json
381382
$ ipfs swarm limit system limit.json
382383
383-
Changes made via command line are discarded on node shutdown.
384-
For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.
384+
Changes made via command line are persisted in the Swarm.ResourceMgr.Limits field of the $IPFS_PATH/config file.
385385
`},
386386
Arguments: []cmds.Argument{
387387
cmds.StringArg("scope", true, false, "scope of the limit"),
@@ -401,7 +401,7 @@ For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.
401401

402402
// set scope limit to new values (when limit.json is passed as a second arg)
403403
if req.Files != nil {
404-
var newLimit config.ResourceMgrScopeConfig
404+
var newLimit rcmgr.BasicLimitConfig
405405
it := req.Files.Entries()
406406
if it.Next() {
407407
file := files.FileFromEntry(it)
@@ -411,7 +411,7 @@ For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.
411411
if err := json.NewDecoder(file).Decode(&newLimit); err != nil {
412412
return errors.New("failed to decode JSON as ResourceMgrScopeConfig")
413413
}
414-
return libp2p.NetSetLimit(node.ResourceManager, scope, newLimit)
414+
return libp2p.NetSetLimit(node.ResourceManager, node.Repo, scope, newLimit)
415415
}
416416
if err := it.Err(); err != nil {
417417
return fmt.Errorf("error opening limit JSON file: %w", err)

core/node/libp2p/rcmgr.go

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package libp2p
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"os"
87
"path/filepath"
@@ -27,7 +26,6 @@ var NoResourceMgrError = fmt.Errorf("missing ResourceMgr: make sure the daemon i
2726

2827
func ResourceManager(cfg config.SwarmConfig) func(fx.Lifecycle, repo.Repo) (network.ResourceManager, Libp2pOpts, error) {
2928
return func(lc fx.Lifecycle, repo repo.Repo) (network.ResourceManager, Libp2pOpts, error) {
30-
var limiter *rcmgr.BasicLimiter
3129
var manager network.ResourceManager
3230
var opts Libp2pOpts
3331

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

4846
repoPath, err := config.PathRoot()
4947
if err != nil {
50-
return nil, opts, fmt.Errorf("error opening IPFS_PATH: %w", err)
48+
return nil, opts, fmt.Errorf("opening IPFS_PATH: %w", err)
5149
}
5250

53-
// Create limiter:
54-
// - parse $IPFS_PATH/limits.json if exists
55-
// - use defaultLimits from rcmgr_defaults.go
5651
defaultLimits := adjustedDefaultLimits(cfg)
57-
limitFilePath := filepath.Join(repoPath, NetLimitDefaultFilename)
58-
limitFile, err := os.Open(limitFilePath)
59-
switch {
60-
case err == nil:
61-
defer limitFile.Close()
62-
limiter, err = rcmgr.NewLimiterFromJSON(limitFile, defaultLimits)
63-
if err != nil {
64-
return nil, opts, fmt.Errorf("error parsing libp2p limit file: %w", err)
65-
}
66-
case errors.Is(err, os.ErrNotExist):
67-
limiter = rcmgr.NewStaticLimiter(defaultLimits)
68-
default:
52+
53+
var limits rcmgr.BasicLimiterConfig
54+
if cfg.ResourceMgr.Limits != nil {
55+
limits = *cfg.ResourceMgr.Limits
56+
}
57+
58+
limiter, err := rcmgr.NewLimiter(limits, defaultLimits)
59+
if err != nil {
6960
return nil, opts, err
7061
}
7162

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

8172
manager, err = rcmgr.NewResourceManager(limiter, ropts...)
8273
if err != nil {
83-
return nil, opts, fmt.Errorf("error creating libp2p resource manager: %w", err)
74+
return nil, opts, fmt.Errorf("creating libp2p resource manager: %w", err)
8475
}
85-
8676
} else {
8777
log.Debug("libp2p resource manager is disabled")
8878
manager = network.NullResourceManager
@@ -196,14 +186,13 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
196186
}
197187
}
198188

199-
func NetLimit(mgr network.ResourceManager, scope string) (config.ResourceMgrScopeConfig, error) {
200-
var result config.ResourceMgrScopeConfig
189+
func NetLimit(mgr network.ResourceManager, scope string) (rcmgr.BasicLimitConfig, error) {
190+
var result rcmgr.BasicLimitConfig
201191
getLimit := func(s network.ResourceScope) error {
202192
limiter, ok := s.(rcmgr.ResourceScopeLimiter)
203193
if !ok { // NullResourceManager
204194
return NoResourceMgrError
205195
}
206-
207196
limit := limiter.Limit()
208197
switch l := limit.(type) {
209198
case *rcmgr.StaticLimit:
@@ -280,7 +269,8 @@ func NetLimit(mgr network.ResourceManager, scope string) (config.ResourceMgrScop
280269
}
281270
}
282271

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

317+
cfg, err := repo.Config()
318+
if err != nil {
319+
return fmt.Errorf("reading config to set limit: %w", err)
320+
}
321+
322+
if cfg.Swarm.ResourceMgr.Limits == nil {
323+
cfg.Swarm.ResourceMgr.Limits = &rcmgr.BasicLimiterConfig{}
324+
}
325+
configLimits := cfg.Swarm.ResourceMgr.Limits
326+
327+
var setConfigFunc func()
327328
switch {
328329
case scope == config.ResourceMgrSystemScope:
329-
err := mgr.ViewSystem(func(s network.ResourceScope) error {
330+
err = mgr.ViewSystem(func(s network.ResourceScope) error {
330331
return setLimit(s)
331332
})
332-
return err
333+
setConfigFunc = func() { configLimits.System = &limit }
333334

334335
case scope == config.ResourceMgrTransientScope:
335-
err := mgr.ViewTransient(func(s network.ResourceScope) error {
336+
err = mgr.ViewTransient(func(s network.ResourceScope) error {
336337
return setLimit(s)
337338
})
338-
return err
339+
setConfigFunc = func() { configLimits.Transient = &limit }
339340

340341
case strings.HasPrefix(scope, config.ResourceMgrServiceScopePrefix):
341-
svc := scope[4:]
342-
err := mgr.ViewService(svc, func(s network.ServiceScope) error {
342+
svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix)
343+
err = mgr.ViewService(svc, func(s network.ServiceScope) error {
343344
return setLimit(s)
344345
})
345-
return err
346+
setConfigFunc = func() {
347+
if configLimits.Service == nil {
348+
configLimits.Service = map[string]rcmgr.BasicLimitConfig{}
349+
}
350+
configLimits.Service[svc] = limit
351+
}
346352

347353
case strings.HasPrefix(scope, config.ResourceMgrProtocolScopePrefix):
348-
proto := scope[6:]
349-
err := mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error {
354+
proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix)
355+
err = mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error {
350356
return setLimit(s)
351357
})
352-
return err
358+
setConfigFunc = func() {
359+
if configLimits.Protocol == nil {
360+
configLimits.Protocol = map[string]rcmgr.BasicLimitConfig{}
361+
}
362+
configLimits.Protocol[proto] = limit
363+
}
353364

354365
case strings.HasPrefix(scope, config.ResourceMgrPeerScopePrefix):
355-
p := scope[5:]
356-
pid, err := peer.Decode(p)
366+
p := strings.TrimPrefix(scope, config.ResourceMgrPeerScopePrefix)
367+
var pid peer.ID
368+
pid, err = peer.Decode(p)
357369
if err != nil {
358370
return fmt.Errorf("invalid peer ID: %q: %w", p, err)
359371
}
360372
err = mgr.ViewPeer(pid, func(s network.PeerScope) error {
361373
return setLimit(s)
362374
})
363-
return err
375+
setConfigFunc = func() {
376+
if configLimits.Peer == nil {
377+
configLimits.Peer = map[string]rcmgr.BasicLimitConfig{}
378+
}
379+
configLimits.Peer[p] = limit
380+
}
364381

365382
default:
366383
return fmt.Errorf("invalid scope %q", scope)
367384
}
385+
386+
if err != nil {
387+
return fmt.Errorf("setting new limits on resource manager: %w", err)
388+
}
389+
390+
if cfg.Swarm.ResourceMgr.Limits == nil {
391+
cfg.Swarm.ResourceMgr.Limits = &rcmgr.BasicLimiterConfig{}
392+
}
393+
setConfigFunc()
394+
395+
if err := repo.SetConfig(cfg); err != nil {
396+
return fmt.Errorf("writing new limits to repo config: %w", err)
397+
}
398+
399+
return nil
368400
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ require (
8282
github.com/libp2p/go-libp2p-pubsub-router v0.5.0
8383
github.com/libp2p/go-libp2p-quic-transport v0.16.1
8484
github.com/libp2p/go-libp2p-record v0.1.3
85-
github.com/libp2p/go-libp2p-resource-manager v0.1.5
85+
github.com/libp2p/go-libp2p-resource-manager v0.3.0
8686
github.com/libp2p/go-libp2p-routing-helpers v0.2.3
8787
github.com/libp2p/go-libp2p-swarm v0.10.2
8888
github.com/libp2p/go-libp2p-testing v0.8.0

go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,8 +910,9 @@ github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7
910910
github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk=
911911
github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0=
912912
github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4=
913-
github.com/libp2p/go-libp2p-resource-manager v0.1.5 h1:7J6t9KLFS0MxXDTfqA6rwfVCZl/yLQnXW5LpZjHAANI=
914913
github.com/libp2p/go-libp2p-resource-manager v0.1.5/go.mod h1:wJPNjeE4XQlxeidwqVY5G6DLOKqFK33u2n8blpl0I6Y=
914+
github.com/libp2p/go-libp2p-resource-manager v0.3.0 h1:2+cYxUNi33tcydsVLt6K5Fv2E3OTiVeafltecAj15E0=
915+
github.com/libp2p/go-libp2p-resource-manager v0.3.0/go.mod h1:K+eCkiapf+ey/LADO4TaMpMTP9/Qde/uLlrnRqV4PLQ=
915916
github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys=
916917
github.com/libp2p/go-libp2p-routing-helpers v0.2.3 h1:xY61alxJ6PurSi+MXbywZpelvuU4U4p/gPTxjqCqTzY=
917918
github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw=

0 commit comments

Comments
 (0)