Skip to content

Commit

Permalink
feat: Reduce RM code footprint
Browse files Browse the repository at this point in the history
Co-Authored-By: Antonio Navarro Perez <[email protected]>
  • Loading branch information
Jorropo and ajnavarro committed Mar 1, 2023
1 parent 145795b commit 46c251e
Show file tree
Hide file tree
Showing 16 changed files with 543 additions and 880 deletions.
6 changes: 2 additions & 4 deletions config/swarm.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package config

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

type SwarmConfig struct {
// AddrFilters specifies a set libp2p addresses that we should never
// dial or receive connections from.
Expand Down Expand Up @@ -141,8 +139,8 @@ type ConnMgr struct {
// <https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#readme>
type ResourceMgr struct {
// Enables the Network Resource Manager feature, default to on.
Enabled Flag `json:",omitempty"`
Limits *rcmgr.PartialLimitConfig `json:",omitempty"`
Enabled Flag `json:",omitempty"`
Limits swarmLimits `json:",omitempty"`

MaxMemory *OptionalString `json:",omitempty"`
MaxFileDescriptors *OptionalInteger `json:",omitempty"`
Expand Down
25 changes: 25 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package config

import (
"bytes"
"encoding/json"
"fmt"
"io"
"strings"
"time"
)
Expand Down Expand Up @@ -412,3 +414,26 @@ func (p OptionalString) String() string {

var _ json.Unmarshaler = (*OptionalInteger)(nil)
var _ json.Marshaler = (*OptionalInteger)(nil)

type swarmLimits struct{}

var _ json.Unmarshaler = swarmLimits{}

func (swarmLimits) UnmarshalJSON(b []byte) error {
d := json.NewDecoder(bytes.NewReader(b))
for {
switch tok, err := d.Token(); err {
case io.EOF:
return nil
case nil:
switch tok {
case json.Delim('{'), json.Delim('}'):
// accept empty objects
continue
}
return fmt.Errorf("field Swarm.ResourceMgr.Limits has been removed in 0.19 and must be empty or not present")
default:
return err
}
}
}
1 change: 1 addition & 0 deletions core/commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ func TestCommands(t *testing.T) {
"/swarm/peering/ls",
"/swarm/peering/rm",
"/swarm/stats",
"/swarm/resources",
"/tar",
"/tar/add",
"/tar/cat",
Expand Down
185 changes: 88 additions & 97 deletions core/commands/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"path"
"sort"
"sync"
"text/tabwriter"
"time"

"github.com/ipfs/go-libipfs/files"
"github.com/ipfs/kubo/commands"
"github.com/ipfs/kubo/config"
"github.com/ipfs/kubo/core/commands/cmdenv"
Expand All @@ -21,6 +21,7 @@ import (
"github.com/ipfs/kubo/repo/fsrepo"

cmds "github.com/ipfs/go-ipfs-cmds"
"github.com/libp2p/go-libp2p/core/network"
inet "github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
Expand Down Expand Up @@ -57,8 +58,10 @@ ipfs peers in the internet.
"filters": swarmFiltersCmd,
"peers": swarmPeersCmd,
"peering": swarmPeeringCmd,
"stats": swarmStatsCmd, // libp2p Network Resource Manager
"limit": swarmLimitCmd, // libp2p Network Resource Manager
"stats": swarmStatsCmd, // libp2p Network Resource Manager
"limit": swarmLimitCmd, // libp2p Network Resource Manager
"resources": swarmResourcesCmd, // libp2p Network Resource Manager

},
}

Expand Down Expand Up @@ -326,27 +329,10 @@ var swarmPeersCmd = &cmds.Command{
var swarmStatsCmd = &cmds.Command{
Status: cmds.Experimental,
Helptext: cmds.HelpText{
Tagline: "Report resource usage for a scope.",
LongDescription: `Report resource usage for a scope.
The scope can be one of the following:
- system -- reports the system aggregate resource usage.
- transient -- reports the transient resource usage.
- svc:<service> -- reports the resource usage of a specific service.
- proto:<proto> -- reports the resource usage of a specific protocol.
- peer:<peer> -- reports the resource usage of a specific peer.
- all -- reports the resource usage for all currently active scopes.
Tagline: "Report resource usage reported by libp2p Resource Manager.",
LongDescription: `Report resource usage reported by libp2p Resource Manager.
The output of this command is JSON.
To see all resources that are close to hitting their respective limit, one can do something like:
ipfs swarm stats --min-used-limit-perc=90 all
`},
Arguments: []cmds.Argument{
cmds.StringArg("scope", true, false, "scope of the stat report"),
},
Options: []cmds.Option{
cmds.IntOption(swarmUsedResourcesPercentageName, "Only display resources that are using above the specified percentage of their respective limit"),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
node, err := cmdenv.GetNode(env)
if err != nil {
Expand All @@ -357,25 +343,16 @@ To see all resources that are close to hitting their respective limit, one can d
return libp2p.ErrNoResourceMgr
}

if len(req.Arguments) != 1 {
return fmt.Errorf("must specify exactly one scope")
}

percentage, _ := req.Options[swarmUsedResourcesPercentageName].(int)
scope := req.Arguments[0]

if percentage != 0 && scope != "all" {
return fmt.Errorf("%q can only be used when scope is %q", swarmUsedResourcesPercentageName, "all")
rapi, ok := node.ResourceManager.(rcmgr.ResourceManagerState)
if !ok { // NullResourceManager
return libp2p.ErrNoResourceMgr
}

result, err := libp2p.NetStat(node.ResourceManager, scope, percentage)
if err != nil {
return err
}
stats := rapi.Stat()

b := new(bytes.Buffer)
enc := json.NewEncoder(b)
err = enc.Encode(result)
err = enc.Encode(stats)
if err != nil {
return err
}
Expand All @@ -389,88 +366,38 @@ To see all resources that are close to hitting their respective limit, one can d
var swarmLimitCmd = &cmds.Command{
Status: cmds.Experimental,
Helptext: cmds.HelpText{
Tagline: "Get or set resource limits for a scope.",
LongDescription: `Get or set resource limits for a scope.
The scope can be one of the following:
- all -- all limits actually being applied.
- system -- limits for the system aggregate resource usage.
- transient -- limits for the transient resource usage.
- svc:<service> -- limits for the resource usage of a specific service.
- proto:<proto> -- limits for the resource usage of a specific protocol.
- peer:<peer> -- limits for the resource usage of a specific peer.
Tagline: "Get actual limits reported by libp2p Resource Manager.",
LongDescription: `Get actual limits reported by libp2p Resource Manager.
The output of this command is JSON.
It is possible to use this command to inspect and tweak limits at runtime:
$ ipfs swarm limit system > limit.json
$ vi limit.json
$ ipfs swarm limit system limit.json
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"),
cmds.FileArg("limit.json", false, false, "limits to be set").EnableStdin(),
},
Options: []cmds.Option{
cmds.BoolOption(swarmResetLimitsOptionName, "reset limit to default"),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
node, err := cmdenv.GetNode(env)
if err != nil {
return err
}

if node.ResourceManager == nil {
if _, ok := node.ResourceManager.(*network.NullResourceManager); ok {
return libp2p.ErrNoResourceMgr
}

scope := req.Arguments[0]

// set scope limit to new values (when limit.json is passed as a second arg)
if req.Files != nil {
var newLimit rcmgr.ResourceLimits
it := req.Files.Entries()
if it.Next() {
file := files.FileFromEntry(it)
if file == nil {
return errors.New("expected a JSON file")
}

r := io.LimitReader(file, 32*1024*1024) // 32MiB

if err := json.NewDecoder(r).Decode(&newLimit); err != nil {
return fmt.Errorf("decoding JSON as ResourceMgrScopeConfig: %w", err)
}
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)
}
cfg, err := node.Repo.Config()
if err != nil {
return err
}

var result interface{}
switch _, reset := req.Options[swarmResetLimitsOptionName]; {
case reset:
result, err = libp2p.NetResetLimit(node.ResourceManager, node.Repo, scope)
case scope == "all":
result, err = libp2p.NetLimitAll(node.ResourceManager)
default:
// get scope limit
result, err = libp2p.NetLimit(node.ResourceManager, scope)
}
userRessourceOverrides, err := node.Repo.UserRessourceOverrides()
if err != nil {
return err
}

if base, ok := result.(rcmgr.BaseLimit); ok {
result = base.ToResourceLimits()
result, _, err := libp2p.LimitConfig(cfg.Swarm, userRessourceOverrides)
if err != nil {
return err
}

b := new(bytes.Buffer)
enc := json.NewEncoder(b)
err = enc.Encode(result)
err = enc.Encode(result.ToPartialLimitConfig())
if err != nil {
return err
}
Expand All @@ -481,6 +408,70 @@ Changes made via command line are persisted in the Swarm.ResourceMgr.Limits fiel
},
}

var swarmResourcesCmd = &cmds.Command{
Status: cmds.Experimental,
Helptext: cmds.HelpText{
Tagline: "Get a summary about all resources in use by libp2p Resource Manager.",
LongDescription: `Get a summary about all resources in use by libp2p Resource Manager.
The output of this command is JSON.
`},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
node, err := cmdenv.GetNode(env)
if err != nil {
return err
}

if node.ResourceManager == nil {
return libp2p.ErrNoResourceMgr
}

cfg, err := node.Repo.Config()
if err != nil {
return err
}

userRessourceOverrides, err := node.Repo.UserRessourceOverrides()
if err != nil {
return err
}

defaultConfig, _, err := libp2p.LimitConfig(cfg.Swarm, userRessourceOverrides)
if err != nil {
return err
}

rapi, ok := node.ResourceManager.(rcmgr.ResourceManagerState)
if !ok { // NullResourceManager
return libp2p.ErrNoResourceMgr
}

stats := rapi.Stat()
statsConfig := libp2p.StatToLimitConfig(stats)

return cmds.EmitOnce(res, libp2p.LimitConfigsToInfo(defaultConfig, statsConfig))
},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, ris libp2p.ResourceInfos) error {
tw := tabwriter.NewWriter(w, 30, 8, 0, '\t', 0)
defer tw.Flush()

fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t\n", "scope name", "limit name", "limit", "usage", "percent usage")
for _, ri := range ris {
fmt.Fprintf(tw, "%s\t%s\t%d\t%d\t%.1f%%\t\n",
ri.ScopeName,
ri.LimitName,
ri.Limit,
ri.CurrentUsage,
(float32(ri.CurrentUsage)/float32(ri.Limit))*100,
)
}

return nil
}),
},
Type: libp2p.ResourceInfos{},
}

type streamInfo struct {
Protocol string
}
Expand Down
Loading

0 comments on commit 46c251e

Please sign in to comment.