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 }