Skip to content

Commit a599737

Browse files
feat: Provider.WorkerCount and stats reprovide (#10779)
* adjust ipfs stats provide * update boxo dep * bump boxo * fixing tests * docs/chore: mark stat reprovide as experimental * docs: Provider.Strategy explicitly document it is not used - without this legacy users will have it in their config and be very confused --------- Co-authored-by: Marcin Rataj <[email protected]>
1 parent 0556508 commit a599737

File tree

14 files changed

+213
-60
lines changed

14 files changed

+213
-60
lines changed

cmd/ipfs/kubo/daemon.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,9 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
485485
// This should never happen, but better safe than sorry
486486
log.Fatal("Private network does not work with Routing.Type=auto. Update your config to Routing.Type=dht (or none, and do manual peering)")
487487
}
488+
if cfg.Provider.Strategy.WithDefault("") != "" && cfg.Reprovider.Strategy.IsDefault() {
489+
log.Fatal("Invalid config. Remove unused Provider.Strategy and set Reprovider.Strategy instead. Documentation: https://github.com/ipfs/kubo/blob/master/docs/config.md#reproviderstrategy")
490+
}
488491

489492
printLibp2pPorts(node)
490493

config/config_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ func TestCheckKey(t *testing.T) {
134134
t.Fatal("Foo.Bar isn't a valid key in the config")
135135
}
136136

137-
err = CheckKey("Provider.Strategy")
137+
err = CheckKey("Reprovider.Strategy")
138138
if err != nil {
139-
t.Fatalf("%s: %s", err, "Provider.Strategy is a valid key in the config")
139+
t.Fatalf("%s: %s", err, "Reprovider.Strategy is a valid key in the config")
140140
}
141141

142142
err = CheckKey("Provider.Foo")

config/provider.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package config
22

3+
const (
4+
DefaultProviderWorkerCount = 64
5+
)
6+
7+
// Provider configuration describes how NEW CIDs are announced the moment they are created.
8+
// For periodical reprovide configuration, see Reprovider.*
39
type Provider struct {
4-
Strategy string // Which keys to announce
10+
Strategy *OptionalString `json:",omitempty"` // Unused, you are likely looking for Reprovider.Strategy instead
11+
WorkerCount *OptionalInteger `json:",omitempty"` // Number of concurrent provides allowed, 0 means unlimited
512
}

config/reprovider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const (
77
DefaultReproviderStrategy = "all"
88
)
99

