Skip to content

Commit 9431bce

Browse files
Jorropoajnavarro
andcommitted
feat: Reduce RM code footprint
Co-Authored-By: Antonio Navarro Perez <[email protected]>
1 parent 68f4dd4 commit 9431bce

17 files changed

+639
-951
lines changed

config/swarm.go

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

3-
import rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
4-
53
type SwarmConfig struct {
64
// AddrFilters specifies a set libp2p addresses that we should never
75
// dial or receive connections from.
@@ -141,8 +139,8 @@ type ConnMgr struct {
141139
// <https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#readme>
142140
type ResourceMgr struct {
143141
// Enables the Network Resource Manager feature, default to on.
144-
Enabled Flag `json:",omitempty"`
145-
Limits *rcmgr.PartialLimitConfig `json:",omitempty"`
142+
Enabled Flag `json:",omitempty"`
143+
Limits swarmLimits `json:",omitempty"`
146144

147145
MaxMemory *OptionalString `json:",omitempty"`
148146
MaxFileDescriptors *OptionalInteger `json:",omitempty"`

config/types.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package config
22

33
import (
4+
"bytes"
45
"encoding/json"
56
"fmt"
7+
"io"
68
"strings"
79
"time"
810
)
@@ -412,3 +414,27 @@ func (p OptionalString) String() string {
412414

413415
var _ json.Unmarshaler = (*OptionalInteger)(nil)
414416
var _ json.Marshaler = (*OptionalInteger)(nil)
417+
418+
type swarmLimits struct{}
419+
420+
var _ json.Unmarshaler = swarmLimits{}
421+
422+
func (swarmLimits) UnmarshalJSON(b []byte) error {
423+
d := json.NewDecoder(bytes.NewReader(b))
424+
for {
425+
switch tok, err := d.Token(); err {
426+
case io.EOF:
427+
return nil
428+
case nil:
429+
switch tok {
430+
case json.Delim('{'), json.Delim('}'):
431+
// accept empty objects
432+
continue
433+
}
434+
//nolint
435+
return fmt.Errorf("The Swarm.ResourceMgr.Limits configuration has been removed in Kubo 0.19 and should be empty or not present. To set custom libp2p limits, read https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#user-supplied-override-limits")
436+
default:
437+
return err
438+
}
439+
}
440+
}

core/commands/commands_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ func TestCommands(t *testing.T) {
255255
"/swarm/peering/ls",
256256
"/swarm/peering/rm",
257257
"/swarm/stats",
258+
"/swarm/resources",
258259
"/tar",
259260
"/tar/add",
260261
"/tar/cat",

core/commands/swarm.go

Lines changed: 51 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package commands
22

33
import (
4-
"bytes"
54
"context"
65
"encoding/json"
76
"errors"
87
"fmt"
98
"io"
109
"path"
1110
"sort"
11+
"strconv"
1212
"sync"
13+
"text/tabwriter"
1314
"time"
1415

15-
"github.com/ipfs/go-libipfs/files"
1616
"github.com/ipfs/kubo/commands"
1717
"github.com/ipfs/kubo/config"
1818
"github.com/ipfs/kubo/core/commands/cmdenv"
@@ -57,8 +57,8 @@ ipfs peers in the internet.
5757
"filters": swarmFiltersCmd,
5858
"peers": swarmPeersCmd,
5959
"peering": swarmPeeringCmd,
60-
"stats": swarmStatsCmd, // libp2p Network Resource Manager
61-
"limit": swarmLimitCmd, // libp2p Network Resource Manager
60+
"resources": swarmResourcesCmd, // libp2p Network Resource Manager
61+
6262
},
6363
}
6464

@@ -323,30 +323,13 @@ var swarmPeersCmd = &cmds.Command{
323323
Type: connInfos{},
324324
}
325325

326-
var swarmStatsCmd = &cmds.Command{
326+
var swarmResourcesCmd = &cmds.Command{
327327
Status: cmds.Experimental,
328328
Helptext: cmds.HelpText{
329-
Tagline: "Report resource usage for a scope.",
330-
LongDescription: `Report resource usage for a scope.
331-
The scope can be one of the following:
332-
- system -- reports the system aggregate resource usage.
333-
- transient -- reports the transient resource usage.
334-
- svc:<service> -- reports the resource usage of a specific service.
335-
- proto:<proto> -- reports the resource usage of a specific protocol.
336-
- peer:<peer> -- reports the resource usage of a specific peer.
337-
- all -- reports the resource usage for all currently active scopes.
338-
339-
The output of this command is JSON.
340-
341-
To see all resources that are close to hitting their respective limit, one can do something like:
342-
ipfs swarm stats --min-used-limit-perc=90 all
329+
Tagline: "Get a summary about all resources accounted for by the libp2p Resource Manager.",
330+
LongDescription: `Get a summary about all resources accounted for by the libp2p Resource Manager. This includes the limits and the usage against those limits.
331+
This can output a human readable table and JSON encoding.
343332
`},
344-
Arguments: []cmds.Argument{
345-
cmds.StringArg("scope", true, false, "scope of the stat report"),
346-
},
347-
Options: []cmds.Option{
348-
cmds.IntOption(swarmUsedResourcesPercentageName, "Only display resources that are using above the specified percentage of their respective limit"),
349-
},
350333
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
351334
node, err := cmdenv.GetNode(env)
352335
if err != nil {
@@ -357,128 +340,68 @@ To see all resources that are close to hitting their respective limit, one can d
357340
return libp2p.ErrNoResourceMgr
358341
}
359342

360-
if len(req.Arguments) != 1 {
361-
return fmt.Errorf("must specify exactly one scope")
362-
}
363-
364-
percentage, _ := req.Options[swarmUsedResourcesPercentageName].(int)
365-
scope := req.Arguments[0]
366-
367-
if percentage != 0 && scope != "all" {
368-
return fmt.Errorf("%q can only be used when scope is %q", swarmUsedResourcesPercentageName, "all")
369-
}
370-
371-
result, err := libp2p.NetStat(node.ResourceManager, scope, percentage)
343+
cfg, err := node.Repo.Config()
372344
if err != nil {
373345
return err
374346
}
375347

376-
b := new(bytes.Buffer)
377-
enc := json.NewEncoder(b)
378-
err = enc.Encode(result)
348+
userResourceOverrides, err := node.Repo.UserResourceOverrides()
379349
if err != nil {
380350
return err
381351
}
382-
return cmds.EmitOnce(res, b)
383-
},
384-
Encoders: cmds.EncoderMap{
385-
cmds.Text: HumanJSONEncoder,
386-
},
387-
}
388-
389-
var swarmLimitCmd = &cmds.Command{
390-
Status: cmds.Experimental,
391-
Helptext: cmds.HelpText{
392-
Tagline: "Get or set resource limits for a scope.",
393-
LongDescription: `Get or set resource limits for a scope.
394-
The scope can be one of the following:
395-
- all -- all limits actually being applied.
396-
- system -- limits for the system aggregate resource usage.
397-
- transient -- limits for the transient resource usage.
398-
- svc:<service> -- limits for the resource usage of a specific service.
399-
- proto:<proto> -- limits for the resource usage of a specific protocol.
400-
- peer:<peer> -- limits for the resource usage of a specific peer.
401-
402-
The output of this command is JSON.
403-
404-
It is possible to use this command to inspect and tweak limits at runtime:
405-
406-
$ ipfs swarm limit system > limit.json
407-
$ vi limit.json
408-
$ ipfs swarm limit system limit.json
409352

410-
Changes made via command line are persisted in the Swarm.ResourceMgr.Limits field of the $IPFS_PATH/config file.
411-
`},
412-
Arguments: []cmds.Argument{
413-
cmds.StringArg("scope", true, false, "scope of the limit"),
414-
cmds.FileArg("limit.json", false, false, "limits to be set").EnableStdin(),
415-
},
416-
Options: []cmds.Option{
417-
cmds.BoolOption(swarmResetLimitsOptionName, "reset limit to default"),
418-
},
419-
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
420-
node, err := cmdenv.GetNode(env)
353+
// FIXME: we shouldn't recompute limits, either save them or load them from libp2p (https://github.com/libp2p/go-libp2p/issues/2166)
354+
limitConfig, _, err := libp2p.LimitConfig(cfg.Swarm, userResourceOverrides)
421355
if err != nil {
422356
return err
423357
}
424358

425-
if node.ResourceManager == nil {
359+
rapi, ok := node.ResourceManager.(rcmgr.ResourceManagerState)
360+
if !ok { // NullResourceManager
426361
return libp2p.ErrNoResourceMgr
427362
}
428363

429-
scope := req.Arguments[0]
430-
431-
// set scope limit to new values (when limit.json is passed as a second arg)
432-
if req.Files != nil {
433-
var newLimit rcmgr.ResourceLimits
434-
it := req.Files.Entries()
435-
if it.Next() {
436-
file := files.FileFromEntry(it)
437-
if file == nil {
438-
return errors.New("expected a JSON file")
439-
}
440-
441-
r := io.LimitReader(file, 32*1024*1024) // 32MiB
442-
443-
if err := json.NewDecoder(r).Decode(&newLimit); err != nil {
444-
return fmt.Errorf("decoding JSON as ResourceMgrScopeConfig: %w", err)
364+
return cmds.EmitOnce(res, libp2p.MergeLimitsAndStatsIntoLimitsConfigAndUsage(limitConfig, rapi.Stat()))
365+
},
366+
Encoders: cmds.EncoderMap{
367+
cmds.JSON: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, limitsAndUsage libp2p.LimitsConfigAndUsage) error {
368+
return json.NewEncoder(w).Encode(limitsAndUsage)
369+
}),
370+
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, limitsAndUsage libp2p.LimitsConfigAndUsage) error {
371+
tw := tabwriter.NewWriter(w, 20, 8, 0, '\t', 0)
372+
defer tw.Flush()
373+
374+
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t\n", "Scope", "Limit Name", "Limit Value", "Limit Usage Amount", "Limit Usage Percent")
375+
for _, ri := range libp2p.LimitConfigsToInfo(limitsAndUsage) {
376+
var limit, percentage string
377+
switch ri.LimitValue {
378+
case rcmgr.Unlimited64:
379+
limit = "unlimited"
380+
percentage = "n/a"
381+
case rcmgr.BlockAllLimit64:
382+
limit = "blockAll"
383+
percentage = "n/a"
384+
default:
385+
limit = strconv.FormatInt(int64(ri.LimitValue), 10)
386+
if ri.CurrentUsage == 0 {
387+
percentage = "0%"
388+
} else {
389+
percentage = strconv.FormatFloat(float64(ri.CurrentUsage)/float64(ri.LimitValue)*100, 'f', 1, 64) + "%"
390+
}
445391
}
446-
return libp2p.NetSetLimit(node.ResourceManager, node.Repo, scope, newLimit)
392+
fmt.Fprintf(tw, "%s\t%s\t%s\t%d\t%s\t\n",
393+
ri.ScopeName,
394+
ri.LimitName,
395+
limit,
396+
ri.CurrentUsage,
397+
percentage,
398+
)
447399
}
448-
if err := it.Err(); err != nil {
449-
return fmt.Errorf("error opening limit JSON file: %w", err)
450-
}
451-
}
452-
453-
var result interface{}
454-
switch _, reset := req.Options[swarmResetLimitsOptionName]; {
455-
case reset:
456-
result, err = libp2p.NetResetLimit(node.ResourceManager, node.Repo, scope)
457-
case scope == "all":
458-
result, err = libp2p.NetLimitAll(node.ResourceManager)
459-
default:
460-
// get scope limit
461-
result, err = libp2p.NetLimit(node.ResourceManager, scope)
462-
}
463-
if err != nil {
464-
return err
465-
}
466400

467-
if base, ok := result.(rcmgr.BaseLimit); ok {
468-
result = base.ToResourceLimits()
469-
}
470-
471-
b := new(bytes.Buffer)
472-
enc := json.NewEncoder(b)
473-
err = enc.Encode(result)
474-
if err != nil {
475-
return err
476-
}
477-
return cmds.EmitOnce(res, b)
478-
},
479-
Encoders: cmds.EncoderMap{
480-
cmds.Text: HumanJSONEncoder,
401+
return nil
402+
}),
481403
},
404+
Type: libp2p.LimitsConfigAndUsage{},
482405
}
483406

484407
type streamInfo struct {

0 commit comments

Comments
 (0)