gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/tusuploadstorememory.go (about) 1 package renter 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/tus/tusd/pkg/handler" 10 "github.com/tus/tusd/pkg/memorylocker" 11 "gitlab.com/SkynetLabs/skyd/build" 12 "gitlab.com/SkynetLabs/skyd/skymodules" 13 "go.sia.tech/siad/crypto" 14 ) 15 16 type ( 17 // skynetTUSInMemoryUploadStore is an in-memory skynetTUSUploadStore 18 // implementation. 19 skynetTUSInMemoryUploadStore struct { 20 uploads map[string]*skynetInMemoryUpload 21 mu sync.Mutex 22 staticLocker *memorylocker.MemoryLocker 23 } 24 25 // skynetInMemoryUpload represents an upload within the 26 // skynetTUSInMemoryUploadStore. 27 skynetInMemoryUpload struct { 28 complete bool 29 fanout []byte 30 fi handler.FileInfo 31 lastWrite time.Time 32 staticFilename string 33 staticSP skymodules.SiaPath 34 35 // Base chunk related fields. 36 staticBaseChunkRedundancy uint8 37 staticMetadata []byte 38 39 // Fanout related fields. 40 staticFanoutDataPieces int 41 staticFanoutParityPieces int 42 staticCipherType crypto.CipherType 43 44 // utilities 45 mu sync.Mutex 46 } 47 ) 48 49 // NewSkynetTUSInMemoryUploadStore creates a new skynetTUSInMemoryUploadStore. 50 func NewSkynetTUSInMemoryUploadStore() skymodules.SkynetTUSUploadStore { 51 return &skynetTUSInMemoryUploadStore{ 52 uploads: make(map[string]*skynetInMemoryUpload), 53 staticLocker: memorylocker.New(), 54 } 55 } 56 57 // Close implements the io.Closer and is a no-op for the in-memory store. 58 func (us *skynetTUSInMemoryUploadStore) Close() error { return nil } 59 60 // CreateUpload creates a new upload and adds it to the store. 61 func (us *skynetTUSInMemoryUploadStore) CreateUpload(_ context.Context, fi handler.FileInfo, sp skymodules.SiaPath, fileName string, baseChunkRedundancy uint8, fanoutDataPieces, fanoutParityPieces int, sm []byte, ct crypto.CipherType) (skymodules.SkynetTUSUpload, error) { 62 us.mu.Lock() 63 defer us.mu.Unlock() 64 upload := &skynetInMemoryUpload{ 65 complete: false, 66 fanout: nil, 67 fi: fi, 68 lastWrite: time.Now(), 69 staticFilename: fileName, 70 staticSP: sp, 71 72 staticBaseChunkRedundancy: baseChunkRedundancy, 73 staticMetadata: sm, 74 75 staticFanoutDataPieces: fanoutDataPieces, 76 staticFanoutParityPieces: fanoutParityPieces, 77 staticCipherType: ct, 78 } 79 us.uploads[fi.ID] = upload 80 return upload, nil 81 } 82 83 // GetUpload returns an upload from the upload store. 84 func (us *skynetTUSInMemoryUploadStore) GetUpload(_ context.Context, id string) (skymodules.SkynetTUSUpload, error) { 85 us.mu.Lock() 86 defer us.mu.Unlock() 87 upload, exists := us.uploads[id] 88 if !exists { 89 return nil, handler.ErrNotFound 90 } 91 return upload, nil 92 } 93 94 // NewLock implements handler.Locker by forwarding the call to an in-memory 95 // locker. 96 func (us *skynetTUSInMemoryUploadStore) NewLock(id string) (handler.Lock, error) { 97 return us.staticLocker.NewLock(id) 98 } 99 100 // CommitFinishUpload commits a finished upload to the upload store. This means 101 // setting the skylink of the finished upload. 102 func (u *skynetInMemoryUpload) CommitFinishUpload(_ context.Context, skylink skymodules.Skylink) error { 103 u.mu.Lock() 104 defer u.mu.Unlock() 105 if u.complete { 106 return ErrUploadFinished 107 } 108 u.fi.Offset = u.fi.Size 109 u.complete = true 110 u.lastWrite = time.Now() 111 u.fi.MetaData["Skylink"] = skylink.String() 112 return nil 113 } 114 115 // CommitWriteChunk commits the changes to the upload after successfully writing 116 // a chunk to the store. 117 func (u *skynetInMemoryUpload) CommitWriteChunk(_ context.Context, newOffset int64, newLastWrite time.Time, isSmall bool, fanout []byte) error { 118 u.mu.Lock() 119 defer u.mu.Unlock() 120 u.fanout = append(u.fanout, fanout...) 121 return u.commitWriteChunk(newOffset, newLastWrite, isSmall) 122 } 123 124 // GetInfo returns the upload's underlying handler.FileInfo. 125 func (u *skynetInMemoryUpload) GetInfo(ctx context.Context) (handler.FileInfo, error) { 126 u.mu.Lock() 127 defer u.mu.Unlock() 128 return u.fi, nil 129 } 130 131 // PruneInfo returns the relevant info for pruning an upload. This includes the 132 // upload id and the siapath. 133 func (u *skynetInMemoryUpload) PruneInfo(ctx context.Context) (id string, sp skymodules.SiaPath, err error) { 134 u.mu.Lock() 135 defer u.mu.Unlock() 136 id = u.fi.ID 137 sp = u.staticSP 138 return 139 } 140 141 // UploadParams return skymodules.SkyfileUploadParameters and 142 // skymodules.FileUploadParams for the upload. 143 func (u *skynetInMemoryUpload) UploadParams(ctx context.Context) (skymodules.SkyfileUploadParameters, skymodules.FileUploadParams, error) { 144 sup := skymodules.SkyfileUploadParameters{ 145 BaseChunkRedundancy: u.staticBaseChunkRedundancy, 146 Filename: u.staticFilename, 147 SiaPath: u.staticSP, 148 } 149 fanoutSiaPath, err := u.staticSP.AddSuffixStr(skymodules.ExtendedSuffix) 150 if err != nil { 151 return skymodules.SkyfileUploadParameters{}, skymodules.FileUploadParams{}, err 152 } 153 up, err := fileUploadParams(fanoutSiaPath, u.staticFanoutDataPieces, u.staticFanoutParityPieces, true, u.staticCipherType) 154 if err != nil { 155 return skymodules.SkyfileUploadParameters{}, skymodules.FileUploadParams{}, err 156 } 157 if up.CipherType != crypto.TypePlain { 158 return skymodules.SkyfileUploadParameters{}, skymodules.FileUploadParams{}, fmt.Errorf("ciphertype should be TypePlain") 159 } 160 up.CipherKey = crypto.GenerateSiaKey(crypto.TypePlain) 161 return sup, up, nil 162 } 163 164 // Fanout returns the fanout of the upload. 165 func (u *skynetInMemoryUpload) Fanout(ctx context.Context) ([]byte, error) { 166 u.mu.Lock() 167 defer u.mu.Unlock() 168 return u.fanout, nil 169 } 170 171 // SkyfileMetadata returns the raw SkyfileMetadata of the upload. 172 func (u *skynetInMemoryUpload) SkyfileMetadata(ctx context.Context) ([]byte, error) { 173 u.mu.Lock() 174 defer u.mu.Unlock() 175 return u.staticMetadata, nil 176 } 177 178 // Skylink returns the skylink of the upload if it was set already. 179 func (u *skynetInMemoryUpload) GetSkylink() (skymodules.Skylink, bool) { 180 u.mu.Lock() 181 defer u.mu.Unlock() 182 sl, exists := u.fi.MetaData["Skylink"] 183 if !exists { 184 return skymodules.Skylink{}, false 185 } 186 var skylink skymodules.Skylink 187 if err := skylink.LoadString(sl); err != nil { 188 build.Critical("upload contains invalid skylink") 189 return skymodules.Skylink{}, false 190 } 191 return skylink, true 192 } 193 194 // Prune removes uploads that have been idle for too long. 195 func (us *skynetTUSInMemoryUploadStore) ToPrune(_ context.Context) ([]skymodules.SkynetTUSUpload, error) { 196 us.mu.Lock() 197 defer us.mu.Unlock() 198 var toDelete []skymodules.SkynetTUSUpload 199 for _, u := range us.uploads { 200 u.mu.Lock() 201 lastWrite := u.lastWrite 202 complete := u.complete 203 u.mu.Unlock() 204 if time.Since(lastWrite) < PruneTUSUploadTimeout { 205 continue // nothing to do 206 } 207 // If the upload wasn't completed, delete the files on disk. 208 if !complete { 209 toDelete = append(toDelete, u) 210 } 211 } 212 return toDelete, nil 213 } 214 215 // Prune removes uploads that have been idle for too long. 216 func (us *skynetTUSInMemoryUploadStore) Prune(_ context.Context, uploadIDs []string) error { 217 us.mu.Lock() 218 defer us.mu.Unlock() 219 for _, uploadID := range uploadIDs { 220 delete(us.uploads, uploadID) 221 } 222 return nil 223 } 224 225 // WithTransaction allows for grouping multiple database operations into a 226 // single atomic transaction. 227 func (us *skynetTUSInMemoryUploadStore) WithTransaction(ctx context.Context, handler func(context.Context) error) error { 228 return handler(ctx) 229 } 230 231 // commitWriteChunk commits the changes to the upload after successfully 232 // writing a chunk to the store. 233 func (u *skynetInMemoryUpload) commitWriteChunk(newOffset int64, newLastWrite time.Time, smallFile bool) error { 234 u.fi.Offset = newOffset 235 u.lastWrite = newLastWrite 236 return nil 237 }