10+
// Reprovider configuration describes how CID from local datastore are periodically re-announced to routing systems.
11+
// For provide behavior of ad-hoc or newly created CIDs and their first-time announcement, see Provider.*
1012
type Reprovider struct {
1113
Interval *OptionalDuration `json:",omitempty"` // Time period to reprovide locally stored objects to the network
1214
Strategy *OptionalString `json:",omitempty"` // Which keys to announce

core/commands/commands_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ func TestCommands(t *testing.T) {
184184
"/stats/bw",
185185
"/stats/dht",
186186
"/stats/provide",
187+
"/stats/reprovide",
187188
"/stats/repo",
188189
"/swarm",
189190
"/swarm/addrs",

core/commands/stat.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ for your IPFS node.`,
2727
},
2828

2929
Subcommands: map[string]*cmds.Command{
30-
"bw": statBwCmd,
31-
"repo": repoStatCmd,
32-
"bitswap": bitswapStatCmd,
33-
"dht": statDhtCmd,
34-
"provide": statProvideCmd,
30+
"bw": statBwCmd,
31+
"repo": repoStatCmd,
32+
"bitswap": bitswapStatCmd,
33+
"dht": statDhtCmd,
34+
"provide": statProvideCmd,
35+
"reprovide": statReprovideCmd,
3536
},
3637
}
3738

core/commands/stat_provide.go

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,20 @@ import (
44
"fmt"
55
"io"
66
"text/tabwriter"
7-
"time"
87

9-
humanize "github.com/dustin/go-humanize"
10-
"github.com/ipfs/boxo/provider"
118
cmds "github.com/ipfs/go-ipfs-cmds"
129
"github.com/ipfs/kubo/core/commands/cmdenv"
1310
"github.com/libp2p/go-libp2p-kad-dht/fullrt"
14-
"golang.org/x/exp/constraints"
1511
)
1612

17-
type reprovideStats struct {
18-
provider.ReproviderStats
19-
fullRT bool
20-
}
21-
2213
var statProvideCmd = &cmds.Command{
14+
Status: cmds.Deprecated,
2315
Helptext: cmds.HelpText{
24-
Tagline: "Returns statistics about the node's (re)provider system.",
16+
Tagline: "Deprecated command, use 'ipfs stats reprovide' instead.",
2517
ShortDescription: `
26-
Returns statistics about the content the node is advertising.
27-
28-
This interface is not stable and may change from release to release.
18+
'ipfs stats provide' is deprecated because provide and reprovide operations
19+
are now distinct. This command may be replaced by provide only stats in the
20+
future.
2921
`,
3022
},
3123
Arguments: []cmds.Argument{},
@@ -57,8 +49,8 @@ This interface is not stable and may change from release to release.
5749
wtr := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)
5850
defer wtr.Flush()
5951

60-
fmt.Fprintf(wtr, "TotalReprovides:\t%s\n", humanNumber(s.TotalReprovides))
61-
fmt.Fprintf(wtr, "AvgReprovideDuration:\t%s\n", humanDuration(s.AvgReprovideDuration))
52+
fmt.Fprintf(wtr, "TotalProvides:\t%s\n", humanNumber(s.TotalReprovides))
53+
fmt.Fprintf(wtr, "AvgProvideDuration:\t%s\n", humanDuration(s.AvgReprovideDuration))
6254
fmt.Fprintf(wtr, "LastReprovideDuration:\t%s\n", humanDuration(s.LastReprovideDuration))
6355
if !s.LastRun.IsZero() {
6456
fmt.Fprintf(wtr, "LastRun:\t%s\n", humanTime(s.LastRun))
@@ -71,30 +63,3 @@ This interface is not stable and may change from release to release.
7163
},
7264
Type: reprovideStats{},
7365
}
74-
75-
func humanDuration(val time.Duration) string {
76-
return val.Truncate(time.Microsecond).String()
77-
}
78-
79-
func humanTime(val time.Time) string {
80-
return val.Format("2006-01-02 15:04:05")
81-
}
82-
83-
func humanNumber[T constraints.Float | constraints.Integer](n T) string {
84-
nf := float64(n)
85-
str := humanSI(nf, 0)
86-
fullStr := humanFull(nf, 0)
87-
if str != fullStr {
88-
return fmt.Sprintf("%s\t(%s)", str, fullStr)
89-
}
90-
return str
91-
}
92-
93-
func humanSI(val float64, decimals int) string {
94-
v, unit := humanize.ComputeSI(val)
95-
return fmt.Sprintf("%s%s", humanFull(v, decimals), unit)
96-
}
97-
98-
func humanFull(val float64, decimals int) string {
99-
return humanize.CommafWithDigits(val, decimals)
100-
}

core/commands/stat_reprovide.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"text/tabwriter"
7+
"time"
8+
9+
humanize "github.com/dustin/go-humanize"
10+
"github.com/ipfs/boxo/provider"
11+
cmds "github.com/ipfs/go-ipfs-cmds"
12+
"github.com/ipfs/kubo/core/commands/cmdenv"
13+
"github.com/libp2p/go-libp2p-kad-dht/fullrt"
14+
"golang.org/x/exp/constraints"
15+
)
16+
17+
type reprovideStats struct {
18+
provider.ReproviderStats
19+
fullRT bool
20+
}
21+
22+
var statReprovideCmd = &cmds.Command{
23+
Status: cmds.Experimental,
24+
Helptext: cmds.HelpText{
25+
Tagline: "Returns statistics about the node's reprovider system.",
26+
ShortDescription: `
27+
Returns statistics about the content the node is reproviding every
28+
Reprovider.Interval according to Reprovider.Strategy:
29+
https://github.com/ipfs/kubo/blob/master/docs/config.md#reprovider
30+
31+
This interface is not stable and may change from release to release.
32+
33+
`,
34+
},
35+
Arguments: []cmds.Argument{},
36+
Options: []cmds.Option{},
37+
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
38+
nd, err := cmdenv.GetNode(env)
39+
if err != nil {
40+
return err
41+
}
42+
43+
if !nd.IsOnline {
44+
return ErrNotOnline
45+
}
46+
47+
stats, err := nd.Provider.Stat()
48+
if err != nil {
49+
return err
50+
}
51+
_, fullRT := nd.DHTClient.(*fullrt.FullRT)
52+
53+
if err := res.Emit(reprovideStats{stats, fullRT}); err != nil {
54+
return err
55+
}
56+
57+
return nil
58+
},
59+
Encoders: cmds.EncoderMap{
60+
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, s reprovideStats) error {
61+
wtr := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)
62+
defer wtr.Flush()
63+
64+
fmt.Fprintf(wtr, "TotalReprovides:\t%s\n", humanNumber(s.TotalReprovides))
65+
fmt.Fprintf(wtr, "AvgReprovideDuration:\t%s\n", humanDuration(s.AvgReprovideDuration))
66+
fmt.Fprintf(wtr, "LastReprovideDuration:\t%s\n", humanDuration(s.LastReprovideDuration))
67+
if !s.LastRun.IsZero() {
68+
fmt.Fprintf(wtr, "LastReprovide:\t%s\n", humanTime(s.LastRun))
69+
if s.fullRT {
70+
fmt.Fprintf(wtr, "NextReprovide:\t%s\n", humanTime(s.LastRun.Add(s.ReprovideInterval)))
71+
}
72+
}
73+
return nil
74+
}),
75+
},
76+
Type: reprovideStats{},
77+
}
78+
79+
func humanDuration(val time.Duration) string {
80+
return val.Truncate(time.Microsecond).String()
81+
}
82+
83+
func humanTime(val time.Time) string {
84+
return val.Format("2006-01-02 15:04:05")
85+
}
86+
87+
func humanNumber[T constraints.Float | constraints.Integer](n T) string {
88+
nf := float64(n)
89+
str := humanSI(nf, 0)
90+
fullStr := humanFull(nf, 0)
91+
if str != fullStr {
92+
return fmt.Sprintf("%s\t(%s)", str, fullStr)
93+
}
94+
return str
95+
}
96+
97+
func humanSI(val float64, decimals int) string {
98+
v, unit := humanize.ComputeSI(val)
99+
return fmt.Sprintf("%s%s", humanFull(v, decimals), unit)
100+
}
101+
102+
func humanFull(val float64, decimals int) string {
103+
return humanize.CommafWithDigits(val, decimals)
104+
}

core/node/groups.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ func Online(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
359359
cfg.Reprovider.Strategy.WithDefault(config.DefaultReproviderStrategy),
360360
cfg.Reprovider.Interval.WithDefault(config.DefaultReproviderInterval),
361361
cfg.Routing.AcceleratedDHTClient.WithDefault(config.DefaultAcceleratedDHTClient),
362+
int(cfg.Provider.WorkerCount.WithDefault(config.DefaultProviderWorkerCount)),
362363
),
363364
)
364365
}

core/node/provider.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ import (
2121
// and in 'ipfs stats provide' report.
2222
const sampledBatchSize = 1000
2323

24-
func ProviderSys(reprovideInterval time.Duration, acceleratedDHTClient bool) fx.Option {
24+
func ProviderSys(reprovideInterval time.Duration, acceleratedDHTClient bool, provideWorkerCount int) fx.Option {
2525
return fx.Provide(func(lc fx.Lifecycle, cr irouting.ProvideManyRouter, keyProvider provider.KeyChanFunc, repo repo.Repo, bs blockstore.Blockstore) (provider.System, error) {
2626
opts := []provider.Option{
2727
provider.Online(cr),
2828
provider.ReproviderInterval(reprovideInterval),
2929
provider.KeyProvider(keyProvider),
30+
provider.ProvideWorkerCount(provideWorkerCount),
3031
}
3132
if !acceleratedDHTClient && reprovideInterval > 0 {
3233
// The estimation kinda suck if you are running with accelerated DHT client,
@@ -131,7 +132,7 @@ https://github.com/ipfs/kubo/blob/master/docs/config.md#routingaccelerateddhtcli
131132
// ONLINE/OFFLINE
132133

133134
// OnlineProviders groups units managing provider routing records online
134-
func OnlineProviders(useStrategicProviding bool, reprovideStrategy string, reprovideInterval time.Duration, acceleratedDHTClient bool) fx.Option {
135+
func OnlineProviders(useStrategicProviding bool, reprovideStrategy string, reprovideInterval time.Duration, acceleratedDHTClient bool, provideWorkerCount int) fx.Option {
135136
if useStrategicProviding {
136137
return OfflineProviders()
137138
}
@@ -146,7 +147,7 @@ func OnlineProviders(useStrategicProviding bool, reprovideStrategy string, repro
146147

147148
return fx.Options(
148149
keyProvider,
149-
ProviderSys(reprovideInterval, acceleratedDHTClient),
150+
ProviderSys(reprovideInterval, acceleratedDHTClient, provideWorkerCount),
150151
)
151152
}
152153

@@ -169,7 +170,6 @@ func mfsProvider(mfsRoot *mfs.Root, fetcher fetcher.Factory) provider.KeyChanFun
169170
kcf := provider.NewDAGProvider(rootNode.Cid(), fetcher)
170171
return kcf(ctx)
171172
}
172-
173173
}
174174

175175
func mfsRootProvider(mfsRoot *mfs.Root) provider.KeyChanFunc {

0 commit comments

Comments
 (0)