Skip to content

Conversation

@guillaumemichel
Copy link
Collaborator

@guillaumemichel guillaumemichel commented Jul 9, 2025

Supersedes #1082

Reprovide Sweep

Problem

Reproviding many keys to the DHT one by one is inefficient, because it requires a GetClosestPeers (or GCP) request for every key.

Current state

Currently, reprovides are managed in boxo/provider. Every ReprovideInterval (22h in Amino DHT), all keys matching the reprovide strategy are reprovided at once. The process is slightly different depending on whether the accelerated DHT client is enabled.

Default DHT client

All the keys are reprovided sequentially, using the go-libp2p-kad-dht Provide() method. This operation consists in finding the k closest peers to the given key, and then request them all to store the associated provider record.

The process is expensive because it requires a GCP for each key (opening approx. 20-30 connections). Timeouts due to unreachable peers make this process very long, resulting in a mean of ~10s in provide time (source: probelab.io 2025-06-13).

dht-publish-performance-overall

With 10 seconds per provide, a node using this process could reprovide less than 8'000 keys over the reprovide interval of 22h (using a single thread).

Accelerated DHT client (fullrt)

The accelerated DHT client periodically (every 1h) crawls the DHT swarm to cache the addresses of all discovered peers. It allows it to skip the GCP during the provide request, since it already knows the k closest peers and the associated multiaddrs.

Hence, the accelerated DHT client is able to provide much more keys during the reprovide interval compared with the default DHT client. However, crawling the DHT swarm is an expensive operation (networking, memory), and since all the keys are reprovided at once, the node will experience a bust period until all keys are reprovided.

Ideally, nodes wouldn't have to crawl the swarm to reprovide content, and the reprovide operation could be smoothed over time to avoid a bust during which the libp2p node is incapable of performing other actions.

Pooling Reprovides

If there are more keys to be reprovided than the number of nodes in the DHT swarm divided by the replication factor (k), then it means that there are at least two keys that will be provided to the exact same set of peers. This means that the number of GCP is less than the number of keys to reprovide.

For the Amino DHT, containing ~10k DHT servers and having a replication factor of 20, pooling reprovides becomes efficient starting from 500 keys.

Reprovide Sweep

The current process of reproviding all keys at once is bad because it creates a bust. In order to smooth the reprovide process, we can sweep the keyspace from left to right, in order to cover all peers over time. This consists of exploring keyspace regions, corresponding to a set of peers that are close to each other in the Kademlia XOR distance metric.

⚠️ The Kademlia keyspace in NOT linear

A keyspace region is explored using a few (typically 2-4 GCP) to discover all the peers it contains. A keyspace region can be identified by a Kademlia identifier prefix, the kademlia identifiers of all peers within this region start with the region's prefix.

Once a region is fully explored, all the keys matching the keyspace region's prefix can be allocated to this set of peers. No additional GCP is needed.

Pull Requests

Done

Ready for review (PRs must be merged in this order)

Ready for review (nice-to-have)

Issues

Optional improvements

  • provider: stats #1144
  • ipfs provide status <cid>
  • Resume operations on restart
  • Limit ProviderQueue and ReproviderQueue size
  • Timeout management
  • provider package readme

Admin

Depends on:

Need new release of:

Closes #824

Part of ipshipyard/roadmaps#6, ipshipyard/roadmaps#7, ipshipyard/roadmaps#8

@guillaumemichel guillaumemichel requested a review from gammazero July 9, 2025 14:07
@lidel lidel mentioned this pull request Jul 14, 2025
17 tasks
guillaumemichel and others added 24 commits September 17, 2025 10:51
* SweepingProvider interface

* updated interface to address reviews

* LastProvideAt

* ProvideStatus

* Update provider/provider.go

Co-authored-by: Marcin Rataj <[email protected]>

* renamed `ts` to `lastProvide`

* add TODO for missing implementation

---------

Co-authored-by: Marcin Rataj <[email protected]>
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* close avgPrefixLenReady to signal initialPrefixLen measurement is done
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* simplify unscheduleSubsumedPrefixesNoClock

* moved maxPrefixSize const to top
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* refactor and test groupAndScheduleKeysByPrefix

* moved maxPrefixSize const to top
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* provider: explore swarm

* address review

* decrease minimal region size from replicationFactor+1 to replicationFactor

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* refactor and test groupAndScheduleKeysByPrefix

* moved maxPrefixSize const to top
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* provider: explore swarm

* provider: batch provide

* address review

* decrease minimal region size from replicationFactor+1 to replicationFactor

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* refactor and test groupAndScheduleKeysByPrefix

* moved maxPrefixSize const to top

* address review
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* provider: explore swarm

* provider: batch provide

