@@ -24,6 +24,7 @@ import (
2424 "github.com/libp2p/go-libp2p-kad-dht/fullrt"
2525 dht_pb "github.com/libp2p/go-libp2p-kad-dht/pb"
2626 dhtprovider "github.com/libp2p/go-libp2p-kad-dht/provider"
27+ "github.com/libp2p/go-libp2p-kad-dht/provider/buffered"
2728 ddhtprovider "github.com/libp2p/go-libp2p-kad-dht/provider/dual"
2829 "github.com/libp2p/go-libp2p-kad-dht/provider/keystore"
2930 routinghelpers "github.com/libp2p/go-libp2p-routing-helpers"
@@ -84,7 +85,7 @@ type DHTProvider interface {
8485 // The keys are not deleted from the keystore, so they will continue to be
8586 // reprovided as scheduled.
8687 Clear () int
87- // RefreshSchedule scans the KeyStore for any keys that are not currently
88+ // RefreshSchedule scans the Keystore for any keys that are not currently
8889 // scheduled for reproviding. If such keys are found, it schedules their
8990 // associated keyspace region to be reprovided.
9091 //
@@ -314,13 +315,40 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
314315 }
315316 sweepingReprovider := fx .Provide (func (in providerInput ) (DHTProvider , * keystore.ResettableKeystore , error ) {
316317 ds := in .Repo .Datastore ()
317- keyStore , err := keystore .NewResettableKeystore (ds ,
318+ ks , err := keystore .NewResettableKeystore (ds ,
318319 keystore .WithPrefixBits (16 ),
319320 keystore .WithDatastorePath ("/provider/keystore" ),
320- keystore .WithBatchSize (int (cfg .Provide .DHT .KeyStoreBatchSize .WithDefault (config .DefaultProvideDHTKeyStoreBatchSize ))),
321+ keystore .WithBatchSize (int (cfg .Provide .DHT .KeystoreBatchSize .WithDefault (config .DefaultProvideDHTKeystoreBatchSize ))),
321322 )
322323 if err != nil {
323- return & NoopProvider {}, nil , err
324+ return nil , nil , err
325+ }
326+ // Constants for buffered provider configuration
327+ // These values match the upstream defaults from go-libp2p-kad-dht and have been battle-tested
328+ const (
329+ // bufferedDsName is the datastore namespace used by the buffered provider.
330+ // The dsqueue persists operations here to handle large data additions without
331+ // being memory-bound, allowing operations on hardware with limited RAM and
332+ // enabling core operations to return instantly while processing happens async.
333+ bufferedDsName = "bprov"
334+
335+ // bufferedBatchSize controls how many operations are dequeued and processed
336+ // together from the datastore queue. The worker processes up to this many
337+ // operations at once, grouping them by type for efficiency.
338+ bufferedBatchSize = 1 << 10 // 1024 items
339+
340+ // bufferedIdleWriteTime is an implementation detail of go-dsqueue that controls
341+ // how long the datastore buffer waits for new multihashes to arrive before
342+ // flushing in-memory items to the datastore. This does NOT affect providing speed -
343+ // provides happen as fast as possible via a dedicated worker that continuously
344+ // processes the queue regardless of this timing.
345+ bufferedIdleWriteTime = time .Minute
346+ )
347+
348+ bufferedProviderOpts := []buffered.Option {
349+ buffered .WithBatchSize (bufferedBatchSize ),
350+ buffered .WithDsName (bufferedDsName ),
351+ buffered .WithIdleWriteTime (bufferedIdleWriteTime ),
324352 }
325353 var impl dhtImpl
326354 switch inDht := in .DHT .(type ) {
@@ -331,7 +359,7 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
331359 case * dual.DHT :
332360 if inDht != nil {
333361 prov , err := ddhtprovider .New (inDht ,
334- ddhtprovider .WithKeystore (keyStore ),
362+ ddhtprovider .WithKeystore (ks ),
335363
336364 ddhtprovider .WithReprovideInterval (reprovideInterval ),
337365 ddhtprovider .WithMaxReprovideDelay (time .Hour ),
@@ -346,8 +374,7 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
346374 if err != nil {
347375 return nil , nil , err
348376 }
349- _ = prov
350- return prov , keyStore , nil
377+ return buffered .New (prov , ds , bufferedProviderOpts ... ), ks , nil
351378 }
352379 case * fullrt.FullRT :
353380 if inDht != nil {
@@ -365,7 +392,7 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
365392 selfAddrsFunc = func () []ma.Multiaddr { return impl .Host ().Addrs () }
366393 }
367394 opts := []dhtprovider.Option {
368- dhtprovider .WithKeystore (keyStore ),
395+ dhtprovider .WithKeystore (ks ),
369396 dhtprovider .WithPeerID (impl .Host ().ID ()),
370397 dhtprovider .WithRouter (impl ),
371398 dhtprovider .WithMessageSender (impl .MessageSender ()),
@@ -387,16 +414,19 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
387414 }
388415
389416 prov , err := dhtprovider .New (opts ... )
390- return prov , keyStore , err
417+ if err != nil {
418+ return nil , nil , err
419+ }
420+ return buffered .New (prov , ds , bufferedProviderOpts ... ), ks , nil
391421 })
392422
393423 type keystoreInput struct {
394424 fx.In
395425 Provider DHTProvider
396- KeyStore * keystore.ResettableKeystore
426+ Keystore * keystore.ResettableKeystore
397427 KeyProvider provider.KeyChanFunc
398428 }
399- initKeyStore := fx .Invoke (func (lc fx.Lifecycle , in keystoreInput ) {
429+ initKeystore := fx .Invoke (func (lc fx.Lifecycle , in keystoreInput ) {
400430 // Skip keystore initialization for NoopProvider
401431 if _ , ok := in .Provider .(* NoopProvider ); ok {
402432 return
@@ -407,12 +437,12 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
407437 done = make (chan struct {})
408438 )
409439
410- syncKeyStore := func (ctx context.Context ) error {
440+ syncKeystore := func (ctx context.Context ) error {
411441 kcf , err := in .KeyProvider (ctx )
412442 if err != nil {
413443 return err
414444 }
415- if err := in .KeyStore .ResetCids (ctx , kcf ); err != nil {
445+ if err := in .Keystore .ResetCids (ctx , kcf ); err != nil {
416446 return err
417447 }
418448 if err := in .Provider .RefreshSchedule (); err != nil {
@@ -424,7 +454,7 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
424454 lc .Append (fx.Hook {
425455 OnStart : func (ctx context.Context ) error {
426456 // Set the KeyProvider as a garbage collection function for the
427- // keystore. Periodically purge the KeyStore from all its keys and
457+ // keystore. Periodically purge the Keystore from all its keys and
428458 // replace them with the keys that needs to be reprovided, coming from
429459 // the KeyChanFunc. So far, this is the less worse way to remove CIDs
430460 // that shouldn't be reprovided from the provider's state.
@@ -434,7 +464,7 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
434464 // which can take a while.
435465 strategy := cfg .Provide .Strategy .WithDefault (config .DefaultProvideStrategy )
436466 logger .Infow ("provider keystore sync started" , "strategy" , strategy )
437- if err := syncKeyStore (ctx ); err != nil {
467+ if err := syncKeystore (ctx ); err != nil {
438468 logger .Errorw ("provider keystore sync failed" , "err" , err , "strategy" , strategy )
439469 } else {
440470 logger .Infow ("provider keystore sync completed" , "strategy" , strategy )
@@ -454,7 +484,7 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
454484 case <- gcCtx .Done ():
455485 return
456486 case <- ticker .C :
457- if err := syncKeyStore (gcCtx ); err != nil {
487+ if err := syncKeystore (gcCtx ); err != nil {
458488 logger .Errorw ("provider keystore sync" , "err" , err )
459489 }
460490 }
@@ -471,18 +501,16 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option {
471501 case <- ctx .Done ():
472502 return ctx .Err ()
473503 }
474-
475504 // Keystore data isn't purged, on close, but it will be overwritten
476505 // when the node starts again.
477-
478- return in .KeyStore .Close ()
506+ return in .Keystore .Close ()
479507 },
480508 })
481509 })
482510
483511 return fx .Options (
484512 sweepingReprovider ,
485- initKeyStore ,
513+ initKeystore ,
486514 )
487515}
488516
0 commit comments