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  }