github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/uploadstore.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 "fmt" 11 "sort" 12 13 storage "github.com/ethersphere/bee/v2/pkg/storage" 14 "github.com/ethersphere/bee/v2/pkg/storer/internal" 15 pinstore "github.com/ethersphere/bee/v2/pkg/storer/internal/pinning" 16 "github.com/ethersphere/bee/v2/pkg/storer/internal/transaction" 17 "github.com/ethersphere/bee/v2/pkg/storer/internal/upload" 18 "github.com/ethersphere/bee/v2/pkg/swarm" 19 ) 20 21 const uploadsLock = "pin-upload-store" 22 23 // Report implements the storage.PushReporter by wrapping the internal reporter 24 // with a transaction. 25 func (db *DB) Report(ctx context.Context, chunk swarm.Chunk, state storage.ChunkState) error { 26 27 unlock := db.Lock(uploadsLock) 28 defer unlock() 29 30 err := db.storage.Run(ctx, func(s transaction.Store) error { 31 return upload.Report(ctx, s, chunk, state) 32 }) 33 if err != nil { 34 return fmt.Errorf("reporter.Report: %w", err) 35 } 36 37 return nil 38 } 39 40 // Upload is the implementation of UploadStore.Upload method. 41 func (db *DB) Upload(ctx context.Context, pin bool, tagID uint64) (PutterSession, error) { 42 if tagID == 0 { 43 return nil, fmt.Errorf("storer: tagID required") 44 } 45 46 var ( 47 uploadPutter internal.PutterCloserWithReference 48 pinningPutter internal.PutterCloserWithReference 49 err error 50 ) 51 52 err = db.storage.Run(ctx, func(s transaction.Store) error { 53 uploadPutter, err = upload.NewPutter(s.IndexStore(), tagID) 54 if err != nil { 55 return fmt.Errorf("upload.NewPutter: %w", err) 56 } 57 58 if pin { 59 pinningPutter, err = pinstore.NewCollection(s.IndexStore()) 60 if err != nil { 61 return fmt.Errorf("pinstore.NewCollection: %w", err) 62 } 63 } 64 return nil 65 }) 66 67 if err != nil { 68 return nil, err 69 } 70 71 return &putterSession{ 72 Putter: putterWithMetrics{ 73 storage.PutterFunc(func(ctx context.Context, chunk swarm.Chunk) error { 74 unlock := db.Lock(uploadsLock) 75 defer unlock() 76 return errors.Join( 77 db.storage.Run(ctx, func(s transaction.Store) error { 78 return uploadPutter.Put(ctx, s, chunk) 79 }), 80 func() error { 81 if pinningPutter != nil { 82 return db.storage.Run(ctx, func(s transaction.Store) error { 83 return pinningPutter.Put(ctx, s, chunk) 84 }) 85 } 86 return nil 87 }(), 88 ) 89 }), 90 db.metrics, 91 "uploadstore", 92 }, 93 done: func(address swarm.Address) error { 94 defer db.events.Trigger(subscribePushEventKey) 95 unlock := db.Lock(uploadsLock) 96 defer unlock() 97 98 return errors.Join( 99 db.storage.Run(ctx, func(s transaction.Store) error { 100 return uploadPutter.Close(s.IndexStore(), address) 101 }), 102 func() error { 103 if pinningPutter != nil { 104 return db.storage.Run(ctx, func(s transaction.Store) error { 105 pinErr := pinningPutter.Close(s.IndexStore(), address) 106 if errors.Is(pinErr, pinstore.ErrDuplicatePinCollection) { 107 pinErr = pinningPutter.Cleanup(db.storage) 108 } 109 return pinErr 110 }) 111 } 112 return nil 113 }(), 114 ) 115 }, 116 cleanup: func() error { 117 defer db.events.Trigger(subscribePushEventKey) 118 unlock := db.Lock(uploadsLock) 119 defer unlock() 120 return errors.Join( 121 uploadPutter.Cleanup(db.storage), 122 func() error { 123 if pinningPutter != nil { 124 return pinningPutter.Cleanup(db.storage) 125 } 126 return nil 127 }(), 128 ) 129 }, 130 }, nil 131 } 132 133 // NewSession is the implementation of UploadStore.NewSession method. 134 func (db *DB) NewSession() (SessionInfo, error) { 135 unlock := db.Lock(lockKeyNewSession) 136 defer unlock() 137 138 trx, done := db.storage.NewTransaction(context.Background()) 139 defer done() 140 141 info, err := upload.NextTag(trx.IndexStore()) 142 if err != nil { 143 return SessionInfo{}, err 144 } 145 return info, trx.Commit() 146 } 147 148 // Session is the implementation of the UploadStore.Session method. 149 func (db *DB) Session(tagID uint64) (SessionInfo, error) { 150 return upload.TagInfo(db.storage.IndexStore(), tagID) 151 } 152 153 // DeleteSession is the implementation of the UploadStore.DeleteSession method. 154 func (db *DB) DeleteSession(tagID uint64) error { 155 return db.storage.Run(context.Background(), func(s transaction.Store) error { 156 return upload.DeleteTag(s.IndexStore(), tagID) 157 }) 158 } 159 160 // ListSessions is the implementation of the UploadStore.ListSessions method. 161 func (db *DB) ListSessions(offset, limit int) ([]SessionInfo, error) { 162 const maxPageSize = 1000 163 164 limit = min(limit, maxPageSize) 165 166 tags, err := upload.ListAllTags(db.storage.IndexStore()) 167 if err != nil { 168 return nil, err 169 } 170 171 sort.Slice(tags, func(i, j int) bool { 172 return tags[i].TagID < tags[j].TagID 173 }) 174 175 return tags[min(offset, len(tags)):min(offset+limit, len(tags))], nil 176 }