* provider: batch reprovide

* fix panic when adding key to trie if superstring already exists

* address review

* decrease minimal region size from replicationFactor+1 to replicationFactor

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* fix test to match region size (now: replicationFactor, before: replicationFactor+1)

* refactor and test groupAndScheduleKeysByPrefix

* moved maxPrefixSize const to top

* address review

* address review
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* provider: explore swarm

* provider: batch provide

* provider: batch reprovide

* provider: catchup pending work

* fix panic when adding key to trie if superstring already exists

* address review

* decrease minimal region size from replicationFactor+1 to replicationFactor

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* fix test to match region size (now: replicationFactor, before: replicationFactor+1)

* dequeue outside of go routine

* refactor and test groupAndScheduleKeysByPrefix

* moved maxPrefixSize const to top

* address review

* address review

* address review
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* provider: explore swarm

* provider: batch provide

* provider: batch reprovide

* provider: catchup pending work

* provider: options

* fix panic when adding key to trie if superstring already exists

* address review

* decrease minimal region size from replicationFactor+1 to replicationFactor

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* fix test to match region size (now: replicationFactor, before: replicationFactor+1)

* dequeue outside of go routine

* refactor and test groupAndScheduleKeysByPrefix

* moved maxPrefixSize const to top

* address review

* address review

* address review

* don't allocate capacity to avgPrefixLenReady
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* provider: explore swarm

* provider: batch provide

* provider: batch reprovide

* provider: catchup pending work

* provider: options

* provide: handle reprovide

* fix panic when adding key to trie if superstring already exists

* address review

* decrease minimal region size from replicationFactor+1 to replicationFactor

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* fix test to match region size (now: replicationFactor, before: replicationFactor+1)

* dequeue outside of go routine

* refactor and test groupAndScheduleKeysByPrefix

* moved maxPrefixSize const to top

* address review

* address review

* address review

* don't allocate capacity to avgPrefixLenReady

* address review
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* provider: explore swarm

* provider: batch provide

* provider: batch reprovide

* provider: catchup pending work

* provider: options

* provide: handle reprovide

* provider: daemon

* cancel context of external functions + tests

* fix panic when adding key to trie if superstring already exists

* address review

* decrease minimal region size from replicationFactor+1 to replicationFactor

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* fix test to match region size (now: replicationFactor, before: replicationFactor+1)

* dequeue outside of go routine

* close connectivity

* address review

* optimise expensive calls to trie.Size()
* provider: adding provide and reprovide queue

* provider: network operations

* add some tests

* schedule prefix len computations

* provider schedule

* provider: handleProvide

* addressed review

* use go-test/random

* satisfy linter

* log errors during initial prefix len measurement

* address review

* satisfy linter

* address review

* provider: explore swarm

* provider: batch provide

* provider: batch reprovide

* provider: catchup pending work

* provider: options

* provide: handle reprovide

* provider: daemon

* provider: integration tests

* cancel context of external functions + tests

* fix panic when adding key to trie if superstring already exists

* address review

* decrease minimal region size from replicationFactor+1 to replicationFactor

* simplify unscheduleSubsumedPrefixesNoClock

* address review

* fix test to match region size (now: replicationFactor, before: replicationFactor+1)

* dequeue outside of go routine

* fix tests

* close connectivity

* fix waitgroup
* provider: refresh schedule

* address review
* provider: refresh schedule

* dual: provider

* fix: flaky TestStartProvidingUnstableNetwork

* addressing review
* provider: minor fixes

* adjusting for kubo PR

* address review
* update ConnectivityChecker

* ai tests

* update connectivity state machine

* remove useless connectivity funcs

* connectivity: get rid of internal state

* docs and tests

* fix(dual/provider): don't prevent providing if a DHT returns an error

* address review
* keystore: revamp

* fix: KeyStore.Close dependencies

* keystore: implement KeyStore4
* provider: ResettableKeyStore

* keystore: remove mutex

* use datastore namespace

* don't sync to write to altDs

* simplify put

* deduplicate operation execution code
* provider: ResettableKeyStore

* keystore: remove mutex

* use datastore namespace

* don't sync to write to altDs

* simplify put

* deduplicate operation execution code

* buffered provider

* tests

* removing redundant code

* docs

* wait on empty queue

* fix flaky test
* provider: ResettableKeyStore

* keystore: remove mutex

* use datastore namespace

* don't sync to write to altDs

* simplify put

* deduplicate operation execution code

* addressing gammazero review
@guillaumemichel guillaumemichel merged commit 2e3cea1 into master Sep 18, 2025
9 checks passed
@guillaumemichel guillaumemichel deleted the provider branch September 18, 2025 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reprovide Sweep

4 participants