github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/netstore.go (about)

     1  // Copyright 2023 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package storer
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  
    11  	"github.com/ethersphere/bee/v2/pkg/pusher"
    12  	"github.com/ethersphere/bee/v2/pkg/pushsync"
    13  	"github.com/ethersphere/bee/v2/pkg/storage"
    14  	"github.com/ethersphere/bee/v2/pkg/swarm"
    15  	"github.com/ethersphere/bee/v2/pkg/topology"
    16  	"github.com/opentracing/opentracing-go/ext"
    17  	olog "github.com/opentracing/opentracing-go/log"
    18  	"golang.org/x/sync/errgroup"
    19  )
    20  
    21  // DirectUpload is the implementation of the NetStore.DirectUpload method.
    22  func (db *DB) DirectUpload() PutterSession {
    23  	// egCtx will allow early exit of Put operations if we have
    24  	// already encountered error.
    25  	eg, egCtx := errgroup.WithContext(context.Background())
    26  
    27  	return &putterSession{
    28  		Putter: putterWithMetrics{
    29  			storage.PutterFunc(func(ctx context.Context, ch swarm.Chunk) error {
    30  				db.directUploadLimiter <- struct{}{}
    31  				eg.Go(func() (err error) {
    32  					defer func() { <-db.directUploadLimiter }()
    33  
    34  					span, logger, ctx := db.tracer.FollowSpanFromContext(ctx, "put-direct-upload", db.logger)
    35  					defer func() {
    36  						if err != nil {
    37  							ext.LogError(span, err)
    38  						}
    39  						span.Finish()
    40  					}()
    41  
    42  					for {
    43  						op := &pusher.Op{Chunk: ch, Err: make(chan error, 1), Direct: true, Span: span}
    44  						select {
    45  						case <-ctx.Done():
    46  							return ctx.Err()
    47  						case <-egCtx.Done():
    48  							return egCtx.Err()
    49  						case <-db.quit:
    50  							return ErrDBQuit
    51  						case db.pusherFeed <- op:
    52  							select {
    53  							case <-ctx.Done():
    54  								return ctx.Err()
    55  							case <-egCtx.Done():
    56  								return egCtx.Err()
    57  							case <-db.quit:
    58  								return ErrDBQuit
    59  							case err := <-op.Err:
    60  								if errors.Is(err, pushsync.ErrShallowReceipt) {
    61  									logger.Debug("direct upload: shallow receipt received, retrying", "chunk", ch.Address())
    62  								} else if errors.Is(err, topology.ErrNotFound) {
    63  									logger.Debug("direct upload: no peers available, retrying", "chunk", ch.Address())
    64  								} else {
    65  									return err
    66  								}
    67  							}
    68  						}
    69  					}
    70  				})
    71  				return nil
    72  			}),
    73  			db.metrics,
    74  			"netstore",
    75  		},
    76  		done:    func(swarm.Address) error { return eg.Wait() },
    77  		cleanup: func() error { _ = eg.Wait(); return nil },
    78  	}
    79  }
    80  
    81  // Download is the implementation of the NetStore.Download method.
    82  func (db *DB) Download(cache bool) storage.Getter {
    83  	return getterWithMetrics{
    84  		storage.GetterFunc(func(ctx context.Context, address swarm.Address) (ch swarm.Chunk, err error) {
    85  
    86  			span, logger, ctx := db.tracer.StartSpanFromContext(ctx, "get-chunk", db.logger)
    87  			defer func() {
    88  				if err != nil {
    89  					ext.LogError(span, err)
    90  				} else {
    91  					span.LogFields(olog.Bool("success", true))
    92  				}
    93  				span.Finish()
    94  			}()
    95  
    96  			ch, err = db.Lookup().Get(ctx, address)
    97  			switch {
    98  			case err == nil:
    99  				span.LogFields(olog.String("step", "chunk found locally"))
   100  				return ch, nil
   101  			case errors.Is(err, storage.ErrNotFound):
   102  				span.LogFields(olog.String("step", "retrieve chunk from network"))
   103  				if db.retrieval != nil {
   104  					// if chunk is not found locally, retrieve it from the network
   105  					ch, err = db.retrieval.RetrieveChunk(ctx, address, swarm.ZeroAddress)
   106  					if err == nil && cache {
   107  						select {
   108  						case <-ctx.Done():
   109  						case <-db.quit:
   110  						case db.cacheLimiter.sem <- struct{}{}:
   111  							db.cacheLimiter.wg.Add(1)
   112  							go func() {
   113  								defer func() {
   114  									<-db.cacheLimiter.sem
   115  									db.cacheLimiter.wg.Done()
   116  								}()
   117  
   118  								err := db.Cache().Put(db.cacheLimiter.ctx, ch)
   119  								if err != nil {
   120  									logger.Debug("putting chunk to cache failed", "error", err, "chunk_address", ch.Address())
   121  								}
   122  							}()
   123  						}
   124  					}
   125  				}
   126  			}
   127  			if err != nil {
   128  				return nil, err
   129  			}
   130  			return ch, nil
   131  		}),
   132  		db.metrics,
   133  		"netstore",
   134  	}
   135  }
   136  
   137  // PusherFeed is the implementation of the NetStore.PusherFeed method.
   138  func (db *DB) PusherFeed() <-chan *pusher.Op {
   139  	return db.pusherFeed
   140  }