feat: enable DHT Provide Sweep by default (#10955)
Co-authored-by: Marcin Rataj <lidel@lidel.org> Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com>
Guillaume Michel committed
Nov 12, 2025 at 23:55 UTC
702c63b6dbc1cd5abd70d9083520d4f5bc8c623f
19 files changed
+192
-116
config/provide.go
+2
-2
@@ -15,7 +15,7 @@ const (
15
// DHT provider defaults
16
DefaultProvideDHTInterval = 22 * time.Hour // https://github.com/ipfs/kubo/pull/9326
17
DefaultProvideDHTMaxWorkers = 16 // Unified default for both sweep and legacy providers
18
- DefaultProvideDHTSweepEnabled = false
18
+ DefaultProvideDHTSweepEnabled = true
19
DefaultProvideDHTResumeEnabled = true
20
DefaultProvideDHTDedicatedPeriodicWorkers = 2
21
DefaultProvideDHTDedicatedBurstWorkers = 1
@@ -64,7 +64,7 @@ type ProvideDHT struct {
64
MaxWorkers *OptionalInteger `json:",omitempty"`
65
66
// SweepEnabled activates the sweeping reprovider system which spreads
67
- // reprovide operations over time. This will become the default in a future release.
67
+ // reprovide operations over time.
68
// Default: DefaultProvideDHTSweepEnabled
69
SweepEnabled Flag `json:",omitempty"`
70
core/coreiface/tests/routing.go
+21
-8
@@ -240,14 +240,27 @@ func (tp *TestSuite) TestRoutingProvide(t *testing.T) {
240
t.Fatal(err)
241
}
242
243
- out, err = apis[2].Routing().FindProviders(ctx, p, options.Routing.NumProviders(1))
244
- if err != nil {
245
- t.Fatal(err)
246
- }
247
-
248
- provider := <-out
243
+ maxAttempts := 5
244
+ success := false
245
+ for range maxAttempts {
246
+ // We may need to try again as Provide() doesn't block until the CID is
247
+ // actually provided.
248
+ out, err = apis[2].Routing().FindProviders(ctx, p, options.Routing.NumProviders(1))
249
+ if err != nil {
250
+ t.Fatal(err)
251
+ }
252
+ provider := <-out
253
250
- if provider.ID.String() != self0.ID().String() {
251
- t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String())
254
+ if provider.ID.String() == self0.ID().String() {
255
+ success = true
256
+ break
257
+ }
258
+ if len(provider.ID.String()) > 0 {
259
+ t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String())
260
+ }
261
+ time.Sleep(time.Second)
262
+ }
263
+ if !success {
264
+ t.Errorf("missing provider after %d attempts", maxAttempts)
265
}
266
}
core/node/provider.go
+38
-2
@@ -116,6 +116,7 @@ type DHTProvider interface {
116
// `OfflineDelay`). The schedule depends on the network size, hence recent
117
// network connectivity is essential.
118
RefreshSchedule() error
119
+ Close() error
120
}
121
122
var (
@@ -134,6 +135,7 @@ func (r *NoopProvider) StartProviding(bool, ...mh.Multihash) error { return nil
135
func (r *NoopProvider) ProvideOnce(...mh.Multihash) error { return nil }
136
func (r *NoopProvider) Clear() int { return 0 }
137
func (r *NoopProvider) RefreshSchedule() error { return nil }
138
+func (r *NoopProvider) Close() error { return nil }
139
140
// LegacyProvider is a wrapper around the boxo/provider.System that implements
141
// the DHTProvider interface. This provider manages reprovides using a burst
@@ -523,8 +525,41 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
525
case <-ctx.Done():
526
return ctx.Err()
527
}
526
- // Keystore data isn't purged, on close, but it will be overwritten
527
- // when the node starts again.
528
+ // Keystore will be closed by ensureProviderClosesBeforeKeystore hook
529
+ // to guarantee provider closes before keystore.
530
+ return nil
531
+ },
532
+ })
533
+ })
534
+
535
+ // ensureProviderClosesBeforeKeystore manages the shutdown order between
536
+ // provider and keystore to prevent race conditions.
537
+ //
538
+ // The provider's worker goroutines may call keystore methods during their
539
+ // operation. If keystore closes while these operations are in-flight, we get
540
+ // "keystore is closed" errors. By closing the provider first, we ensure all
541
+ // worker goroutines exit and complete any pending keystore operations before
542
+ // the keystore itself closes.
543
+ type providerKeystoreShutdownInput struct {
544
+ fx.In
545
+ Provider DHTProvider
546
+ Keystore *keystore.ResettableKeystore
547
+ }
548
+ ensureProviderClosesBeforeKeystore := fx.Invoke(func(lc fx.Lifecycle, in providerKeystoreShutdownInput) {
549
+ // Skip for NoopProvider
550
+ if _, ok := in.Provider.(*NoopProvider); ok {
551
+ return
552
+ }
553
+
554
+ lc.Append(fx.Hook{
555
+ OnStop: func(ctx context.Context) error {
556
+ // Close provider first - waits for all worker goroutines to exit.
557
+ // This ensures no code can access keystore after this returns.
558
+ if err := in.Provider.Close(); err != nil {
559
+ logger.Errorw("error closing provider during shutdown", "error", err)
560
+ }
561
+
562
+ // Close keystore - safe now, provider is fully shut down
563
return in.Keystore.Close()
564
},
565
})
@@ -650,6 +685,7 @@ See docs: https://github.com/ipfs/kubo/blob/master/docs/config.md#providedhtmaxw
685
return fx.Options(
686
sweepingReprovider,
687
initKeystore,
688
+ ensureProviderClosesBeforeKeystore,
689
reprovideAlert,
690
)
691
}
docs/changelogs/v0.38.md
+6
@@ -59,6 +59,9 @@ A new experimental DHT provider is available as an alternative to both the defau
59
60
**Monitoring and debugging:** Legacy mode (`SweepEnabled=false`) tracks `provider_reprovider_provide_count` and `provider_reprovider_reprovide_count`, while sweep mode (`SweepEnabled=true`) tracks `total_provide_count_total`. Enable debug logging with `GOLOG_LOG_LEVEL=error,provider=debug,dht/provider=debug` to see detailed logs from either system.
61
62
+> [!IMPORTANT]
63
+> The metric `total_provide_count_total` was renamed to `provider_provides_total` in Kubo v0.39 to follow OpenTelemetry naming conventions. If you have dashboards or alerts monitoring this metric, update them accordingly.
64
+
65
> [!NOTE]
66
> This feature is experimental and opt-in. In the future, it will become the default and replace the legacy system. Some commands like `ipfs stats provide` and `ipfs routing provide` are not yet available with sweep mode. Run `ipfs provide --help` for alternatives.
67
@@ -68,6 +71,9 @@ For configuration details, see [`Provide.DHT`](https://github.com/ipfs/kubo/blob
71
72
Kubo now exposes DHT metrics from [go-libp2p-kad-dht](https://github.com/libp2p/go-libp2p-kad-dht/), including `total_provide_count_total` for sweep provider operations and RPC metrics prefixed with `rpc_inbound_` and `rpc_outbound_` for DHT message traffic. See [Kubo metrics documentation](https://github.com/ipfs/kubo/blob/master/docs/metrics.md) for details.
73
74
+> [!IMPORTANT]
75
+> The metric `total_provide_count_total` was renamed to `provider_provides_total` in Kubo v0.39 to follow OpenTelemetry naming conventions. If you have dashboards or alerts monitoring this metric, update them accordingly.
76
+
77
#### 🚨 Improved gateway error pages with diagnostic tools
78
79
Gateway error pages now provide more actionable information during content retrieval failures. When a 504 Gateway Timeout occurs, users see detailed retrieval state information including which phase failed and a sample of providers that were attempted:
docs/changelogs/v0.39.md
+54
-67
@@ -10,11 +10,14 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team.
10
11
- [Overview](#overview)
12
- [🔦 Highlights](#-highlights)
13
+ - [🎯 Amino DHT Sweep provider is now the default](#-amino-dht-sweep-provider-is-now-the-default)
14
- [📊 Detailed statistics for Sweep provider with `ipfs provide stat`](#-detailed-statistics-for-sweep-provider-with-ipfs-provide-stat)
15
- [⏯️ Provider resume cycle for improved reproviding reliability](#provider-resume-cycle-for-improved-reproviding-reliability)
16
- [🔔 Sweep provider slow reprovide warnings](#-sweep-provider-slow-reprovide-warnings)
17
+ - [📊 Metric rename: `provider_provides_total`](#-metric-rename-provider_provides_total)
18
- [🔧 Fixed UPnP port forwarding after router restarts](#-fixed-upnp-port-forwarding-after-router-restarts)
19
- [🖥️ RISC-V support with prebuilt binaries](#️-risc-v-support-with-prebuilt-binaries)
20
+ - [🚦 Gateway range request limits for CDN compatibility](#-gateway-range-request-limits-for-cdn-compatibility)
21
- [🪦 Deprecated `go-ipfs` name no longer published](#-deprecated-go-ipfs-name-no-longer-published)
22
- [📦️ Important dependency updates](#-important-dependency-updates)
23
- [📝 Changelog](#-changelog)
@@ -22,77 +25,54 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team.
25
26
### Overview
27
28
+Kubo 0.39.0 graduates the experimental sweep provider to default, bringing efficient content announcement to all nodes. This release adds detailed provider statistics, automatic state persistence for reliable reproviding after restarts, and proactive monitoring alerts for identifying issues early. It also includes important fixes for UPnP port forwarding, RISC-V prebuilt binaries, and finalizes the deprecation of the legacy go-ipfs name.
29
+
30
### 🔦 Highlights
31
27
-#### 🚦 Gateway range request limits for CDN compatibility
32
+#### 🎯 Amino DHT Sweep provider is now the default
33
29
-The new [`Gateway.MaxRangeRequestFileSize`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewaymaxrangerequestfilesize) configuration protects against CDN bugs where range requests over a certain size are silently ignored and the entire file is returned instead ([boxo#856](https://github.com/ipfs/boxo/issues/856#issuecomment-2786431369)). This causes unexpected bandwidth costs for both gateway operators and clients who only wanted a small byte range.
34
+The Amino DHT Sweep provider system, introduced as experimental in v0.38, is now enabled by default (`Provide.DHT.SweepEnabled=true`).
35
31
-Set this to your CDN's range request limit (e.g., `"5GiB"` for Cloudflare's default plan) to return 501 Not Implemented for oversized range requests, with an error message suggesting verifiable block requests as an alternative.
32
-#### 📊 Detailed statistics for Sweep provider with `ipfs provide stat`
36
+**What this means:** All nodes now benefit from efficient keyspace-sweeping content announcements that reduce memory overhead and create predictable network patterns, especially for nodes providing large content collections.
37
+
38
+**Migration:** The transition is automatic on upgrade. Your existing configuration is preserved:
39
34
-The experimental Sweep provider system ([introduced in
35
-v0.38](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.38.md#-experimental-sweeping-dht-provider))
36
-now has detailed statistics available through `ipfs provide stat`.
40
+- If you explicitly set `Provide.DHT.SweepEnabled=false` in v0.38, you'll continue using the legacy provider
41
+- If you were using the default settings, you'll automatically get the sweep provider
42
+- To opt out and return to legacy behavior: `ipfs config --json Provide.DHT.SweepEnabled false`
43
38
-These statistics help you monitor provider health and troubleshoot issues,
39
-especially useful for nodes providing large content collections. You can quickly
40
-identify bottlenecks like queue backlog, worker saturation, or connectivity
41
-problems that might prevent content from being announced to the DHT.
44
+**New features available with sweep mode:**
45
43
-**Default behavior:** Displays a brief summary showing queue sizes, scheduled
44
-CIDs/regions, average record holders, ongoing/total provides, and worker status
45
-when resources are constrained.
46
+- Detailed statistics via `ipfs provide stat` ([see below](#-detailed-statistics-for-sweep-provider-with-ipfs-provide-stat))
47
+- Automatic resume after restarts with persistent state ([see below](#provider-resume-cycle-for-improved-reproviding-reliability))
48
+- Proactive alerts when reproviding falls behind ([see below](#-sweep-provider-slow-reprovide-warnings))
49
+- Better metrics for monitoring (`provider_provides_total`) ([see below](#-metric-rename-provider_provides_total))
50
47
-**Detailed statistics with `--all`:** View complete metrics organized into sections:
51
+For background on the sweep provider design and motivations, see [`Provide.DHT.SweepEnabled`](https://github.com/ipfs/kubo/blob/master/docs/config.md#providedhtsweepenabled) and [ipshipyard.com#8](https://github.com/ipshipyard/ipshipyard.com/pull/8).
52
49
-- **Connectivity**: DHT connection status
50
-- **Queues**: Pending provide and reprovide operations
51
-- **Schedule**: CIDs/regions scheduled for reprovide
52
-- **Timings**: Uptime, reprovide cycle information
53
-- **Network**: Peer statistics, keyspace region sizes
54
-- **Operations**: Ongoing and past provides, rates, errors
55
-- **Workers**: Worker pool utilization and availability
53
+#### 📊 Detailed statistics for Sweep provider with `ipfs provide stat`
54
+
55
+The Sweep provider system now exposes detailed statistics through `ipfs provide stat`, helping you monitor provider health and troubleshoot issues.
56
57
-**Real-time monitoring:** For continuous monitoring, run
58
-`watch ipfs provide stat --all --compact` to see detailed statistics refreshed
59
-in a 2-column layout. This lets you observe provide rates, queue sizes, and
60
-worker availability in real-time. Individual sections can be displayed using
61
-flags like `--network`, `--operations`, or `--workers`, and multiple flags can
62
-be combined for custom views.
57
+Run `ipfs provide stat` for a quick summary, or use `--all` to see complete metrics including connectivity status, queue sizes, reprovide schedules, network statistics, operation rates, and worker utilization. For real-time monitoring, use `watch ipfs provide stat --all --compact` to observe changes in a 2-column layout. Individual sections can be displayed with flags like `--network`, `--operations`, or `--workers`.
58
64
-**Dual DHT support:** For Dual DHT configurations, use `--lan` to view LAN DHT
65
-provider statistics instead of the default WAN DHT stats.
59
+For Dual DHT configurations, use `--lan` to view LAN DHT statistics instead of the default WAN DHT stats.
60
+
61
+For more information, run `ipfs provide stat --help` or see the [Provide Stats documentation](https://github.com/ipfs/kubo/blob/master/docs/provide-stats.md).
62
63
> [!NOTE]
68
-> These statistics are only available when using the Sweep provider system
69
-> (enabled via
70
-> [`Provide.DHT.SweepEnabled`](https://github.com/ipfs/kubo/blob/master/docs/config.md#providedhtsweepenabled)).
71
-> Legacy provider shows basic statistics without flag support.
64
+> Legacy provider (when `Provide.DHT.SweepEnabled=false`) shows basic statistics without flag support.
65
66
#### ⏯️ Provider resume cycle for improved reproviding reliability
67
75
-When using the sweeping provider (`Provide.DHT.SweepEnabled`), Kubo now
76
-persists the reprovide cycle state and automatically resumes where it left off
77
-after a restart. This brings several improvements:
78
-
79
-- **Persistent progress**: The provider now saves its position in the reprovide
80
-cycle to the datastore. On restart, it continues from where it stopped instead
81
-of starting from scratch.
82
-- **Catch-up reproviding**: If the node was offline for an extended period, all
83
-CIDs that haven't been reprovided within the configured reprovide interval are
84
-immediately queued for reproviding when the node starts up. This ensures
85
-content availability is maintained even after downtime.
86
-- **Persistent provide queue**: The provide queue is now persisted to the
87
-datastore on shutdown. When the node restarts, queued CIDs are restored and
88
-provided as expected, preventing loss of pending provide operations.
89
-- **Resume control**: The resume behavior is now controlled via the
90
-`Provide.DHT.ResumeEnabled` config option (default: `true`). If you don't want
91
-to keep the persisted provider state from a previous run, you can set
92
-`Provide.DHT.ResumeEnabled=false` in your config.
93
-
94
-This feature significantly improves the reliability of content providing,
95
-especially for nodes that experience intermittent connectivity or restarts.
68
+The Sweep provider now persists the reprovide cycle state and automatically resumes where it left off after a restart. This brings several improvements:
69
+
70
+- **Persistent progress**: The provider saves its position in the reprovide cycle to the datastore. On restart, it continues from where it stopped instead of starting from scratch.
71
+- **Catch-up reproviding**: If the node was offline for an extended period, all CIDs that haven't been reprovided within the configured reprovide interval are immediately queued for reproviding when the node starts up. This ensures content availability is maintained even after downtime.
72
+- **Persistent provide queue**: The provide queue is persisted to the datastore on shutdown. When the node restarts, queued CIDs are restored and provided as expected, preventing loss of pending provide operations.
73
+- **Resume control**: The resume behavior is controlled via [`Provide.DHT.ResumeEnabled`](https://github.com/ipfs/kubo/blob/master/docs/config.md#providedhtresumeenabled) (default: `true`). Set to `false` if you don't want to keep the persisted provider state from a previous run.
74
+
75
+This feature improves reliability for nodes that experience intermittent connectivity or restarts.
76
77
#### 🔔 Sweep provider slow reprovide warnings
78
@@ -110,6 +90,12 @@ The alert polls every 15 minutes (to avoid alert fatigue while catching
90
persistent issues) and only triggers after sustained growth across multiple
91
intervals. The legacy provider is unaffected by this change.
92
93
+#### 📊 Metric rename: `provider_provides_total`
94
+
95
+The Amino DHT Sweep provider metric has been renamed from `total_provide_count_total` to `provider_provides_total` to follow OpenTelemetry naming conventions and maintain consistency with other kad-dht metrics (which use dot notation like `rpc.inbound.messages`, `rpc.outbound.requests`, etc.).
96
+
97
+**Migration:** If you have Prometheus queries, dashboards, or alerts monitoring the old `total_provide_count_total` metric, update them to use `provider_provides_total` instead. This affects all nodes using sweep mode, which is now the default in v0.39 (previously opt-in experimental in v0.38).
98
+
99
#### 🔧 Fixed UPnP port forwarding after router restarts
100
101
Kubo now automatically recovers UPnP port mappings when routers restart or
@@ -136,26 +122,27 @@ using UPnP for NAT traversal.
122
123
#### 🖥️ RISC-V support with prebuilt binaries
124
139
-Kubo now provides official `linux-riscv64` prebuilt binaries with every release,
140
-bringing IPFS to [RISC-V](https://en.wikipedia.org/wiki/RISC-V) open hardware.
125
+Kubo provides official `linux-riscv64` prebuilt binaries, bringing IPFS to [RISC-V](https://en.wikipedia.org/wiki/RISC-V) open hardware.
126
+
127
+As RISC-V single-board computers and embedded systems become more accessible, the distributed web is now supported on open hardware architectures - a natural pairing of open technologies.
128
+
129
+Download from <https://dist.ipfs.tech/kubo/> or <https://github.com/ipfs/kubo/releases> and look for the `linux-riscv64` archive.
130
+
131
+#### 🚦 Gateway range request limits for CDN compatibility
132
142
-As RISC-V single-board computers and embedded systems become more accessible,
143
-it's good to see the distributed web supported on open hardware architectures -
144
-a natural pairing of open technologies.
133
+The new [`Gateway.MaxRangeRequestFileSize`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewaymaxrangerequestfilesize) configuration protects against CDN range request limitations that cause bandwidth overcharges on deserialized responses. Some CDNs convert range requests over large files into full file downloads, causing clients requesting small byte ranges to unknowingly download entire multi-gigabyte files.
134
146
-Download from <https://dist.ipfs.tech/kubo/> or
147
-<https://github.com/ipfs/kubo/releases> and look for the `linux-riscv64` archive.
135
+This only impacts deserialized responses. Clients using verifiable block requests (`application/vnd.ipld.raw`) are not affected. See the [configuration documentation](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewaymaxrangerequestfilesize) for details.
136
137
#### 🪦 Deprecated `go-ipfs` name no longer published
138
151
-The `go-ipfs` name was deprecated in 2022 and renamed to `kubo`. Starting with this release, we have stopped publishing Docker images and distribution binaries under the old `go-ipfs` name.
139
+The `go-ipfs` name was deprecated in 2022 and renamed to `kubo`. Starting with this release, the legacy Docker image name has been replaced with a stub that displays an error message directing users to switch to `ipfs/kubo`.
140
153
-Existing users should switch to:
141
+**Docker images:** The `ipfs/go-ipfs` image tags now contain only a stub script that exits with an error, instructing users to update their Docker configurations to use [`ipfs/kubo`](https://hub.docker.com/r/ipfs/kubo) instead. This ensures users are aware of the deprecation while allowing existing automation to fail explicitly rather than silently using outdated images.
142
155
-- Docker: `ipfs/kubo` image (instead of `ipfs/go-ipfs`)
156
-- Binaries: download from <https://dist.ipfs.tech/kubo/> or <https://github.com/ipfs/kubo/releases>
143
+**Distribution binaries:** Download Kubo from <https://dist.ipfs.tech/kubo/> or <https://github.com/ipfs/kubo/releases>. The legacy `go-ipfs` distribution path should no longer be used.
144
158
-For Docker users, the legacy `ipfs/go-ipfs` image name now shows a deprecation notice directing you to `ipfs/kubo`.
145
+All users should migrate to the `kubo` name in their scripts and configurations.
146
147
### 📦️ Important dependency updates
148
docs/config.md
+14
-6
@@ -1162,11 +1162,20 @@ Type: `optionalDuration`
1162
1163
### `Gateway.MaxRangeRequestFileSize`
1164
1165
-Maximum file size for HTTP range requests. Range requests for files larger than this limit return 501 Not Implemented.
1165
+Maximum file size for HTTP range requests on deserialized responses. Range requests for files larger than this limit return 501 Not Implemented.
1166
1167
-Protects against CDN bugs where range requests are silently ignored and the entire file is returned instead. For example, Cloudflare's default plan returns the full file for range requests over 5GiB, causing unexpected bandwidth costs for both gateway operators and clients who only wanted a small byte range.
1167
+**Why this exists:**
1168
1169
-Set this to your CDN's range request limit (e.g., `"5GiB"` for Cloudflare's default plan). The error response suggests using verifiable block requests (application/vnd.ipld.raw) as an alternative.
1169
+Some CDNs like Cloudflare intercept HTTP range requests and convert them to full file downloads when files exceed their cache bucket limits. Cloudflare's default plan only caches range requests for files up to 5GiB. Files larger than this receive HTTP 200 with the entire file instead of HTTP 206 with the requested byte range. A client requesting 1MB from a 40GiB file would unknowingly download all 40GiB, causing bandwidth overcharges for the gateway operator, unexpected data costs for the client, and potential browser crashes.
1170
+
1171
+This only affects deserialized responses. Clients fetching verifiable blocks as `application/vnd.ipld.raw` are not impacted because they work with small chunks that stay well below CDN cache limits.
1172
+
1173
+**How to use:**
1174
+
1175
+Set this to your CDN's range request cache limit (e.g., `"5GiB"` for Cloudflare's default plan). The gateway returns 501 Not Implemented for range requests over files larger than this limit, with an error message suggesting verifiable block requests as an alternative.
1176
+
1177
+> [!NOTE]
1178
+> Cloudflare users running open gateway hosting deserialized responses should deploy additional protection via Cloudflare Snippets (requires Enterprise plan). The Kubo configuration alone is not sufficient because Cloudflare has already intercepted and cached the response by the time it reaches your origin. See [boxo#856](https://github.com/ipfs/boxo/issues/856#issuecomment-3523944976) for a snippet that aborts HTTP 200 responses when Content-Length exceeds the limit.
1179
1180
Default: `0` (no limit)
1181
@@ -2181,10 +2190,9 @@ to `false`.
2190
You can compare the effectiveness of sweep mode vs legacy mode by monitoring the appropriate metrics (see [Monitoring Provide Operations](#monitoring-provide-operations) above).
2191
2192
> [!NOTE]
2184
-> This feature is opt-in for now, but will become the default in a future release.
2185
-> Eventually, this configuration flag will be removed once the feature is stable.
2193
+> This is the default provider system as of Kubo v0.39. To use the legacy provider instead, set `Provide.DHT.SweepEnabled=false`.
2194
2187
-Default: `false`
2195
+Default: `true`
2196
2197
Type: `flag`
2198
docs/examples/kubo-as-a-library/go.mod
+2
-2
@@ -115,7 +115,7 @@ require (
115
github.com/libp2p/go-doh-resolver v0.5.0 // indirect
116
github.com/libp2p/go-flow-metrics v0.3.0 // indirect
117
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
118
- github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32 // indirect
118
+ github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb // indirect
119
github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect
120
github.com/libp2p/go-libp2p-pubsub v0.14.2 // indirect
121
github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect
@@ -171,7 +171,7 @@ require (
171
github.com/pion/webrtc/v4 v4.1.2 // indirect
172
github.com/pkg/errors v0.9.1 // indirect
173
github.com/polydawn/refmt v0.89.0 // indirect
174
- github.com/probe-lab/go-libdht v0.3.0 // indirect
174
+ github.com/probe-lab/go-libdht v0.4.0 // indirect
175
github.com/prometheus/client_golang v1.23.2 // indirect
176
github.com/prometheus/client_model v0.6.2 // indirect
177
github.com/prometheus/common v0.66.1 // indirect
docs/examples/kubo-as-a-library/go.sum
+4
-4
@@ -430,8 +430,8 @@ github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl9
430
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
431
github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g=
432
github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw=
433
-github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32 h1:xZj18PsLD157snR/BFo547jwOkGDH7jZjMEkBDOoD4Q=
434
-github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32/go.mod h1:aHMTg23iseX9grGSfA5gFUzLrqzmYbA8PqgGPqM8VkI=
433
+github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb h1:jOWsCSRZKnRgocz4Ocu25Yigh5ZUkar2zWt/bzBh43Q=
434
+github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb/go.mod h1:WIysu8hNWQN8t73dKyTNqiZdcYKRrGFl4wjzX4Gz6pQ=
435
github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio=
436
github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s=
437
github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4=
@@ -630,8 +630,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
630
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
631
github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4=
632
github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
633
-github.com/probe-lab/go-libdht v0.3.0 h1:Q3ZXK8wCjZvgeHSTtRrppXobXY/KHPLZJfc+cdTTyqA=
634
-github.com/probe-lab/go-libdht v0.3.0/go.mod h1:hamw22kI6YkPQFGy5P6BrWWDrgE9ety5Si8iWAyuDvc=
633
+github.com/probe-lab/go-libdht v0.4.0 h1:LAqHuko/owRW6+0cs5wmJXbHzg09EUMJEh5DI37yXqo=
634
+github.com/probe-lab/go-libdht v0.4.0/go.mod h1:hamw22kI6YkPQFGy5P6BrWWDrgE9ety5Si8iWAyuDvc=
635
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
636
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
637
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
docs/metrics.md
+1
-1
@@ -59,7 +59,7 @@ Metrics for the legacy provider system when `Provide.DHT.SweepEnabled=false`:
59
60
Metrics for the DHT provider system when `Provide.DHT.SweepEnabled=true`:
61
62
-- `total_provide_count_total` - Counter: total successful provide operations since node startup (includes both one-time provides and periodic provides done on `Provide.DHT.Interval`)
62
+- `provider_provides_total` - Counter: total successful provide operations since node startup (includes both one-time provides and periodic provides done on `Provide.DHT.Interval`)
63
64
> [!NOTE]
65
> These metrics are exposed by [go-libp2p-kad-dht](https://github.com/libp2p/go-libp2p-kad-dht/). You can enable debug logging for DHT provider activity with `GOLOG_LOG_LEVEL=dht/provider=debug`.
go.mod
+2
-2
@@ -53,7 +53,7 @@ require (
53
github.com/libp2p/go-doh-resolver v0.5.0
54
github.com/libp2p/go-libp2p v0.45.0
55
github.com/libp2p/go-libp2p-http v0.5.0
56
- github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32
56
+ github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb
57
github.com/libp2p/go-libp2p-kbucket v0.8.0
58
github.com/libp2p/go-libp2p-pubsub v0.14.2
59
github.com/libp2p/go-libp2p-pubsub-router v0.6.0
@@ -69,7 +69,7 @@ require (
69
github.com/multiformats/go-multihash v0.2.3
70
github.com/opentracing/opentracing-go v1.2.0
71
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
72
- github.com/probe-lab/go-libdht v0.3.0
72
+ github.com/probe-lab/go-libdht v0.4.0
73
github.com/prometheus/client_golang v1.23.2
74
github.com/stretchr/testify v1.11.1
75
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
go.sum
+4
-4
@@ -514,8 +514,8 @@ github.com/libp2p/go-libp2p-gostream v0.6.0 h1:QfAiWeQRce6pqnYfmIVWJFXNdDyfiR/qk
514
github.com/libp2p/go-libp2p-gostream v0.6.0/go.mod h1:Nywu0gYZwfj7Jc91PQvbGU8dIpqbQQkjWgDuOrFaRdA=
515
github.com/libp2p/go-libp2p-http v0.5.0 h1:+x0AbLaUuLBArHubbbNRTsgWz0RjNTy6DJLOxQ3/QBc=
516
github.com/libp2p/go-libp2p-http v0.5.0/go.mod h1:glh87nZ35XCQyFsdzZps6+F4HYI6DctVFY5u1fehwSg=
517
-github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32 h1:xZj18PsLD157snR/BFo547jwOkGDH7jZjMEkBDOoD4Q=
518
-github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32/go.mod h1:aHMTg23iseX9grGSfA5gFUzLrqzmYbA8PqgGPqM8VkI=
517
+github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb h1:jOWsCSRZKnRgocz4Ocu25Yigh5ZUkar2zWt/bzBh43Q=
518
+github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb/go.mod h1:WIysu8hNWQN8t73dKyTNqiZdcYKRrGFl4wjzX4Gz6pQ=
519
github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio=
520
github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s=
521
github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4=
@@ -732,8 +732,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
732
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
733
github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4=
734
github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
735
-github.com/probe-lab/go-libdht v0.3.0 h1:Q3ZXK8wCjZvgeHSTtRrppXobXY/KHPLZJfc+cdTTyqA=
736
-github.com/probe-lab/go-libdht v0.3.0/go.mod h1:hamw22kI6YkPQFGy5P6BrWWDrgE9ety5Si8iWAyuDvc=
735
+github.com/probe-lab/go-libdht v0.4.0 h1:LAqHuko/owRW6+0cs5wmJXbHzg09EUMJEh5DI37yXqo=
736
+github.com/probe-lab/go-libdht v0.4.0/go.mod h1:hamw22kI6YkPQFGy5P6BrWWDrgE9ety5Si8iWAyuDvc=
737
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
738
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
739
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
test/cli/delegated_routing_v1_http_proxy_test.go
+3
-3
@@ -72,9 +72,9 @@ func TestRoutingV1Proxy(t *testing.T) {
72
73
cidStr := nodes[0].IPFSAddStr(string(random.Bytes(1000)))
74
// Reprovide as initialProviderDelay still ongoing
75
- res := nodes[0].IPFS("routing", "reprovide")
76
- require.NoError(t, res.Err)
77
- res = nodes[1].IPFS("routing", "findprovs", cidStr)
75
+ waitUntilProvidesComplete(t, nodes[0])
76
+
77
+ res := nodes[1].IPFS("routing", "findprovs", cidStr)
78
assert.Equal(t, nodes[0].PeerID().String(), res.Stdout.Trimmed())
79
})
80
test/cli/delegated_routing_v1_http_server_test.go
+1
-4
@@ -14,7 +14,6 @@ import (
14
"github.com/ipfs/kubo/test/cli/harness"
15
"github.com/libp2p/go-libp2p/core/peer"
16
"github.com/stretchr/testify/assert"
17
- "github.com/stretchr/testify/require"
17
)
18
19
func TestRoutingV1Server(t *testing.T) {
@@ -39,9 +38,7 @@ func TestRoutingV1Server(t *testing.T) {
38
text := "hello world " + uuid.New().String()
39
cidStr := nodes[2].IPFSAddStr(text)
40
_ = nodes[3].IPFSAddStr(text)
42
- // Reprovide as initialProviderDelay still ongoing
43
- res := nodes[3].IPFS("routing", "reprovide")
44
- require.NoError(t, res.Err)
41
+ waitUntilProvidesComplete(t, nodes[3])
42
43
cid, err := cid.Decode(cidStr)
44
assert.NoError(t, err)
test/cli/dht_opt_prov_test.go
+2
@@ -17,6 +17,8 @@ func TestDHTOptimisticProvide(t *testing.T) {
17
18
nodes[0].UpdateConfig(func(cfg *config.Config) {
19
cfg.Experimental.OptimisticProvide = true
20
+ // Optimistic provide only works with the legacy provider.
21
+ cfg.Provide.DHT.SweepEnabled = config.False
22
})
23
24
nodes.StartDaemons().Connect()
test/cli/routing_dht_test.go
+32
-4
@@ -2,7 +2,10 @@ package cli
2
3
import (
4
"fmt"
5
+ "strconv"
6
+ "strings"
7
"testing"
8
+ "time"
9
10
"github.com/ipfs/kubo/test/cli/harness"
11
"github.com/ipfs/kubo/test/cli/testutils"
@@ -10,6 +13,33 @@ import (
13
"github.com/stretchr/testify/require"
14
)
15
16
+func waitUntilProvidesComplete(t *testing.T, n *harness.Node) {
17
+ getCidsCount := func(line string) int {
18
+ trimmed := strings.TrimSpace(line)
19
+ countStr := strings.SplitN(trimmed, " ", 2)[0]
20
+ count, err := strconv.Atoi(countStr)
21
+ require.NoError(t, err)
22
+ return count
23
+ }
24
+
25
+ queuedProvides, ongoingProvides := true, true
26
+ for queuedProvides || ongoingProvides {
27
+ res := n.IPFS("provide", "stat", "-a")
28
+ require.NoError(t, res.Err)
29
+ for _, line := range res.Stdout.Lines() {
30
+ if trimmed, ok := strings.CutPrefix(line, " Provide queue:"); ok {
31
+ provideQueueSize := getCidsCount(trimmed)
32
+ queuedProvides = provideQueueSize > 0
33
+ }
34
+ if trimmed, ok := strings.CutPrefix(line, " Ongoing provides:"); ok {
35
+ ongoingProvideCount := getCidsCount(trimmed)
36
+ ongoingProvides = ongoingProvideCount > 0
37
+ }
38
+ }
39
+ time.Sleep(10 * time.Millisecond)
40
+ }
41
+}
42
+
43
func testRoutingDHT(t *testing.T, enablePubsub bool) {
44
t.Run(fmt.Sprintf("enablePubSub=%v", enablePubsub), func(t *testing.T) {
45
t.Parallel()
@@ -84,10 +114,8 @@ func testRoutingDHT(t *testing.T, enablePubsub bool) {
114
t.Run("ipfs routing findprovs", func(t *testing.T) {
115
t.Parallel()
116
hash := nodes[3].IPFSAddStr("some stuff")
87
- // Reprovide as initialProviderDelay still ongoing
88
- res := nodes[3].IPFS("routing", "reprovide")
89
- require.NoError(t, res.Err)
90
- res = nodes[4].IPFS("routing", "findprovs", hash)
117
+ waitUntilProvidesComplete(t, nodes[3])
118
+ res := nodes[4].IPFS("routing", "findprovs", hash)
119
assert.Equal(t, nodes[3].PeerID().String(), res.Stdout.Trimmed())
120
})
121
test/dependencies/go.mod
+1
-1
@@ -184,7 +184,7 @@ require (
184
github.com/libp2p/go-flow-metrics v0.3.0 // indirect
185
github.com/libp2p/go-libp2p v0.45.0 // indirect
186
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
187
- github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32 // indirect
187
+ github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb // indirect
188
github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect
189
github.com/libp2p/go-libp2p-record v0.3.1 // indirect
190
github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect
test/dependencies/go.sum
+2
-2
@@ -464,8 +464,8 @@ github.com/libp2p/go-libp2p v0.45.0 h1:Pdhr2HsFXaYjtfiNcBP4CcRUONvbMFdH3puM9vV4T
464
github.com/libp2p/go-libp2p v0.45.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc=
465
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
466
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
467
-github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32 h1:xZj18PsLD157snR/BFo547jwOkGDH7jZjMEkBDOoD4Q=
468
-github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251025120456-f33906fd2f32/go.mod h1:aHMTg23iseX9grGSfA5gFUzLrqzmYbA8PqgGPqM8VkI=
467
+github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb h1:jOWsCSRZKnRgocz4Ocu25Yigh5ZUkar2zWt/bzBh43Q=
468
+github.com/libp2p/go-libp2p-kad-dht v0.35.2-0.20251112013111-6d2d861e0abb/go.mod h1:WIysu8hNWQN8t73dKyTNqiZdcYKRrGFl4wjzX4Gz6pQ=
469
github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s=
470
github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4=
471
github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg=
test/sharness/t0042-add-skip.sh
+2
-2
@@ -93,8 +93,8 @@ EOF
93
test_cmp expected actual
94
'
95
96
- test_expect_failure "'ipfs add' with an unregistered hash and wrapped leaves fails without crashing" '
97
- ipfs add --hash poseidon-bls12_381-a2-fc1 --raw-leaves=false -r mountdir/planets
96
+ test_expect_success "'ipfs add' with an unregistered hash and wrapped leaves fails without crashing" '
97
+ test_expect_code 1 ipfs add --hash poseidon-bls12_381-a2-fc1 --raw-leaves=false -r mountdir/planets
98
'
99
100
}
test/sharness/t0119-prometheus-data/prometheus_metrics
+1
-2
@@ -250,6 +250,5 @@ process_resident_memory_bytes
250
process_start_time_seconds
251
process_virtual_memory_bytes
252
process_virtual_memory_max_bytes
253
-provider_reprovider_provide_count
254
-provider_reprovider_reprovide_count
253
+provider_provides_total
254
target_info